package com.yutou.qqbot.models.BiliBili;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.napcat.enums.MessageEnum;
import com.yutou.napcat.event.MessageEvent;
import com.yutou.napcat.handle.MessageHandleBuild;
import com.yutou.napcat.handle.OtherHandle;
import com.yutou.napcat.handle.Reply;
import com.yutou.napcat.http.NapCatApi;
import com.yutou.napcat.model.MessageBean;
import com.yutou.okhttp.HttpBody;
import com.yutou.qqbot.Annotations.UseModel;
import com.yutou.qqbot.QQBotManager;
import com.yutou.qqbot.bilibili.*;
import com.yutou.qqbot.interfaces.ObjectInterface;
import com.yutou.qqbot.models.Model;
import com.yutou.qqbot.utlis.AppTools;
import com.yutou.qqbot.utlis.ConfigTools;
import com.yutou.qqbot.utlis.HttpTools;
import com.yutou.qqbot.utlis.StringUtils;
import retrofit2.Response;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@UseModel
public class BiliVideo extends Model {
    public String downloadPath = "tmp";
    List<DanmuData> danmuDatas = new ArrayList<>();
    long danmuNextTime = 0;
    private BiliBiliUtils biliUtils;
    private long qq;

    public BiliVideo(long qq) {
        this.qq = qq;
        biliUtils = BiliBiliUtils.getInstance(qq);
    }

    public BiliVideo() {
        this.qq = QQBotManager.defQQ;
        biliUtils = BiliBiliUtils.getInstance(qq);
    }

    @Override
    public boolean isUserPublic() {
        return true;
    }

    @Override
    public String[] getUsePowers() {
        return new String[0];
    }

    @Override
    public String getModelName() {
        return "B站视频下载";
    }

    @Override
    public void onMessage(Long qq, MessageEvent event, boolean isGroup) {
        super.onMessage(qq, event, isGroup);
        if (event.isAtMe() && event.hasType(MessageEnum.REPLY)) {
            Reply reply = event.findType(Reply.class);
            long id = reply.getData().getId();
            if (msg.contains("省流") || msg.contains("总结")) {
                String value = onAIVideo(id);
                if (!StringUtils.isEmpty(value)) {
                    QQBotManager.getInstance().sendMessage(qq, value);
                }
            }
        }
    }

    private String onAIVideo(long id) {
        try {
            Response<HttpBody<MessageBean>> execute = NapCatApi.getMessageApi().getMessage(MessageHandleBuild.create().setMessageId(String.valueOf(id)).build()).execute();
            if(execute.body()==null){
                return "省流失败";
            }
            MessageEvent handle = MessageEvent.parseHandleHttp(execute.body().getSrc());
            if (handle.hasType(MessageEnum.JSON)) {
                OtherHandle type = handle.findType(OtherHandle.class);
                String url = type.getData().getMeta().getDetail1().getQqdocurl();
                if (StringUtils.isEmpty(url)) {
                    return "地址不正确";
                }
                if (url.startsWith("BV")) {
                    url = "https://www.bilibili.com/video/" + url.trim();
                }
                if (!url.startsWith("https://www.bilibili.com/video/") && !url.startsWith("https://b23.tv")) {
                    return "这是B站吗?";
                }
                String ai = BiliBiliAppUtils.getVideoAI(url.trim());
                if (!StringUtils.isEmpty(ai)) {
                    return ai;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        return "省流失败";
    }

    public void downVideo(String url) {
        downVideo(url, true, false);
    }

    public void downVideo(String url, boolean downDanmu, boolean merge) {
        if (biliUtils == null || !new BiliLogin(qq).testLogin()) {
            System.err.println("未登录");
            return;
        }
        if (!url.contains("?")) {
            url += "?";
        }
        danmuDatas.clear();
        JSONObject info = getVideoInfo(url);
        if (info.getInteger("code") == 0) {
            JSONObject infoData = info.getJSONObject("data");
            JSONObject eps = new JSONObject();
            if (infoData.containsKey("ugc_season")) {
                JSONObject ugc = infoData.getJSONObject("ugc_season");
                eps.put("title", ugc.getString("title"));
                JSONArray ep = new JSONArray();
                for (Object o : ugc.getJSONArray("sections")) {
                    JSONObject season = (JSONObject) o;
                    for (Object episodes : season.getJSONArray("episodes")) {
                        JSONObject _epi = (JSONObject) episodes;
                        JSONObject _item = new JSONObject();
                        _item.put("title", season.getString("title") + "-" + _epi.getString("title"));
                        _item.put("cid", _epi.getLong("cid"));
                        _item.put("aid", _epi.getLong("aid"));
                        ep.add(_item);
                    }
                }
                eps.put("eps", ep);
            } else if (infoData.getInteger("videos") != 1) {
                JSONArray pages = infoData.getJSONArray("pages");
                JSONArray ep = new JSONArray();
                for (Object o : pages) {
                    JSONObject page = (JSONObject) o;
                    JSONObject _item = new JSONObject();
                    _item.put("title", infoData.getString("title") + "-" + page.getString("part"));
                    _item.put("cid", page.getLong("cid"));
                    _item.put("aid", infoData.getLong("aid"));
                    ep.add(_item);
                }
                eps.put("title", infoData.getString("title"));
                eps.put("eps", ep);
            } else {
                eps.put("title", infoData.getString("title"));
                eps.put("cid", infoData.getLong("cid"));
            }
            JSONObject json = new JSONObject();
            json.put("danmu", downDanmu);
            json.put("merge", merge);
            json.put("avid", infoData.getLong("aid"));
            if (eps.containsKey("cid")) {
                json.put("cid", eps.getLong("cid"));
                json.put("qn", 127);
                json.put("fnval", 80);
                json.put("fourk", 1);
                downVideo(json, eps);
            } else {
                System.out.println("json = " + json);
                System.out.println("eps = " + eps);
                List<File> list = new ArrayList<>();

                String root = new File("tmp").getAbsolutePath() + File.separator;
                for (Object o : eps.getJSONArray("eps")) {
                    JSONObject item = (JSONObject) o;
                    json.put("avid", item.getLong("aid"));
                    json.put("cid", item.getLong("cid"));
                    json.put("qn", 127);
                    json.put("fnval", 80);
                    json.put("fourk", 1);
                    item.put("title", eps.getString("title") + "$(File.separator)" + item.getString("title"));
                    list.add(new File(root + StringUtils.toSaveFileName(item.getString("title") + ".mp4")));
                    downVideo(json, item);
                    if (downDanmu && merge) {
                        long tmp = 0;
                        for (VideoDanMu.DanmakuElem elem : buildDanmuHttp(json.getLong("cid"), json.getLong("avid"), 1)) {
                            DanmuData danmuData = new DanmuData();
                            danmuData.setDanmu(elem.getContent());
                            danmuData.setFontSize(elem.getFontsize());
                            danmuData.setTime(elem.getProgress() + danmuNextTime);
                            danmuData.setFontColor(elem.getColor());
                            danmuData.setModel(elem.getMode());
                            if (elem.getProgress() > tmp) {
                                tmp = elem.getProgress();
                            }
                            danmuDatas.add(danmuData);
                        }
                        danmuNextTime = tmp;
                    }
                }
                if (merge) {
                    merge(root, StringUtils.toSaveFileName(eps.getString("title")), list);
                }
            }

        }
    }

    private void merge(String root, String name, List<File> files) {
        String saveName = root + name;
        File fileList = new File(saveName + File.separator + "tmp.txt");
        System.out.println("fileList.getAbsolutePath() = " + fileList.getAbsolutePath());
        StringBuilder builder = new StringBuilder();
        int i = 0;
        List<File> tmp = new ArrayList<>();
        for (File file : files) {
            System.out.println("file.getName() = " + file.getName());
            file.renameTo(new File(file.getParentFile(), i + ".mp4"));
            tmp.add(new File(file.getParentFile(), i + ".mp4"));
            builder.append("file '").append(i++).append(".mp4'");
            builder.append("\n");
        }
        try {
            // boolean b = fileList.createNewFile();
            // System.out.println("b = " + b);
            FileWriter fw = new FileWriter(fileList);
            fw.write(builder.toString());
            fw.flush();
            fw.close();

            String exec = String.format("cd \"%s\" && ffmpeg -f concat -i \"%s\" -c copy %s.mp4", saveName, "tmp.txt", name);
            System.out.println("exec = " + exec);
            AppTools.exec(exec, new ObjectInterface() {
                @Override
                public void out(String data) {
                    super.out(data);
                    //   System.out.println(data);
                    System.out.println("over");
                    fileList.delete();
                    for (File file : tmp) {
                        file.delete();
                    }
                    AssTools tools = new AssTools(name);
                    tools.addDanmu(danmuDatas);
                    boolean saveDanmu = tools.saveDanmu(saveName);
                    System.out.println("弹幕保存:" + saveDanmu);
                }
            }, false, false);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private void downVideo(JSONObject json, JSONObject eps) {
        eps.put("title", StringUtils.toSaveFileName(eps.getString("title")));
        File tmp = new File(HttpTools.downloadPath + eps.getString("title") + ".mp4");
        if (json.getBooleanValue("danmu") && !json.getBooleanValue("merge")) {
            List<VideoDanMu.DanmakuElem> elems = buildDanmuHttp(json.getLong("cid"), json.getLong("avid"), 1);
            downDanmu("tmp", eps.getString("title"), elems);
        }
        if (tmp.exists()) {
            return;
        }
        JSONObject http = biliUtils.http("https://api.bilibili.com/x/player/playurl?" + HttpTools.toUrlParams(json), BiliBiliUtils.HTTP.GET, null, BiliBiliUtils.RET_MODEL.JSON);
        if (http.getInteger("code") == 0) {
            JSONObject data = http.getJSONObject("data");
            JSONObject dash = data.getJSONObject("dash");
            JSONObject video = dash.getJSONArray("video").getJSONObject(0);
            JSONObject audio = dash.getJSONArray("audio").getJSONObject(0);

            File videoFile = biliUtils.download(video.getString("baseUrl"), eps.getString("title") + "_video.mp4", false);
            if (videoFile == null) {
                downVideo(json, eps);
                return;
            }
            File audioFile = biliUtils.download(audio.getString("baseUrl"), eps.getString("title") + "_audio.mp3", false);
            if (audioFile == null) {
                videoFile.delete();
                downVideo(json, eps);
                return;
            }
            save(eps.getString("title"), videoFile, audioFile);
        }
    }

    private void downDanmu(String savePath, String title, List<VideoDanMu.DanmakuElem> danmuList) {
        try {
            AssTools tools = new AssTools(title);
            List<DanmuData> list = new ArrayList<>();
            for (VideoDanMu.DanmakuElem elem : danmuList) {
                DanmuData danmuData = new DanmuData();
                danmuData.setDanmu(elem.getContent());
                danmuData.setFontSize(elem.getFontsize());
                danmuData.setTime(elem.getProgress());
                danmuData.setFontColor(elem.getColor());
                danmuData.setModel(elem.getMode());
                list.add(danmuData);
            }
            tools.addDanmu(list);
            boolean saveDanmu = tools.saveDanmu(savePath);
            System.out.println("弹幕保存:" + saveDanmu);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private List<VideoDanMu.DanmakuElem> buildDanmuHttp(long cid, long avid, int segment_index) {
        List<VideoDanMu.DanmakuElem> tmp, danmuList = new ArrayList<>();
        JSONObject json = new JSONObject();
        json.put("type", 1);
        json.put("oid", cid);
        json.put("avid", avid);
        json.put("segment_index", segment_index);
        while (!(tmp = getDanmu(json)).isEmpty()) {
            danmuList.addAll(tmp);
            json.put("segment_index", ++segment_index);
        }
        return danmuList;
    }

    private List<VideoDanMu.DanmakuElem> getDanmu(JSONObject json) {
        try {
            byte[] http = biliUtils.http("https://api.bilibili.com/x/v2/dm/web/seg.so?" + HttpTools.toUrlParams(json), BiliBiliUtils.HTTP.GET, null, BiliBiliUtils.RET_MODEL.BYTE);
            VideoDanMu.DmSegMobileReply parse = VideoDanMu.DmSegMobileReply.parseFrom(http);
            return parse.getElemsList();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ArrayList<>();

    }


    private void save(String name, File videoFile, File audioFile) {
        List<String> urls = new ArrayList<>();
        urls.add(videoFile.getAbsolutePath());
        urls.add(audioFile.getAbsolutePath());

        StringBuilder builder = new StringBuilder();
        builder.append(ConfigTools.load(ConfigTools.CONFIG, "ffmpeg", String.class)).append(" ");
        for (String _url : urls) {
            builder.append("-i").append(" ");
            builder.append("\"").append(_url).append("\"").append(" ");
        }
        builder.append("-vcodec").append(" ");
        builder.append("copy").append(" ");
        builder.append("-acodec").append(" ");
        builder.append("copy").append(" ");
        builder.append("-threads").append(" ");
        builder.append("8").append(" ");
//            builder.append("-y").append(" ");
        builder.append("\"").append(new File(HttpTools.downloadPath + name + ".mp4").getAbsolutePath()).append("\"").append(" ");
        System.out.println(builder);
        AppTools.exec(builder.toString(), new ObjectInterface() {
            @Override
            public void out(String data) {
                super.out(data);
                videoFile.delete();
                audioFile.delete();
            }

        }, false, false);
    }

    public JSONObject getVideoInfo(String url) {
        if (!new BiliLogin(qq).testLogin()) {
            System.err.println("未登录");
            return null;
        }
        JSONObject json = buildJSON(url);
        if (json != null) {
            return biliUtils.http("https://api.bilibili.com/x/web-interface/view?" + HttpTools.toUrlParams(json), BiliBiliUtils.HTTP.GET, null, BiliBiliUtils.RET_MODEL.JSON);
        }
        return null;
    }

    private JSONObject buildJSON(String url) {
        if (!url.contains("?")) {
            url += "?";
        }
        Pattern pattern = Pattern.compile("(?<=video/).*?(?=\\?)");
        Matcher matcher = pattern.matcher(url);
        String id = null;
        if (matcher.find()) {
            id = matcher.group();
        }
        if (id == null) {
            return null;
        }
        if (id.contains("/")) {
            id = id.replace("/", "");
        }
        JSONObject json = new JSONObject();
        if (id.startsWith("BV")) {
            json.put("bvid", id);
        } else {
            json.put("avid", id.toLowerCase().replace("av", ""));
        }
        return json;
    }

    public static void main(String[] args) {
        BiliVideo video = new BiliVideo(QQBotManager.defQQ);
        JSONObject login = new BiliLogin(QQBotManager.defQQ).login();
        System.out.println(login);
        //岚少 480
        //video.downVideo("https://www.bilibili.com/video/BV1Ps411m7pt?spm_id_from=333.999.0.0");
        //唐诱 合集
        //video.downVideo("https://www.bilibili.com/video/BV1Vv4y1K7ox?spm_id_from=444.41.top_right_bar_window_default_collection.content.click");
        //邦邦 长视频
        // video.downVideo("https://www.bilibili.com/video/BV1w5411271A?spm_id_from=444.41.list.card_archive.click");
        //LK 超清4k hdr
        //video.downVideo("https://www.bilibili.com/video/BV1uZ4y1U7h8/?spm_id_from=333.788.recommend_more_video.-1");
        // hdr
        //   video.downVideo("https://www.bilibili.com/video/BV1rp4y1e745/?spm_id_from=333.788.recommend_more_video.-1");
        // 1080+ 60fps
        //video.downVideo("https://www.bilibili.com/video/BV1qF411T7Vf?spm_id_from=444.41.list.card_archive.click");
        //唐诱正片
        //video.downVideo("https://www.bilibili.com/video/BV1L44y147zR?spm_id_from=333.999.0.0");// ep1
        //video.downVideo("https://www.bilibili`.com/video/BV1Zu4y1B7DU/?spm_id_from=333.337.search-card.all.click", true, false);// ep5
        // video.downVideo("https://www.bilibili.com/video/BV1SL411g7FS/?spm_id_from=333.788.recommend_more_video.0"); //all ig 1\5

        // video.downVideo("https://www.bilibili.com/video/BV18L4y1H7rz?spm_id_from=333.999.0.0");
        // video.downVideo("https://www.bilibili.com/video/BV1Pe4y1Q7MX?spm_id_from=444.41.top_right_bar_window_history.content.click");
        // int a=16|2048;
        // System.out.println("a = " + a);
        //video.downDanmu(428855000L,976216102L,"【都市_情感】《唐可可的诱惑》第一集",1);
        video.downVideo("https://www.bilibili.com/bangumi/play/ep776259", false, false);// ep5
        System.out.println("事件结束");
    }
}
