package com.microservices.wiki.service.wiki;

import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson2.JSONArray;
import com.github.jhonnymertz.wkhtmltopdf.wrapper.Pdf;
import com.github.jhonnymertz.wkhtmltopdf.wrapper.configurations.WrapperConfig;
import com.github.jhonnymertz.wkhtmltopdf.wrapper.params.Param;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.wiki.domain.projects.Projects;
import com.microservices.wiki.domain.sys.UserVo;
import com.microservices.wiki.domain.wiki.Wiki;
import com.microservices.wiki.domain.wiki.WikiContentSimple;
import com.microservices.wiki.domain.wiki.WikiPage;
import com.microservices.wiki.domain.wiki.vo.WikiCloneLinkVo;
import com.microservices.wiki.domain.wiki.vo.WikiPageVo;
import com.microservices.wiki.domain.wiki.vo.WikiVo;
import com.microservices.wiki.mapper.members.MembersMapper;
import com.microservices.wiki.mapper.projects.ProjectsMapper;
import com.microservices.wiki.mapper.team_projects.TeamProjectsMapper;
import com.microservices.wiki.refactoring.response.HttpClientResult;
import com.microservices.wiki.refactoring.utils.*;
import com.microservices.wiki.service.sys.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Map;

@Component
public class WikiService {

    @Autowired
    private FileUtils fileUtils;

    @Resource
    private ProjectsMapper projectsMapper;

    @Resource
    private MembersMapper membersMapper;

    @Resource
    private TeamProjectsMapper teamProjectsMapper;

    @Value("${trustie_or_military_host}")
    private String trustieOrMilitaryHost;

    @Value("${gitea-wiki-api-domain}")
    private String giteaApiDomain;

    @Autowired
    private UserService userService;

    Logger logger = LoggerFactory.getLogger(this.getClass());


    public String getGiteaToken(HttpServletRequest request, Integer projectId) {
        UserVo user = userService.getCurrentUser(request);
        Integer userId = user.getUser_id() == null ? null : Integer.parseInt(user.getUser_id());
        //如果用户无权限，抛出异常
        if (!this.canViewWiki(userId, projectId)) {
            throw new ServiceException("项目私有/非项目成员");
        }
        return user.getGiteaToken();
    }

    public HttpClientResult getWikiPages(String owner, String repo, String giteaToken) throws Exception {

        String urlFormat = String.format("/api/hat/repos/%s/%s/wiki/page_names", owner, repo);
        if (giteaToken != null) {
            urlFormat += "?access_token=" + giteaToken;
        }
        String completeUrl = giteaApiDomain + urlFormat;
        logger.info("completeUrl:" + completeUrl);
        HttpClientResult httpClientResult = HttpClientUtils.doGet(completeUrl, null);
        logger.info("httpClientResultCode:" + httpClientResult.getCode());
        return httpClientResult;
    }


    public List<WikiPageVo> getWikiPageVoList(String giteaHttpsDomain, String giteaSSHDomain, String owner, String repo, HttpClientResult httpClientResult) {
        List<WikiPageVo> wikiPageVoList = JSONArray.parseArray(httpClientResult.getContent(), WikiPageVo.class);
        WikiCloneLinkVo wikiCloneLinkVo = new WikiCloneLinkVo();
        wikiCloneLinkVo.setHttps(giteaHttpsDomain + "/" + owner + "/" + repo + ".wiki.git");
        wikiCloneLinkVo.setSsh(giteaSSHDomain + ":" + owner + "/" + repo + ".wiki.git");
        for (WikiPageVo wikiPageVo : wikiPageVoList) {
            wikiPageVo.setWiki_clone_link(wikiCloneLinkVo);
        }
        return wikiPageVoList;
    }

    public HttpClientResult getWiki(String owner, String repo, String pageName, String URL, String giteaToken) throws Exception {
        String urlFormat = String.format("/repos/%s/%s/wiki/page/%s", owner, repo, pageName);
        if (giteaToken != null) {
            urlFormat += "?access_token=" + giteaToken;
        }
        String completeUrl = URL + urlFormat;
        logger.info("completeUrl: " + completeUrl);
        return HttpClientUtils.doGet(completeUrl, null, null);
    }

    public HttpClientResult createWiki(Wiki wiki, String token, String URL) throws Exception {
        String owner = wiki.getOwner();
        String repo = wiki.getRepo();
        String urlFormat = String.format("/repos/%s/%s/wiki/new?access_token=%s", owner, repo, token);
        Map<String, String> params;
        WikiVo wikiVo = BeanCopyUtils.beanPropertiesCopy(wiki, WikiVo.class);
        params = ConvertBean.convertToStringMap(wikiVo);
        logger.info("completeUrl: " + URL + urlFormat);
        String completeUrl = URL + urlFormat;
        return HttpClientUtils.doPost(completeUrl, null, params);
    }


    public HttpClientResult deleteWiki(Wiki wiki, String giteaToken, String URL) throws Exception {
        String owner = wiki.getOwner();
        String repo = wiki.getRepo();
        String pageName = wiki.getTitle();
        String urlFormat = String.format("/repos/%s/%s/wiki/page/%s?access_token=%s", owner, repo, pageName, giteaToken);
        String completeUrl = URL + urlFormat;
        logger.info("delete completeUrl " + completeUrl);
        return HttpClientUtils.doDelete(completeUrl);
    }

    public HttpClientResult updateWiki(Wiki wiki, String giteaToken, String URL) throws Exception {
        String owner = wiki.getOwner();
        String repo = wiki.getRepo();
        String pageName = wiki.getName();
        String urlFormat = String.format("/repos/%s/%s/wiki/page/%s?access_token=%s", owner, repo, pageName, giteaToken);
        WikiVo wikiVo = BeanCopyUtils.beanPropertiesCopy(wiki, WikiVo.class);
        String completeUrl = URL + urlFormat;
        logger.info("completeUrl: " + completeUrl);
        return HttpClientUtils.doPatch(completeUrl, ConvertBean.convertToStringMap(wikiVo));
    }


    public String StringToURLEncode(String s) throws UnsupportedEncodingException {
        s = java.net.URLEncoder.encode(s, "UTF-8").replaceAll("\\+", "-").replaceAll("\\*", "%2A");
        //如果encodeInput以-结尾，就在后面接上.-
        if (s.endsWith("-")) {
            s += ".-";
        };
        return s;
    }

    public void exportWikiToPdf(HttpServletResponse response, String toPdfToolPath, String owner, String repoName,
                                String URL, String destPath, String giteaToken) throws Exception {
        WrapperConfig wrapperConfig = new WrapperConfig(toPdfToolPath);
        Pdf pdf = new Pdf(wrapperConfig);
//        String wkhtmltopdfCommand = "D:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe";
        HttpClientResult httpClientResult = this.getWikiPages(owner, repoName, giteaToken);
        String content = httpClientResult.getContent();
        List<WikiPage> wikiPages = FastJsonUtils.toList(content, WikiPage.class);
        for (WikiPage wikiPage : wikiPages) {
            if ("".equals(wikiPage.getTitle()) || "_Sidebar".equals(wikiPage.getTitle())) {
                continue;
            }
            HttpClientResult httpClientResult1 = this.getWiki(owner, repoName, wikiPage.getSub_url(), URL, giteaToken);
            if (httpClientResult1.getCode() == 200) {
                WikiContentSimple wikiContentSimple = FastJsonUtils.toBean(httpClientResult1.getContent(), WikiContentSimple.class);
                wikiContentSimple.setSimple_content(MarkdownUtil.convertToHtml(wikiContentSimple.getContent_base64()));
                pdf.addPageFromString(processSimpleContentAttachments(wikiContentSimple.getSimple_content()));
//                logger.info("wikiContent" + wikiContentSimple.getSimple_content());
            }
        }

        pdf.setAllowMissingAssets();
        // Add a Table of Contents
        pdf.addToc();
        pdf.addTocParam(new Param("--toc-header-text", "目录"));
        // The `wkhtmltopdf` shell command accepts different types of options such as global, page, headers and footers, and toc. Please see `wkhtmltopdf -H` for a full explanation.
        // All options are passed as array, for example:
//        pdf.addParam(new Param("--no-footer-line"), new Param("--header-html", "file:///header.html"));
//        pdf.addParam(new Param("--enable-javascript"));

        pdf.addParam(new Param("--footer-center", "[page]/[toPage]"));
        pdf.addParam(new Param("--encoding", "UTF-8"));
        pdf.addParam(new Param("--javascript-delay", "1000"));


        // Add styling for Table of Contents
//        pdf.addTocParam(new Param("--xsl-style-sheet", "my_toc.xsl"));

        String pdfName = repoName + ".pdf";
        String completeDestPath = destPath + pdfName;
        // Save the PDF
        pdf.saveAs(completeDestPath);
        logger.info("导出cmd命令：" + pdf.getCommand());

        try {
            fileUtils.downLoadFile(response, completeDestPath, pdfName);

            //文件下载完毕删除临时文件
            org.apache.commons.io.FileUtils.forceDelete(new File(completeDestPath));
            pdf.cleanAllTempFiles();
        } catch (IOException e) {
            logger.warn("delete scratch pdf file fail: " + completeDestPath);
        }

    }

    public void exportWikiToMd(HttpServletResponse response, String owner, String repoName, String URL,
                               String destPath, String giteaToken) throws Exception {
        HttpClientResult httpClientResult = this.getWikiPages(owner, repoName, giteaToken);
        String content = httpClientResult.getContent();
        List<WikiPage> wikiPages = FastJsonUtils.toList(content, WikiPage.class);
        String dirPath = destPath + File.separator + repoName;
        for (WikiPage wikiPage : wikiPages) {
            if ("".equals(wikiPage.getTitle()) || "_Sidebar".equals(wikiPage.getTitle())) {
                continue;
            }
            HttpClientResult httpClientResult1 = this.getWiki(owner, repoName, wikiPage.getSub_url(), URL, giteaToken);
            if (httpClientResult1.getCode() == 200) {
                WikiContentSimple wikiContentSimple = FastJsonUtils.toBean(httpClientResult1.getContent(), WikiContentSimple.class);
                fileUtils.writeStringToFile(wikiContentSimple.getContent_base64(), wikiContentSimple.getTitle() + ".md", dirPath);
                logger.info("wikiContent" + wikiContentSimple.getSimple_content());
            }
        }
        downLoadZip(response, repoName, dirPath);

    }

    public void exportWikiToHtml(HttpServletResponse response, String owner, String repoName, String URL,
                                 String destPath, String projectName, String giteaToken) throws Exception {
        HttpClientResult httpClientResult = this.getWikiPages(owner, repoName, giteaToken);
        String content = httpClientResult.getContent();
        List<WikiPage> wikiPages = FastJsonUtils.toList(content, WikiPage.class);
        String dirPath = destPath + File.separator + repoName;
        StringBuilder htmlContent = new StringBuilder(this.initHtmlContent(projectName));
        for (WikiPage wikiPage : wikiPages) {
            if ("".equals(wikiPage.getTitle()) || "_Sidebar".equals(wikiPage.getTitle())) {
                continue;
            }
            HttpClientResult httpClientResult1 = this.getWiki(owner, repoName, wikiPage.getSub_url(), URL, giteaToken);
            if (httpClientResult1.getCode() == 200) {
                WikiContentSimple wikiContentSimple = FastJsonUtils.toBean(httpClientResult1.getContent(), WikiContentSimple.class);
                wikiContentSimple.setSimple_content(MarkdownUtil.convertToHtml(wikiContentSimple.getContent_base64()));
                htmlContent.append("<div class=\"wiki-content-title\">");
                htmlContent.append(wikiPage.getTitle());
                htmlContent.append("</div>");
                htmlContent.append("<div class=\"wiki-content-detail editor-content-panel markdown-body\">");
                htmlContent.append(processSimpleContentAttachments(wikiContentSimple.getSimple_content()));
                htmlContent.append("</div>");
            }
        }
        fileUtils.writeStringToFile(htmlContent.toString(), "index.html", dirPath);
        downLoadZip(response, repoName, dirPath);
    }

    public HttpClientResult createWikiByUploadFile(String giteaApiDomain, String giteaApiBaseUrl, String owner, String repoName, MultipartFile multipartFile, String fileName, String giteaToken) {
        String suffix = fileName.lastIndexOf(".") > -1 ? fileName.substring(fileName.lastIndexOf(".")) : "";
        if (!(".md".equals(suffix) || ".markdown".equals(suffix) || ".txt".equals(suffix))) {
            throw new RuntimeException("仅支持导入txt、markdown格式文件");
        }
        Wiki wiki = new Wiki();
        this.buildWiki(owner, repoName, fileName, multipartFile, wiki);
        HttpClientResult httpClientResult = null;
        try {
            httpClientResult = this.createWiki(wiki, giteaToken, giteaApiDomain + giteaApiBaseUrl);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return httpClientResult;
    }

    public boolean canViewWiki(Integer currentUserId, Integer projectId) {

        Projects projects = projectsMapper.getProjectById(projectId);
        //仓库不存在，不可访问
        if (projectId == null) {
            logger.info("projectId is null");
            return false;
        }
        //仓库公有，可访问
        if (projects != null && projects.getIsPublic()) {
            return true;
        }
        //判断是否为仓库成员
        if (projects != null) {
            return (membersMapper.doesMemberExist(currentUserId, projectId) > 0)
                    || (teamProjectsMapper.doesMemberExist(currentUserId, projectId) > 0);
        }
        return false;
    }

    public Boolean StringExist(String str){
        return str.startsWith("-");
    }

    private void downLoadZip(HttpServletResponse response, String repoName, String dirPath) {
        String completeDestPath = dirPath + ".zip";
        String zipFileName = repoName + ".zip";
        ZipUtil.zip(dirPath, completeDestPath, true);
        try {
            fileUtils.downLoadFile(response, completeDestPath, zipFileName);

            //文件下载完毕删除临时文件
            org.apache.commons.io.FileUtils.forceDelete(new File(completeDestPath));
            org.apache.commons.io.FileUtils.forceDelete(new File(dirPath));
        } catch (IOException e) {
            logger.warn("delete scratch zip file fail: " + completeDestPath);
        }
    }

    private void buildWiki(String owner, String repoName, String fileName, MultipartFile file, Wiki wiki) {
        try {
            String fileContent = new String(file.getBytes(), StandardCharsets.UTF_8);
            String base64Content = Base64.getEncoder().encodeToString(fileContent.getBytes(StandardCharsets.UTF_8));
            wiki.setOwner(owner);
            wiki.setRepo(repoName);
            wiki.setTitle(fileName.substring(0, fileName.lastIndexOf(".")));
            wiki.setContent_base64(base64Content);
        } catch (IOException e) {
            logger.error("导入文件转字节流异常");
            throw new RuntimeException("上传文件失败");
        }
    }

    private void deleteScratchFile(String filePath) {
        if(!filePath.isEmpty()){
            try {
                org.apache.commons.io.FileUtils.forceDelete(new File(filePath));
            } catch (IOException e) {
                logger.warn("delete scratch file fail: " + filePath);
            }
        }
    }

    //适用于wikiContentSimple中的content_base64字段
    private String processMarkdownAttachments(String content) {
        if (content == null) {
            return null;
        }
        // 匹配相对路径的附件链接并替换为完整路径，支持带别名和可选标题的格式
        return content.replaceAll("!\\[([^\\]]*)\\]\\(/api/attachments/(.+?)( \"[^\"]*\")?\\)", "![$1](" + trustieOrMilitaryHost + "/api/attachments/$2$3)");
    }

    private String processSimpleContentAttachments(String content) {
        if (content == null) {
            return null;
        }
        // 匹配相对路径的附件链接并替换为完整路径，支持带别名和可选标题的格式
        return content.replaceAll("\"/api/attachments", "\"" + trustieOrMilitaryHost + "/api/attachments");
    }

    private String initHtmlContent(String replaceString) {
        if (replaceString == null) {
            replaceString = "Trustie 3.0";
        }
        String htmlContent = "<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "\n" +
                "<head>\n" +
                "  <meta charset=\"UTF-8\">\n" +
                "  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n" +
                "  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
                "  <title>trustie 3.0</title>\n".replace("trustie 3.0", replaceString) +
                "  <style type=\"text/css\">\n" +
                "    body {\n" +
                "      margin: 0;\n" +
                "      padding: 0;\n" +
                "      overflow-y: scroll;\n" +
                "    }\n" +
                "\n" +
                "    .preview-head {\n" +
                "      display: flex;\n" +
                "      justify-content: space-between;\n" +
                "      align-items: center;\n" +
                "      padding: 0 12rem 0 2rem;\n" +
                "      width: 100%;\n" +
                "      height: 80px;\n" +
                "      background: #272f4c;\n" +
                "      color: #fff;\n" +
                "    }\n" +
                "\n" +
                "    .preview-head-left {\n" +
                "      display: inline-flex;\n" +
                "      align-items: center;\n" +
                "      font-size: 24px;\n" +
                "      cursor: pointer;\n" +
                "    }\n" +
                "\n" +
                "    .preview-head-left svg {\n" +
                "      margin-right: 10px;\n" +
                "      font-size: 24px !important;\n" +
                "      font-weight: 700;\n" +
                "    }\n" +
                "\n" +
                "    .content {\n" +
                "      width: 1200px;\n" +
                "      min-height: 400px;\n" +
                "      margin: 20px auto;\n" +
                "    }\n" +
                "\n" +
                "    .wiki-content-title{\n" +
                "      margin-bottom: 10px;\n" +
                "      padding: 10px 0;\n" +
                "      font-size: 20px;\n" +
                "      font-weight: 700;\n" +
                "      border-bottom: 1px solid #bbb;\n" +
                "    }\n" +
                "\n" +
                "    .wiki-content-detail {\n" +
                "      margin-bottom: 30px;\n" +
                "      word-break: break-all;\n" +
                "    }\n" +
                "\n" +
                "    .markdown-body img{\n" +
                "      max-width: 100%;\n" +
                "    }\n" +
                "    @media screen and (max-width: 1200px){\n" +
                "      .content {\n" +
                "        width: 96%;\n" +
                "      }\n" +
                "    }\n" +
                "\n" +
                "  </style>\n" +
                "</head>\n" +
                "\n" +
                "<body>\n" +
                "  <div class=\"preview-head\">\n" +
                "    <div class=\"preview-head-left\"><svg width=\"26px\" height=\"26px\" viewBox=\"0 0 26 26\" version=\"1.1\">\n" +
                "        <title>文档预览_icon</title>\n" +
                "        <g id=\"页面设计\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n" +
                "          <g id=\"12-预览\" transform=\"translate(-53.000000, -27.000000)\" fill=\"#FFFFFF\" fill-rule=\"nonzero\">\n" +
                "            <g id=\"文档预览_icon\" transform=\"translate(53.000000, 27.000000)\">\n" +
                "              <g id=\"编组-3\" transform=\"translate(1.000000, 1.000000)\">\n" +
                "                <path\n" +
                "                  d=\"M16.5,1.5 C16.0857864,1.5 15.75,1.16421356 15.75,0.749999997 C15.75,0.335786436 16.0857864,0 16.5,0 L20.8815,0 C22.605,0 24,1.39499999 24,3.11850001 L24,7.5 C24,7.7679492 23.8570508,8.01554448 23.625,8.14951908 C23.3929492,8.28349369 23.1070508,8.28349369 22.875,8.14951908 C22.6429492,8.01554448 22.5,7.7679492 22.5,7.5 L22.5,3.11850001 C22.5,2.22449999 21.7755,1.5 20.8815,1.5 L16.5,1.5 Z M22.5,16.1445 C22.5,15.8765508 22.6429492,15.6289555 22.875,15.4949809 C23.1070508,15.3610063 23.3929492,15.3610063 23.625,15.4949809 C23.8570508,15.6289555 24,15.8765508 24,16.1445 L24,20.8815 C24,22.605 22.605,24 20.8815,24 L17.9205,24 C17.6525508,24 17.4049555,23.8570508 17.2709809,23.625 C17.1370063,23.3929492 17.1370063,23.1070508 17.2709809,22.875 C17.4049555,22.6429492 17.6525508,22.5 17.9205,22.5 L20.8815,22.5 C21.7755,22.5 22.5,21.7755 22.5,20.8815 L22.5,16.1445 L22.5,16.1445 Z M7.85549999,22.5 C8.12344919,22.5 8.37104447,22.6429492 8.50501908,22.875 C8.63899369,23.1070508 8.63899369,23.3929492 8.50501908,23.625 C8.37104447,23.8570508 8.12344919,24 7.85549999,24 L3.11850001,24 C1.39620001,24 0,22.6038 0,20.8815 L0,16.5 C0,16.0857864 0.335786436,15.75 0.749999997,15.75 C1.16421356,15.75 1.5,16.0857864 1.5,16.5 L1.5,20.8815 C1.5,21.7755 2.22449999,22.5 3.11850001,22.5 L7.85549999,22.5 Z M1.5,7.5 C1.5,7.91421355 1.16421356,8.24999999 0.749999997,8.24999999 C0.335786436,8.24999999 0,7.91421355 0,7.5 L0,3.11850001 C0,1.39499999 1.39499999,0 3.11850001,0 L7.5,0 C7.91421355,0 8.24999999,0.335786436 8.24999999,0.749999997 C8.24999999,1.16421356 7.91421355,1.5 7.5,1.5 L3.11850001,1.5 C2.22449999,1.5 1.5,2.22449999 1.5,3.11850001 L1.5,7.5 Z M12,15.75 C14.5695,15.75 16.9605,14.4165 19.227,11.625 C16.9605,8.83349999 14.5695,7.49999999 12,7.49999999 C9.43050001,7.49999999 7.0395,8.83349999 4.773,11.625 C7.0395,14.4165 9.43050001,15.75 12,15.75 Z M12,17.25 C8.93849999,17.25 6.141,15.69 3.60900001,12.57 C3.16219028,12.0192218 3.16219028,11.2307782 3.60900001,10.68 C6.141,7.55999999 8.93849999,6 12,6 C15.0615,6 17.859,7.55999999 20.391,10.68 C20.8378097,11.2307782 20.8378097,12.0192218 20.391,12.57 C17.859,15.69 15.0615,17.25 12,17.25 Z\"\n" +
                "                  id=\"形状\" stroke=\"#FFFFFF\" stroke-width=\"0.5\"></path>\n" +
                "                <path\n" +
                "                  d=\"M12,13.5 C13.0355339,13.5 13.875,12.6605339 13.875,11.625 C13.875,10.5894661 13.0355339,9.75000001 12,9.75000001 C10.9644661,9.75000001 10.125,10.5894661 10.125,11.625 C10.125,12.6605339 10.9644661,13.5 12,13.5 Z M12,15 C10.7941616,15.0001005 9.67987806,14.3568511 9.07692987,13.3125812 C8.47398168,12.2683113 8.47398168,10.9816887 9.07692987,9.93741879 C9.67987806,8.89314889 10.7941616,8.2498995 12,8.24999999 C13.8638512,8.25015532 15.3747188,9.76114879 15.3747188,11.625 C15.3747188,13.4888512 13.8638512,14.9998447 12,15 Z\"\n" +
                "                  id=\"形状\"></path>\n" +
                "              </g>\n" +
                "            </g>\n" +
                "          </g>\n" +
                "        </g>\n" +
                "      </svg>\n" +
                "      <!-- 项目名，请替换 -->\n" +
                "      <span class=\"ml10\">Trustie 3.0</span></div>\n".replace("Trustie 3.0", replaceString) +
                "  </div>\n" +
                "  <div class=\"content\">";
        return htmlContent;
    }
}
