package com.chuanxia.mcp.sched;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.chuanxia.mcp.bean.model.VideoM3u8;
import com.chuanxia.mcp.bean.model.VideoTs;
import com.chuanxia.mcp.bean.vo.TsArrIsOrderVo;
import com.chuanxia.mcp.exception.ResultException;
import com.chuanxia.mcp.service.VideoM3u8Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
@RequiredArgsConstructor
public class M3U8Utils {

    private final RestTemplate restTemplate;

    @Value("${m3u8.downLoadPath}")
    private String downLoadPath;
    @Value("${m3u8.tsDownLoadPath}")
    private String tsDownLoadPath;
    private Pattern pattern = Pattern.compile("\\d+");

    /**
     * 下载m3u8文件,解析ts
     *
     * @param videoId 视频id
     * @return video对象
     */
    public List<VideoM3u8> downloadM3U8(String baseUrl, String videoId) {
        ArrayList<VideoM3u8> result = new ArrayList<>();
        String fileName = "";
        String uri = "";
        String url = "";
        try {
            URI baseUri = new URI(baseUrl);
            String scheme = baseUri.getScheme();
            url = scheme + "://" + baseUri.getHost();
            String path = baseUri.getPath();
            uri = path.substring(0, path.lastIndexOf("/") + 1);
            fileName = path.substring(path.lastIndexOf("/") + 1);
        } catch (URISyntaxException e) {
            log.info("解析客户服务器地址失败,原因:{}", e.getMessage());
        }
        String m3u8Str = m3u8ToString(fileName, url + uri);
        String resolutionRatio = "1280x720";
        String bandwidth = "2400000";
        String[] m3u8Arr = m3u8Str.split("\n");
        if (m3u8Arr.length > 0) {
            //判断当前m3U8是单码文件还是多码文件
            String endData = m3u8Arr[m3u8Arr.length - 1];
            if (endData.endsWith("#EXT-X-ENDLIST")) {
                //当前m3u8文件是单码文件,直接解析,入库
                VideoM3u8 videoM3u8 = analysisM3u8(url + uri + fileName, m3u8Str, resolutionRatio, bandwidth, videoId, url, uri);
                result.add(videoM3u8);
            } else {
                for (int i = 0; i < m3u8Arr.length; i++) {
                    String m3u8StreamStr = m3u8Arr[i];
                    //判断当前行是否是分辨率
                    if (m3u8StreamStr.contains("#EXT-X-STREAM-INF")) {
                        String[] split = m3u8StreamStr.split(",");
                        if (split.length >= 2) {
                            String resolutionRatioStr = split[1];
                            //bandwidth
                            if (resolutionRatioStr.contains("RESOLUTION=")) {
                                String[] resolutionArr = resolutionRatioStr.split("RESOLUTION=");
                                resolutionRatio = resolutionArr[1];
                            }
                        }
                        if (split[0].contains("BANDWIDTH")) {
                            bandwidth = split[0].split("BANDWIDTH=")[1];
                        }
                        //是分辨率头文件,获取下一行.m3u8地址,可能是http完整地址,可能是相对路径
                        String m3u8RadioUrl = m3u8Arr[i + 1];
                        String nextM3u8Url;
                        String nextM3u8FileName;
                        if (m3u8RadioUrl.startsWith("http")) {
                            //完整链接,直接去获取m3u8
                            nextM3u8Url = m3u8RadioUrl;
                            nextM3u8FileName = nextM3u8Url.substring(nextM3u8Url.lastIndexOf("/") + 1);
                        } else {
                            nextM3u8Url = url + uri;
                            nextM3u8FileName = m3u8RadioUrl;
                        }
                        String nextM3U8FileStr = m3u8ToString(nextM3u8FileName, nextM3u8Url);
                        VideoM3u8 videoM3u8 = analysisM3u8(nextM3u8Url + nextM3u8FileName, nextM3U8FileStr, resolutionRatio, bandwidth, videoId, url, uri);
                        result.add(videoM3u8);
                    }
                }
            }

        }
        return result;
    }

    /**
     * 获取m3u8文件内容
     *
     * @param fileName 文件名
     * @param url      地址
     * @return 文件内容
     */
    private String m3u8ToString(String fileName, String url) {
        StringBuilder fileUrl = new StringBuilder();
        fileUrl.append(url);
        fileUrl.append(fileName);
        log.info("开始去下载m3u8文件,当前url:{},文件名:{}", fileUrl.toString(), fileName);
        ResponseEntity<byte[]> forEntity = restTemplate.getForEntity(fileUrl.toString(), byte[].class);
        if (forEntity.getBody() == null) {
            throw new ResultException(500, "没有获取到对应m3u8文件");
        }
        return new String(forEntity.getBody());
    }

    /**
     * 解析当前m3u8文件
     *
     * @param m3u8Url         m3u8文件下载地址
     * @param m3u8Content     文件内容
     * @param resolutionRatio 分辨率
     * @param bandwidth       码率
     * @param videoId         videoId
     * @param url             请求域名
     * @param uri             相对路径
     * @return m3u8对象
     */
    private VideoM3u8 analysisM3u8(String m3u8Url, String m3u8Content, String resolutionRatio, String bandwidth, String videoId, String url, String uri) {
        //开始解析m3u8文件
        String[] split = m3u8Content.split("\n");
        VideoM3u8 m3u8 = new VideoM3u8();
        m3u8.setVideoId(videoId);
        m3u8.setBandwidth(bandwidth);
        m3u8.setResolution(resolutionRatio);
        m3u8.setSEQUENCE("0");

        //开始封装组合数据入库
        int i = 0;
        List<VideoTs> tsList = new ArrayList<>(split.length);
        for (String data : split) {
            if (data.contains("#EXT-X-VERSION")) {
                //版本
                m3u8.setVERSION(data.split(":")[1]);
            } else if (data.contains("#EXT-X-TARGETDURATION")) {
                //分片最大时长
                m3u8.setTARGETDURATION(data.split(":")[1]);
            } else if (data.contains("#EXT-X-MEDIA-SEQUENCE")) {
                //播放最开始下标
                m3u8.setSEQUENCE(data.split(":")[1]);
            } else if (data.contains("#EXT-X-PLAYLIST-TYPE:")) {
                //播放类型
                m3u8.setTYPE(data.split(":")[1]);
            } else if (data.contains("#EXT-X-KEY:METHOD")) {
                if (data.contains("URI")) {
                    for (String s : data.split(",")) {
                        if (s.contains("URI=")) {
                            String keyUrl = s.split("URI=")[1].replaceAll("\"", "");
                            if (keyUrl.contains("http")) {
                                ResponseEntity<byte[]> forEntity = restTemplate.getForEntity(keyUrl, byte[].class);
                                String key = Base64.getEncoder().encodeToString(forEntity.getBody());
                                m3u8.setKEY(key);
                            } else {
                                //如果key是相对路径,就拼接地址,获取key地址
                                ResponseEntity<byte[]> forEntity = restTemplate.getForEntity(url + uri + keyUrl, byte[].class);
                                String key = Base64.getEncoder().encodeToString(forEntity.getBody());
                                m3u8.setKEY(key);
                            }
                        }
                    }
                }
                //IV,key
                m3u8.setIV(data.split("IV=")[1]);
            } else if (data.contains("#EXTINF")) {
                //分片时间
                String extinf = data.split(":")[1].replaceAll(",", "");
                //ts
                String tsName = split[i + 1];
                VideoTs videoTs = new VideoTs();
                videoTs.setTime(extinf);
                videoTs.setName(tsName);
                tsList.add(videoTs);
            }
            i++;
        }
        if (CollectionUtil.isNotEmpty(tsList)) {
            int listSize = 2;
            TsArrIsOrderVo orderVo = checkIsOrder(tsList);
            //判断当前ts是绝对路径还是相对路径,如果是绝对路径,则不再设置path
            VideoTs videoTs = tsList.get(0);
            String name = videoTs.getName();
            if (!name.contains("http")) {
                try {
                    URI m3u8Uri = new URI(m3u8Url);
                    String path = m3u8Uri.getPath();
                    path = path.substring(0, path.lastIndexOf("/") + 1);
                    m3u8.setPREFIX_PATH(path);
                } catch (URISyntaxException e) {
                    log.error("获取当前m3u8所在相对路径失败,原因:{}", e.getMessage());
                }
            }
            if (tsList.size() >= listSize) {
                if (orderVo.isOrder()) {
                    //是自然排序,设置值
                    m3u8.setTS_START(orderVo.getTsStart());
                    m3u8.setTS_END(orderVo.getTsEnd());
                    m3u8.setEXTINF(orderVo.getExtinf());
                    m3u8.setTS_LAST_TIME(orderVo.getTsLastTime());
                    m3u8.setTS_NAME(orderVo.getTsName());
                    m3u8.setTsListStr(JSON.toJSONString(orderVo.getVideoTs()));
                } else {
                    //不是自然排序,设置arr
                    m3u8.setTsListStr(JSON.toJSONString(orderVo.getVideoTs()));
                }
            } else {
                //ts数组长度小于2,直接设置入库
                m3u8.setTsListStr(JSON.toJSONString(tsList));
            }

        }
        return m3u8;
    }


    /**
     * 比较两个ts文件是否按照自然排序规则
     *
     * @param tsList ts数组
     * @return true表示按规则排序, false表示无规则
     */
    private TsArrIsOrderVo checkIsOrder(List<VideoTs> tsList) {

        VideoTs first = tsList.get(0);
        VideoTs last = tsList.get(tsList.size() - 1);
        String firstName = first.getName();
        firstName = firstName.split("\\.")[0];
        String lastName = last.getName();
        lastName = lastName.split("\\.")[0];

        TsArrIsOrderVo orderVo = new TsArrIsOrderVo();
        //判断里面每一个ts时间是否固定(去除最后一条)

        //每循环一次,用第一次的时间跟下一次时间做比较
        String lastExtinfTime = "";
        for (int i = 0; i < tsList.size() - 1; i++) {
            VideoTs videoTs = tsList.get(i);
            if (i == 0) {
                lastExtinfTime = videoTs.getTime();
            } else {
                if (!lastExtinfTime.equals(videoTs.getTime())) {
                    orderVo.setOrder(false);
                    orderVo.setVideoTs(tsList);
                    return orderVo;
                }
            }
        }
        int size = tsList.size();
        String numberMatcher = "[0-9]+";
        //开头数字,结尾字符串,直接false
        if (firstName.matches(numberMatcher) && !lastName.matches(numberMatcher)) {
            orderVo.setOrder(false);
            orderVo.setVideoTs(tsList);
            return orderVo;
        }
        //开头字符串,结尾数字,直接false
        if (!firstName.matches(numberMatcher) && lastName.matches(numberMatcher)) {
            orderVo.setOrder(false);
            orderVo.setVideoTs(tsList);
            return orderVo;
        }
        //两边都是数字,看看是不是自然排序
        if (firstName.matches(numberMatcher) && lastName.matches(numberMatcher)) {
            //长度+首数-1=尾数

            if (Long.parseLong(firstName) + size - 1 != Long.parseLong(lastName)) {
                orderVo.setOrder(false);
                orderVo.setVideoTs(tsList);
                return orderVo;
            }
        }
        // 匹配一个或多个数字
        Matcher firstMatcher = pattern.matcher(firstName);
        Matcher lastMatcher = pattern.matcher(lastName);
        ArrayList<String> firstArr = new ArrayList<>();
        ArrayList<String> lastArr = new ArrayList<>();
        while (firstMatcher.find()) {
            String number = firstMatcher.group();
            firstArr.add(number);
        }
        while (lastMatcher.find()) {
            String number = lastMatcher.group();
            lastArr.add(number);
        }
        if (firstArr.size() != lastArr.size()) {
            //数字切割分组长度不一致,直接false
            orderVo.setOrder(false);
            orderVo.setVideoTs(tsList);
            return orderVo;
        } else {
            //长度一致,判断数字是不是自然排序
            String firstNum = "";
            String lastNum = "";
            int count = 0;
            //记录一下不一致的下标
            int inconsistentIndex = 0;
            for (int i = 0; i < firstArr.size(); i++) {
                if (!firstArr.get(i).equals(lastArr.get(i))) {
                    count++;
                    firstNum = firstArr.get(i);
                    lastNum = lastArr.get(i);
                    inconsistentIndex = i;
                }
            }
            if (count > 1) {
                //出现不相等的数字次数大于1
                orderVo.setOrder(false);
                orderVo.setVideoTs(tsList);
                return orderVo;
            }
            String tsName = firstName.substring(0, firstName.indexOf(firstNum, inconsistentIndex));
            String substring = tsName + (Integer.parseInt(firstNum) + size - 1);
            lastName = lastName.substring(0, lastName.indexOf(lastNum, inconsistentIndex)) + (Integer.parseInt(lastNum));
            if (substring.equals(lastName)) {
                tsName = tsName + "{num}.ts";
                //规则匹配
                orderVo.setOrder(true);
                //分片时间
                orderVo.setExtinf(lastExtinfTime);
                //最后一次分片时间
                orderVo.setTsLastTime(last.getTime());
                orderVo.setTsStart(firstNum);
                orderVo.setTsEnd(lastNum);
                orderVo.setTsName(tsName);
                orderVo.setVideoTs(tsList);
            } else {
                orderVo.setOrder(false);
                orderVo.setVideoTs(tsList);
                return orderVo;
            }
            return orderVo;
        }
    }

}
