<template>
  <div class="com-viewer">
    <!-- getComponentType() -->
    <div class="content" ref="content">
      <template v-for="(item, index) in coms">
        <component
          :key="index"
          :is="item.type"
          :com="item"
          :coms="comList"
          :currentIndex="index"
          :isActive="false"
          @onUpdateData="onUpdateData"
        />
      </template>
    </div>
    <div class="export">
      <el-button
        @click="onExports"
        type="primary"
        :loading="exporting"
        :disabled="exporting"
        >导出</el-button
      >
      <el-progress v-if="exporting" :percentage="exportProgress" />
    </div>
  </div>
</template>

<script>
import { exportDocument } from "@/api/document";
import TextArea from "./components/TextView";
import htmlDocx from "html-docx-js/dist/html-docx";
import jsPDF from "jspdf";
import html2canvas from "html2canvas";
import { findData } from "@/api/sourceData";
import HTMLtoDOCX from "html-to-docx";
import { saveAs } from "file-saver";

export default {
  name: "Viewer",
  components: {
    TextArea,
  },
  props: {
    id: {
      type: Number,
      default: 0,
    },
    coms: {
      type: Array,
      default: null,
    },
    docAttr: {
      type: Object,
      default: () => ({
        title: "",
      }),
    },
  },
  watch: {
    coms: {
      handler(val) {
        if (val == null) return;
        this.comList = JSON.parse(JSON.stringify(val));
        this.replaceData(val);
      },
      immediate: true,
      deep: true,
    },
  },
  data() {
    return {
      comList: [],
      content: "",
      exporting: false, // 新增：用于跟踪导出状态
      exportProgress: 0,
    };
  },
  methods: {
    async replaceData(data) {
      for (let item of this.comList) {
        for (let el of item.attrs) {
          let attrId = el.id;
          if (el.type == "formual") {
            try {
              let formual = await this.analysisFormual(el);

              formual = await this.getRemote(formual);
              el.content = eval(formual);
            } catch (error) {}
          } else if (el.type == "variableNull") {
            console.log(el);
          }
        }
      }
      //this.$emit('update:coms', [...this.comList])
      return data;
    },
    //分析公式
    async analysisFormual(attrs) {
      let _this = this;
      // console.log("开始分析公式:", attrs.formula);
      var pattern = /(\[.*?\]){3}/;
      var formual = attrs.formula;
      var reg = new RegExp(pattern);
      while (true) {
        var items = formual.match(reg);
        if (items == null) break;
        let itemName = items[0];
        // console.log("处理项:", itemName);
        try {
          let data = await _this.getFormualData(itemName);
          // console.log(`获取到的数据: ${itemName} = ${data}`);
          if (data === null || data === undefined || isNaN(data)) {
            console.warn(`获取到的数据无效: ${itemName}`);
            formual = formual.replace(itemName, "(0)");
          } else {
            formual = formual.replace(itemName, `(${parseFloat(data)})`);
          }
        } catch (error) {
          console.error(`处理 ${itemName} 时出错:`, error);
          formual = formual.replace(itemName, "(0)");
        }
      }
      return formual;
    },
    async getFormualData(formualItem) {
      let _this = this;
      // console.log("开始获取公式数据:", formualItem);
      var pattern = /\[(.*?)\]\[(.*?)\]\[(.*?)\]/;
      var reg = new RegExp(pattern);
      let items = formualItem.match(reg);
      // console.log("解析结果:", items);
      let calResult = 0;
      if (items && items[1] == "T") {
        calResult = await _this.getModuleData(items[2], items[3]);
      } else {
        console.warn("无法解析公式项:", formualItem);
      }
      // console.log("计算结果:", calResult);
      return calResult;
    },
    //获取本地文档模块数据
    async getModuleData(code, attrName) {
      let _this = this;
      // console.log(`尝试获取模块数据: code=${code}, attrName=${attrName}`);
      // console.log("当前 coms:", _this.coms);

      let item = _this.comList.filter((o) => o.name == code);
      // console.log(`找到的模块:`, item);

      if (item.length > 0) {
        let attr = item[0].attrs.filter((o) => o.name == attrName);
        // console.log(`找到的属性:`, attr);

        if (attr.length > 0) {
          // console.log(`返回的值:`, attr[0].content);
          return parseFloat(attr[0].content);
        }
      }
      // console.log("未找到匹配的模块或属性，返回 0");
      return 0;
    },

    async getRemote(formual) {
      let _this = this;
      var pattern = /\[(.*?)\]\[(.*?)\]\[(.*?)\]\[(.*?)\]/;
      var reg = new RegExp(pattern);
      while (true) {
        let items = formual.match(reg);
        if (items == null) break;
        if (items[1] == "R") {
          //表示获取远程数据
          var dataIndex = items[4];
          dataIndex = items[4].split(",");
          let calResult = await _this.getRemoteData(
            items[2],
            items[3],
            dataIndex[0],
            dataIndex[1]
          );
          let itemName = items[0];
          // 检查calResult是否为数字
          if (!isNaN(calResult)) {
            formual = formual.replace(
              itemName,
              "(" + parseFloat(calResult) + ")"
            );
          } else {
            formual = formual.replace(itemName, `"${calResult}"`);
          }
          formual = formual.replace(
            itemName,
            "(" + parseFloat(calResult) + ")"
          );
        }
      }
      return formual;
    },
    async getRemoteData(code, sheet, row, col) {
      let _this = this;
      let result = 0;
      let par = {
        code: code,
        sheetName: sheet,
        row: row,
        col: col,
      };

      /*  if (!this.isEditing) {
        this.loading = true;
        this.progress = 0;

        try {
          const updateProgress = setInterval(() => {
            if (this.progress < 90) {
              this.progress += 10;
            }
          }, 100);

          let res = await findData(par);
          if (res.status == 200) {
            result = res.data.result;
          }

          this.progress = 100;
          clearInterval(updateProgress);
        } catch (error) {
          console.error("Error fetching remote data:", error);
        } finally {
          setTimeout(() => {
            this.loading = false;
            this.progress = 0;
          }, 1000);
        }
      } else { */
      // 如果正在编辑，直接获取数据而不显示进度条
      try {
        let res = await findData(par);
        if (res.status == 200) {
          result = res.data.result;
          // 添加数据类型检查和转换
          if (typeof result === "string" && !isNaN(result)) {
            result = parseFloat(result);
          }
        }
      } catch (error) {
        console.error("获取远程数据错误:", error);
      }
      /*   } */

      return result;
    },
    getComponentType(type) {
      if (type === "textarea") {
        return TextArea;
      }
      // Add other component types if needed
      return type;
    },
    onUpdateData(index, data) {
      this.comList[index].content = data;
      this.rebuildContent();
      //this.$emit('update:coms', [...this.comList])
    },
    rebuildContent() {
      this.content = this.comList.map((item) => item.content).join("");
    },
    async onExport() {
      if (this.exporting) return; // 如果正在导出，直接返回
      this.exporting = true; // 设置导出状态为 true

      try {
        let contentClone = this.$refs.content.cloneNode(true);
        /* const templateTextareas =
          contentClone.querySelectorAll(".template-textarea");

        if (templateTextareas.length > 0) {
          // 创建目录页
          const tocPage = document.createElement("div");
          tocPage.className = "toc-page";
          tocPage.innerHTML = `
    <div style="page-break-before: always; page-break-after: always; height: 100vh; display: flex; flex-direction: column; justify-content: center;">
      <h1 style="text-align: center; margin-bottom: 40px;">目录</h1>
      <div id="toc" style="margin: 0 auto; min-width: 600px;"></div>
    </div>
  `;

          // 在第一个 template-textarea 之后插入目录页
          templateTextareas[0].parentNode.insertBefore(
            tocPage,
            templateTextareas[0].nextSibling
          );

          // 生成目录内容
          let tocHtml = '<div style="column-count: 1; column-gap: 40px;">';
          templateTextareas.forEach((textarea, index) => {
            const title = textarea.querySelector("h1, h2, h3, h4, h5, h6");
            if (title) {
              tocHtml += `
                <p style="margin: 0; padding: 5px 0;">
                  <span style="float: left;">${title.textContent}</span>
                  <span style="float: right;">${index + 1}</span>
                  <span style="display: block; overflow: hidden; height: 1.2em; border-bottom: 1px dotted black;"></span>
                </p>
              `;
              title.id = `section-${index + 1}`;
            }
          });
          tocHtml += "</div>";

          // 将目录内容插入到目录页中
          contentClone.querySelector("#toc").innerHTML = tocHtml;
        } */
        const templateTextareas =
          contentClone.querySelectorAll(".template-textarea");

        if (templateTextareas.length > 0) {
          // 创建空白页和目录页
          const blankPage = document.createElement("div");
          blankPage.className = "blank-page";
          blankPage.innerHTML = '<div style="page-break-after: always;"></div>';

          const tocPage = document.createElement("div");
          tocPage.className = "toc-page";
          tocPage.innerHTML = `
            <div class="toc-content">
              <h1 style="text-align: center; margin-bottom: 40px;">目录</h1>
              <div id="toc"></div>
            </div>
          `;

          // 在第一个 template-textarea 之后插入空白页和目录页
          templateTextareas[0].parentNode.insertBefore(
            tocPage,
            templateTextareas[0].nextSibling
          );
          templateTextareas[0].parentNode.insertBefore(blankPage, tocPage);

          // 生成目录内容
          let tocHtml = "";
          templateTextareas.forEach((textarea, index) => {
            const title = textarea.querySelector("h1, h2, h3, h4, h5, h6");
            if (title) {
              tocHtml += `
                <div class="toc-item">
                  <span class="toc-text">${title.textContent}</span>
                  <span class="toc-page-num">${index + 2}</span>
                </div>
              `;
              title.id = `section-${index + 2}`;
            }
          });

          // 将目录内容插入到目录页中
          contentClone.querySelector("#toc").innerHTML = tocHtml;
        }

        contentClone.querySelectorAll('input[type="text"]').forEach((input) => {
          const span = document.createElement("span");
          span.textContent = input.value;
          input.parentNode.replaceChild(span, input);
        });

        let contentData = `<!DOCTYPE html><html><head><meta charset="UTF-8"><style>
          @page {
            size: A4;
            margin: 2cm;
          }
          body {
            font-family: Arial, sans-serif;
            font-size: 12pt;
            line-height: 1.5;
          }
          table {
            width: 100%;
            border-collapse: collapse;
          }
          td, th {
            border: 1px solid black;
            padding: 5px;
          }
          .template-textarea {
            page-break-after: always;
          }
           .toc-page {
                page-break-before: always;
                page-break-after: always;
              }
              .toc-content {
                height: 100vh;
                display: flex;
                flex-direction: column;
                justify-content: flex-start;
                padding-top: 100px;
              }
              .toc-item {
                display: flex;
                justify-content: space-between;
                margin-bottom: 10px;
              }
              .toc-text {
                margin-right: 10px;
              }
              .toc-page-num {
                flex-shrink: 0;
              }
              .toc-item::after {
                content: "";
                border-bottom: 1px dotted black;
                flex-grow: 1;
                order: 2;
                margin: 0 5px;
                position: relative;
                top: -5px;
              }
              .toc-text {
                order: 1;
              }
              .toc-page-num {
                order: 3;
              }
              
              .TOC {
                display: none !important;
              }
        </style></head><body>${contentClone.innerHTML}</body></html>`; /*`<!DOCTYPE html><html><head><meta charset="UTF-8"><style>
      @media print {
         body {
          width: 210mm;
           height: 297mm;

                margin: 0;
                padding: 0;
              }
              .template-textarea {
                page-break-after: always;
              }
              .toc-page {
                page-break-before: always;
                page-break-after: always;
              }
              .toc-content {
                height: 100vh;
                display: flex;
                flex-direction: column;
                justify-content: flex-start;
                padding-top: 100px;
              }
              #toc {
                margin: 0 auto;
                max-width: 600px;
              }
              .toc-item {
                display: flex;
                justify-content: space-between;
                margin-bottom: 10px;
              }
              .toc-text {
                margin-right: 10px;
              }
              .toc-page-num {
                flex-shrink: 0;
              }
              .toc-item::after {
                content: "";
                border-bottom: 1px dotted black;
                flex-grow: 1;
                order: 2;
                margin: 0 5px;
                position: relative;
                top: -5px;
              }
              .toc-text {
                order: 1;
              }
              .toc-page-num {
                order: 3;
              }
              
              .TOC {
                display: none !important;
              }
      }
    </style></head><body><p class="header1"></p>${contentClone.innerHTML}</body></html>` 移除 WPS 自动添加的目录引用框 */
        contentData = contentData
          .replaceAll("<table ", `<table style="border-collapse: collapse;" `)
          .replaceAll("<td>", `<td style="border: 1px solid black;">`);

        const res = await exportDocument({
          content: contentData,
          title: this.docAttr.title,
        });

        if (res.status != 200) {
          this.$alert(res.errMsg);
          return;
        }

        let a = document.createElement("a");
        a.href = res.data.file_path;
        a.download = res.data.file_name;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
      } catch (error) {
        this.$alert("导出文档时发生错误，请稍后重试。");
      } finally {
        this.exporting = false; // 无论成功还是失败，都将导出状态设置回 false
      }
    },
    /* 前端导出word */
    async onExports() {
      if (this.exporting) return;
      this.exporting = true;

      try {
        let contentClone = this.$refs.content.cloneNode(true);
        console.log(contentClone);

        // 等待所有图片加载完成
        await this.waitForImages(contentClone);

        // 将图片转换为 Base64
        const imgPromises = Array.from(
          contentClone.querySelectorAll("img")
        ).map((img) => this.convertImageToBase64(img));

        await Promise.all(imgPromises);

        // 处理输入框
        contentClone.querySelectorAll('input[type="text"]').forEach((input) => {
          const span = document.createElement("span");
          span.textContent = input.value;
          input.parentNode.replaceChild(span, input);
        });

        // 创建HTML内容
        let contentData = `
      <!DOCTYPE html>
      <html>
        <head>
          <meta charset="UTF-8">
          <style>
            @page {
              size: A4;
              margin: 2cm;
            }
            body {
              font-family: Arial, sans-serif;
              font-size: 12pt;
              line-height: 1.5;
            }
            table {
              width: 100%;
              border-collapse: collapse;
            }
            td, th {
              border: 1px solid black;
              padding: 5px;
            }
            .template-textarea {
              page-break-after: always;
            }
            img {
              max-width: 100%;
              height: auto;
            }
          </style>
        </head>
        <body>${contentClone.innerHTML}</body>
      </html>
    `;

        // 创建文档选项
        const options = {
          margin: {
            top: "2cm",
            right: "2cm",
            bottom: "2cm",
            left: "2cm",
          },
          table: { row: { cantSplit: true } },
          footer: {
            default:
              '<p style="color: #444; font-size: 10pt">页码: {page} / {pages}</p>',
          },
          pageNumber: true,
        };

        // 创建文档
        const docxBlob = await HTMLtoDOCX(contentData, null, options);

        // 保存文件
        saveAs(docxBlob, `${this.docAttr.title}.docx`);

        this.$message.success("文档导出成功");
      } catch (error) {
        console.error("导出文档时发生错误:", error);
        this.$message.error("导出文档时发生错误，请稍后重试");
      } finally {
        this.exporting = false;
      }
    },

    // 新增方法：等待所有图片加载完成
    async waitForImages(element) {
      const images = element.getElementsByTagName("img");
      const imagePromises = Array.from(images).map((img) => {
        if (img.complete) {
          return Promise.resolve();
        } else {
          return new Promise((resolve) => {
            img.onload = img.onerror = resolve;
          });
        }
      });
      await Promise.all(imagePromises);
    },
    // 新增方法：将图片转换为Base64
    async convertImageToBase64(img) {
      if (img.src.startsWith("data:")) {
        return;
      }
      try {
        const response = await fetch(img.src);
        const blob = await response.blob();
        const base64 = await new Promise((resolve) => {
          const reader = new FileReader();
          reader.onloadend = () => resolve(reader.result);
          reader.readAsDataURL(blob);
        });
        img.src = base64;
      } catch (error) {
        console.error("Error converting image to Base64:", error);
      }
    },
    /* 终 */
    /* pdf导出 */
    async onExportPDF() {
      if (this.exporting) return;
      this.exporting = true;
      this.exportProgress = 0;

      try {
        const content = this.$refs.content;

        // 保存原始样式
        const originalStyle = {
          height: content.style.height,
          overflow: content.style.overflow,
          position: content.style.position,
        };

        // 临时修改样式以确保所有内容可见
        content.style.height = "auto";
        content.style.overflow = "visible";
        content.style.position = "absolute";

        // 等待图片加载
        await this.waitForImages(content);

        const pdf = new jsPDF("p", "pt", "a4");
        const pageHeight = pdf.internal.pageSize.getHeight();
        const pageWidth = pdf.internal.pageSize.getWidth();

        // 设置超时
        const timeout = setTimeout(() => {
          throw new Error("PDF export timed out");
        }, 60000); // 60秒超时

        await this.addContentToPDF(content, pdf, pageWidth, pageHeight);

        clearTimeout(timeout);

        pdf.save(`${this.docAttr.title || "document"}.pdf`);

        // 恢复原始样式
        Object.assign(content.style, originalStyle);

        this.$message.success("PDF导出成功");
      } catch (error) {
        console.error("Export to PDF failed:", error);
        this.$message.error(`PDF导出失败：${error.message}`);
      } finally {
        this.exporting = false;
        this.exportProgress = 0;
      }
    },

    async waitForImages(element) {
      const images = element.getElementsByTagName("img");
      const imagePromises = Array.from(images).map((img) => {
        if (img.complete) return Promise.resolve();
        return new Promise((resolve) => {
          img.onload = img.onerror = resolve;
        });
      });
      await Promise.all(imagePromises);
    },

    async addContentToPDF(element, pdf, pageWidth, pageHeight, topOffset = 0) {
      const totalHeight = element.scrollHeight;
      const canvas = await html2canvas(element, {
        scale: 2,
        useCORS: true,
        logging: false,
        windowWidth: pageWidth,
        windowHeight: pageHeight,
        y: topOffset,
        ignoreElements: (element) => {
          // 忽略使用已弃用的 -ms-high-contrast 的元素
          const style = window.getComputedStyle(element);
          return style.getPropertyValue("-ms-high-contrast") !== "none";
        },
      });

      const imgData = canvas.toDataURL("image/jpeg", 1.0);
      const imgWidth = pageWidth;
      const imgHeight = (canvas.height * imgWidth) / canvas.width;

      pdf.addImage(imgData, "JPEG", 0, 0, imgWidth, imgHeight);

      this.exportProgress = Math.min(
        100,
        Math.round(((topOffset + pageHeight) / totalHeight) * 100)
      );

      if (canvas.height > pageHeight) {
        pdf.addPage();
        await this.addContentToPDF(
          element,
          pdf,
          pageWidth,
          pageHeight,
          topOffset + pageHeight
        );
      }
    },

    /* onExport() {
      let contentData = `<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><p class="header1"></p> ${this.$refs.content.innerHTML} </body></html>`;
      contentData = contentData
        .replaceAll("<table ", `<table style="border-collapse: collapse;" `)
        .replaceAll("<td>", `<td style="border: 1px solid black;">`);
      exportDocument({ content: contentData, title: this.docAttr.title }).then(
        (res) => {
          if (res.status != 200) {
            this.$alert(res.errMsg);
            return;
          }
          let a = document.createElement("a");
          a.href = res.data.file_path; // Use the file_path from the response
          a.download = res.data.file_name; // Set the download attribute to the file_name
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
        }
      );
    }, */
  },
};
</script>

<style lang="scss">
@import "./view.scss";
</style>