<!-- 文件上传 -->
<script setup lang="ts">
import type { FileItem } from '@/stores/modules/files';
import { useFileDialog } from '@vueuse/core';
import { ElMessage } from 'element-plus';
import mammoth from 'mammoth';
import * as pdfjsLib from 'pdfjs-dist';
import * as XLSX from 'xlsx';
import Popover from '@/components/Popover/index.vue';
import SvgIcon from '@/components/SvgIcon/index.vue';
import { useFilesStore } from '@/stores/modules/files';

// 配置 PDF.js worker - 使用稳定的 CDN
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjsLib.version}/build/pdf.worker.min.mjs`;

const filesStore = useFilesStore();

// 文件大小限制 3MB
const MAX_FILE_SIZE = 3 * 1024 * 1024;

// 单个文件内容长度限制
const MAX_TEXT_FILE_LENGTH = 50000; // 文本文件最大字符数
const MAX_WORD_LENGTH = 30000; // Word 文档最大字符数
const MAX_EXCEL_ROWS = 100; // Excel 最大行数
const MAX_PDF_PAGES = 10; // PDF 最大页数

// 整个消息总长度限制（所有文件内容加起来，预估 token 安全限制）
// 272000 tokens * 0.55 安全系数 ≈ 150000 字符
const MAX_TOTAL_CONTENT_LENGTH = 150000;

const { reset, open, onChange } = useFileDialog({
  // 支持图片、文档、文本文件等
  accept: 'image/*,.txt,.log,.csv,.tsv,.md,.markdown,.json,.xml,.yaml,.yml,.toml,.ini,.conf,.config,.properties,.prop,.env,'
    + '.js,.jsx,.ts,.tsx,.vue,.html,.htm,.css,.scss,.sass,.less,.styl,'
    + '.java,.c,.cpp,.h,.hpp,.cs,.py,.rb,.go,.rs,.swift,.kt,.php,.sh,.bash,.zsh,.fish,.bat,.cmd,.ps1,'
    + '.sql,.graphql,.proto,.thrift,'
    + '.dockerfile,.gitignore,.gitattributes,.editorconfig,.npmrc,.nvmrc,'
    + '.sln,.csproj,.vbproj,.fsproj,.props,.targets,'
    + '.xlsx,.xls,.csv,.docx,.pdf',
  directory: false,
  multiple: true,
});

/**
 * 压缩图片
 * @param {File} file - 原始图片文件
 * @param {number} maxWidth - 最大宽度，默认 1024px
 * @param {number} maxHeight - 最大高度，默认 1024px
 * @param {number} quality - 压缩质量，0-1之间，默认 0.8
 * @returns {Promise<Blob>} 压缩后的图片 Blob
 */
function compressImage(file: File, maxWidth = 1024, maxHeight = 1024, quality = 0.8): Promise<Blob> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const img = new Image();
      img.onload = () => {
        const canvas = document.createElement('canvas');
        let width = img.width;
        let height = img.height;

        // 计算缩放比例
        if (width > maxWidth || height > maxHeight) {
          const ratio = Math.min(maxWidth / width, maxHeight / height);
          width = width * ratio;
          height = height * ratio;
        }

        canvas.width = width;
        canvas.height = height;

        const ctx = canvas.getContext('2d')!;
        ctx.drawImage(img, 0, 0, width, height);

        // 转换为 Blob
        canvas.toBlob(
          (blob) => {
            if (blob) {
              resolve(blob);
            }
            else {
              reject(new Error('压缩失败'));
            }
          },
          file.type,
          quality,
        );
      };
      img.onerror = reject;
      img.src = e.target?.result as string;
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

/**
 * 将 Blob 转换为 base64 格式
 * @param {Blob} blob - 要转换的 Blob 对象
 * @returns {Promise<string>} base64 编码的字符串（包含 data:xxx;base64, 前缀）
 */
function blobToBase64(blob: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      resolve(reader.result as string);
    };
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}

/**
 * 读取文本文件内容
 * @param {File} file - 文本文件
 * @returns {Promise<string>} 文件内容字符串
 */
function readTextFile(file: File): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      resolve(reader.result as string);
    };
    reader.onerror = reject;
    reader.readAsText(file, 'UTF-8');
  });
}

/**
 * 判断是否为文本文件
 * 通过 MIME 类型或文件扩展名判断
 * @param {File} file - 要判断的文件
 * @returns {boolean} 是否为文本文件
 */
function isTextFile(file: File): boolean {
  // 通过 MIME type 判断
  if (file.type.startsWith('text/')) {
    return true;
  }

  // 通过扩展名判断（更全面的列表）
  const textExtensions = [
    // 通用文本
    'txt',
    'log',
    'md',
    'markdown',
    'rtf',
    // 配置文件
    'json',
    'xml',
    'yaml',
    'yml',
    'toml',
    'ini',
    'conf',
    'config',
    'properties',
    'prop',
    'env',
    // 前端
    'js',
    'jsx',
    'ts',
    'tsx',
    'vue',
    'html',
    'htm',
    'css',
    'scss',
    'sass',
    'less',
    'styl',
    // 编程语言
    'java',
    'c',
    'cpp',
    'h',
    'hpp',
    'cs',
    'py',
    'rb',
    'go',
    'rs',
    'swift',
    'kt',
    'php',
    // 脚本
    'sh',
    'bash',
    'zsh',
    'fish',
    'bat',
    'cmd',
    'ps1',
    // 数据库/API
    'sql',
    'graphql',
    'proto',
    'thrift',
    // 版本控制/工具
    'dockerfile',
    'gitignore',
    'gitattributes',
    'editorconfig',
    'npmrc',
    'nvmrc',
    // .NET 项目文件
    'sln',
    'csproj',
    'vbproj',
    'fsproj',
    'props',
    'targets',
    // 数据文件
    'csv',
    'tsv',
  ];

  const ext = file.name.split('.').pop()?.toLowerCase();
  return ext ? textExtensions.includes(ext) : false;
}

/**
 * 解析 Excel 文件，提取前 N 行数据转为 CSV 格式
 * @param {File} file - Excel 文件 (.xlsx, .xls)
 * @returns {Promise<{content: string, totalRows: number, extractedRows: number}>}
 * - content: CSV 格式的文本内容
 * - totalRows: 文件总行数
 * - extractedRows: 实际提取的行数（受 MAX_EXCEL_ROWS 限制）
 */
async function parseExcel(file: File): Promise<{ content: string; totalRows: number; extractedRows: number }> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      try {
        const data = new Uint8Array(e.target?.result as ArrayBuffer);
        const workbook = XLSX.read(data, { type: 'array' });

        let result = '';
        let totalRows = 0;
        let extractedRows = 0;

        workbook.SheetNames.forEach((sheetName, index) => {
          const worksheet = workbook.Sheets[sheetName];

          // 获取工作表的范围
          const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
          const sheetTotalRows = range.e.r - range.s.r + 1;
          totalRows += sheetTotalRows;

          // 限制行数
          const rowsToExtract = Math.min(sheetTotalRows, MAX_EXCEL_ROWS);
          extractedRows += rowsToExtract;

          // 创建新的范围，只包含前 N 行
          const limitedRange = {
            s: { r: range.s.r, c: range.s.c },
            e: { r: range.s.r + rowsToExtract - 1, c: range.e.c },
          };

          // 提取限制范围内的数据
          const limitedData: any[][] = [];
          for (let row = limitedRange.s.r; row <= limitedRange.e.r; row++) {
            const rowData: any[] = [];
            for (let col = limitedRange.s.c; col <= limitedRange.e.c; col++) {
              const cellAddress = XLSX.utils.encode_cell({ r: row, c: col });
              const cell = worksheet[cellAddress];
              rowData.push(cell ? cell.v : '');
            }
            limitedData.push(rowData);
          }

          // 转换为 CSV
          const csvData = limitedData.map(row => row.join(',')).join('\n');

          if (workbook.SheetNames.length > 1) {
            result += `=== Sheet: ${sheetName} ===\n`;
          }
          result += csvData;
          if (index < workbook.SheetNames.length - 1) {
            result += '\n\n';
          }
        });

        resolve({ content: result, totalRows, extractedRows });
      }
      catch (error) {
        reject(error);
      }
    };
    reader.onerror = reject;
    reader.readAsArrayBuffer(file);
  });
}

/**
 * 解析 Word 文档，提取纯文本内容
 * @param {File} file - Word 文档 (.docx)
 * @returns {Promise<{content: string, totalLength: number, extracted: boolean}>}
 * - content: 提取的文本内容
 * - totalLength: 原始文本总长度
 * - extracted: 是否被截断（超过 MAX_WORD_LENGTH）
 */
async function parseWord(file: File): Promise<{ content: string; totalLength: number; extracted: boolean }> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = async (e) => {
      try {
        const arrayBuffer = e.target?.result as ArrayBuffer;
        const result = await mammoth.extractRawText({ arrayBuffer });
        const fullText = result.value;
        const totalLength = fullText.length;

        if (totalLength > MAX_WORD_LENGTH) {
          const truncated = fullText.substring(0, MAX_WORD_LENGTH);
          resolve({ content: truncated, totalLength, extracted: true });
        }
        else {
          resolve({ content: fullText, totalLength, extracted: false });
        }
      }
      catch (error) {
        reject(error);
      }
    };
    reader.onerror = reject;
    reader.readAsArrayBuffer(file);
  });
}

/**
 * 解析 PDF 文件，提取前 N 页的文本内容
 * @param {File} file - PDF 文件
 * @returns {Promise<{content: string, totalPages: number, extractedPages: number}>}
 * - content: 提取的文本内容
 * - totalPages: 文件总页数
 * - extractedPages: 实际提取的页数（受 MAX_PDF_PAGES 限制）
 */
async function parsePDF(file: File): Promise<{ content: string; totalPages: number; extractedPages: number }> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = async (e) => {
      try {
        const typedArray = new Uint8Array(e.target?.result as ArrayBuffer);
        const pdf = await pdfjsLib.getDocument(typedArray).promise;
        const totalPages = pdf.numPages;
        const pagesToExtract = Math.min(totalPages, MAX_PDF_PAGES);

        let fullText = '';
        for (let i = 1; i <= pagesToExtract; i++) {
          const page = await pdf.getPage(i);
          const textContent = await page.getTextContent();
          const pageText = textContent.items.map((item: any) => item.str).join(' ');
          fullText += `${pageText}\n`;
        }

        resolve({ content: fullText, totalPages, extractedPages: pagesToExtract });
      }
      catch (error) {
        reject(error);
      }
    };
    reader.onerror = reject;
    reader.readAsArrayBuffer(file);
  });
}

/**
 * 获取文件扩展名
 * @param {string} filename - 文件名
 * @returns {string} 小写的扩展名，无点号
 */
function getFileExtension(filename: string): string {
  return filename.split('.').pop()?.toLowerCase() || '';
}

onChange(async (files) => {
  if (!files)
    return;

  const arr = [] as FileItem[];
  let totalContentLength = 0; // 跟踪总内容长度

  // 先计算已有文件的总内容长度
  filesStore.filesList.forEach((f) => {
    if (f.fileType === 'text' && f.fileContent) {
      totalContentLength += f.fileContent.length;
    }
    // 图片 base64 也计入（虽然转 token 时不同，但也要计算）
    if (f.fileType === 'image' && f.base64) {
      // base64 转 token 比例约 1:1.5，这里保守估计
      totalContentLength += Math.floor(f.base64.length * 0.5);
    }
  });

  for (let i = 0; i < files!.length; i++) {
    const file = files![i];

    // 验证文件大小
    if (file.size > MAX_FILE_SIZE) {
      ElMessage.error(`文件 ${file.name} 超过 3MB 限制，已跳过`);
      continue;
    }

    const ext = getFileExtension(file.name);
    const isImage = file.type.startsWith('image/');
    const isExcel = ['xlsx', 'xls'].includes(ext);
    const isWord = ext === 'docx';
    const isPDF = ext === 'pdf';
    const isText = isTextFile(file);

    // 处理图片文件
    if (isImage) {
      try {
        // 多级压缩策略：逐步降低质量和分辨率
        const compressionLevels = [
          { maxWidth: 800, maxHeight: 800, quality: 0.6 },
          { maxWidth: 600, maxHeight: 600, quality: 0.5 },
          { maxWidth: 400, maxHeight: 400, quality: 0.4 },
        ];

        let compressedBlob: Blob | null = null;
        let base64 = '';
        let compressionLevel = 0;

        // 尝试不同级别的压缩
        for (const level of compressionLevels) {
          compressionLevel++;
          compressedBlob = await compressImage(file, level.maxWidth, level.maxHeight, level.quality);
          base64 = await blobToBase64(compressedBlob);

          // 检查是否满足总长度限制
          const estimatedLength = Math.floor(base64.length * 0.5);
          if (totalContentLength + estimatedLength <= MAX_TOTAL_CONTENT_LENGTH) {
            // 满足限制，使用当前压缩级别
            totalContentLength += estimatedLength;
            break;
          }

          // 如果是最后一级压缩仍然超限，则跳过
          if (compressionLevel === compressionLevels.length) {
            const fileSizeMB = (file.size / 1024 / 1024).toFixed(2);
            ElMessage.error(`${file.name} (${fileSizeMB}MB) 即使压缩后仍超过总内容限制，已跳过`);
            compressedBlob = null;
            break;
          }
        }

        // 如果压缩失败，跳过此文件
        if (!compressedBlob) {
          continue;
        }

        // 计算压缩比例
        const originalSize = (file.size / 1024).toFixed(2);
        const compressedSize = (compressedBlob.size / 1024).toFixed(2);
        console.log(`图片压缩: ${file.name} - 原始: ${originalSize}KB, 压缩后: ${compressedSize}KB (级别${compressionLevel})`);

        arr.push({
          uid: crypto.randomUUID(),
          name: file.name,
          fileSize: file.size,
          file,
          maxWidth: '200px',
          showDelIcon: true,
          imgPreview: true,
          imgVariant: 'square',
          url: base64, // 使用压缩后的 base64 作为预览地址
          isUploaded: true,
          base64,
          fileType: 'image',
        });
      }
      catch (error) {
        console.error('压缩图片失败:', error);
        ElMessage.error(`${file.name} 压缩失败`);
        continue;
      }
    }
    // 处理 Excel 文件
    else if (isExcel) {
      try {
        const result = await parseExcel(file);

        // 动态裁剪内容以适应剩余空间
        let finalContent = result.content;
        let wasTruncated = result.totalRows > MAX_EXCEL_ROWS;

        // 如果超过总内容限制，裁剪内容
        const remainingSpace = MAX_TOTAL_CONTENT_LENGTH - totalContentLength;
        if (result.content.length > remainingSpace && remainingSpace > 1000) {
          // 至少保留1000字符才有意义
          finalContent = result.content.substring(0, remainingSpace);
          wasTruncated = true;
        } else if (remainingSpace <= 1000) {
          const fileSizeKB = (file.size / 1024).toFixed(2);
          ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总内容限制，已跳过`);
          continue;
        }

        totalContentLength += finalContent.length;

        arr.push({
          uid: crypto.randomUUID(),
          name: file.name,
          fileSize: file.size,
          file,
          maxWidth: '200px',
          showDelIcon: true,
          imgPreview: false,
          isUploaded: true,
          fileContent: finalContent,
          fileType: 'text',
        });

        // 提示信息
        if (wasTruncated) {
          const fileSizeKB = (file.size / 1024).toFixed(2);
          ElMessage.warning(`${file.name} (${fileSizeKB}KB) 内容过大，已自动截取部分内容`);
        }

        console.log(`Excel 解析: ${file.name} - 大小: ${(file.size / 1024).toFixed(2)}KB, 总行数: ${result.totalRows}, 已提取: ${result.extractedRows} 行, 内容长度: ${finalContent.length} 字符`);
      }
      catch (error) {
        console.error('解析 Excel 失败:', error);
        ElMessage.error(`${file.name} 解析失败`);
        continue;
      }
    }
    // 处理 Word 文档
    else if (isWord) {
      try {
        const result = await parseWord(file);

        // 动态裁剪内容以适应剩余空间
        let finalContent = result.content;
        let wasTruncated = result.extracted;

        // 如果超过总内容限制，裁剪内容
        const remainingSpace = MAX_TOTAL_CONTENT_LENGTH - totalContentLength;
        if (result.content.length > remainingSpace && remainingSpace > 1000) {
          finalContent = result.content.substring(0, remainingSpace);
          wasTruncated = true;
        } else if (remainingSpace <= 1000) {
          const fileSizeKB = (file.size / 1024).toFixed(2);
          ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总内容限制，已跳过`);
          continue;
        }

        totalContentLength += finalContent.length;

        arr.push({
          uid: crypto.randomUUID(),
          name: file.name,
          fileSize: file.size,
          file,
          maxWidth: '200px',
          showDelIcon: true,
          imgPreview: false,
          isUploaded: true,
          fileContent: finalContent,
          fileType: 'text',
        });

        // 提示信息
        if (wasTruncated) {
          const fileSizeKB = (file.size / 1024).toFixed(2);
          ElMessage.warning(`${file.name} (${fileSizeKB}KB) 内容过大，已自动截取部分内容`);
        }

        console.log(`Word 解析: ${file.name} - 大小: ${(file.size / 1024).toFixed(2)}KB, 总长度: ${result.totalLength}, 已提取: ${finalContent.length} 字符`);
      }
      catch (error) {
        console.error('解析 Word 失败:', error);
        ElMessage.error(`${file.name} 解析失败`);
        continue;
      }
    }
    // 处理 PDF 文件
    else if (isPDF) {
      try {
        const result = await parsePDF(file);

        // 动态裁剪内容以适应剩余空间
        let finalContent = result.content;
        let wasTruncated = result.totalPages > MAX_PDF_PAGES;

        // 如果超过总内容限制，裁剪内容
        const remainingSpace = MAX_TOTAL_CONTENT_LENGTH - totalContentLength;
        if (result.content.length > remainingSpace && remainingSpace > 1000) {
          finalContent = result.content.substring(0, remainingSpace);
          wasTruncated = true;
        } else if (remainingSpace <= 1000) {
          const fileSizeKB = (file.size / 1024).toFixed(2);
          ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总内容限制，已跳过`);
          continue;
        }

        totalContentLength += finalContent.length;

        arr.push({
          uid: crypto.randomUUID(),
          name: file.name,
          fileSize: file.size,
          file,
          maxWidth: '200px',
          showDelIcon: true,
          imgPreview: false,
          isUploaded: true,
          fileContent: finalContent,
          fileType: 'text',
        });

        // 提示信息
        if (wasTruncated) {
          const fileSizeKB = (file.size / 1024).toFixed(2);
          ElMessage.warning(`${file.name} (${fileSizeKB}KB) 内容过大，已自动截取部分内容`);
        }

        console.log(`PDF 解析: ${file.name} - 大小: ${(file.size / 1024).toFixed(2)}KB, 总页数: ${result.totalPages}, 已提取: ${result.extractedPages} 页, 内容长度: ${finalContent.length} 字符`);
      }
      catch (error) {
        console.error('解析 PDF 失败:', error);
        ElMessage.error(`${file.name} 解析失败`);
        continue;
      }
    }
    // 处理文本文件
    else if (isText) {
      try {
        // 读取文本文件内容
        const content = await readTextFile(file);

        // 限制单个文本文件长度
        let finalContent = content;
        let truncated = false;
        if (content.length > MAX_TEXT_FILE_LENGTH) {
          finalContent = content.substring(0, MAX_TEXT_FILE_LENGTH);
          truncated = true;
        }

        // 动态裁剪内容以适应剩余空间
        const remainingSpace = MAX_TOTAL_CONTENT_LENGTH - totalContentLength;
        if (finalContent.length > remainingSpace && remainingSpace > 1000) {
          finalContent = finalContent.substring(0, remainingSpace);
          truncated = true;
        } else if (remainingSpace <= 1000) {
          const fileSizeKB = (file.size / 1024).toFixed(2);
          ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总内容限制，已跳过`);
          continue;
        }

        totalContentLength += finalContent.length;

        arr.push({
          uid: crypto.randomUUID(),
          name: file.name,
          fileSize: file.size,
          file,
          maxWidth: '200px',
          showDelIcon: true,
          imgPreview: false,
          isUploaded: true,
          fileContent: finalContent,
          fileType: 'text',
        });

        // 提示信息
        if (truncated) {
          const fileSizeKB = (file.size / 1024).toFixed(2);
          ElMessage.warning(`${file.name} (${fileSizeKB}KB) 内容过大，已自动截取部分内容`);
        }

        console.log(`文本文件读取: ${file.name} - 大小: ${(file.size / 1024).toFixed(2)}KB, 内容长度: ${content.length} 字符`);
      }
      catch (error) {
        console.error('读取文件失败:', error);
        ElMessage.error(`${file.name} 读取失败`);
        continue;
      }
    }
    // 不支持的文件类型
    else {
      ElMessage.warning(`${file.name} 不是支持的文件类型`);
      continue;
    }
  }

  if (arr.length > 0) {
    filesStore.setFilesList([...filesStore.filesList, ...arr]);
    ElMessage.success(`已添加 ${arr.length} 个文件`);
  }

  // 重置文件选择器
  nextTick(() => reset());
});

/**
 * 打开文件选择对话框
 */
function handleUploadFiles() {
  open();
}
</script>

<template>
  <div class="files-select">
    <!-- 直接点击上传，添加 tooltip 提示 -->
    <el-tooltip
      content="上传文件或图片（支持 Excel、Word、PDF、代码文件等，最大3MB）"
      placement="top"
    >
      <div
        class="flex items-center gap-4px p-10px rounded-10px cursor-pointer font-size-14px border-1px border-[rgba(0,0,0,0.08)] border-solid hover:bg-[rgba(0,0,0,.04)]"
        @click="handleUploadFiles"
      >
        <el-icon>
          <Paperclip />
        </el-icon>
      </div>
    </el-tooltip>
  </div>
</template>

<style scoped lang="scss"></style>
