const fs = require("fs");
const path = require("path");
const http = require("http");
const https = require("https");
const { URL } = require("url");
const { getDTFSetting } = require("@/server/utils/store");

export function createBatchDownloadQueue(options = {}) {
  // ========== 1. 配置项 ==========
  const config = Object.assign(
    {
      concurrency: 3,
      retryTimes: 2,
      onSuccess: () => {}, // 单文件下载成功
      onError: () => {}, // 单文件下载失败
      onBatchInvalid: () => {}, // 批次校验失败回调（返回异常批次列表+原因）
      onIdle: () => {} // 全部任务完成
    },
    options
  );

  // ========== 2. 内部状态 ==========
  let pendingQueue = [];
  let runningCount = 0;
  const successSet = new Set();
  const runningSet = new Set();
  let isDestroyed = false;

  // ========== 3. 通用工具方法 ==========
  const getRequestModule = url => {
    try {
      return new URL(url).protocol === "https:" ? https : http;
    } catch (e) {
      return https;
    }
  };

  const isPngUrl = url => {
    try {
      const urlObj = new URL(url);
      return urlObj.pathname.toLowerCase().endsWith(".png");
    } catch (e) {
      return false;
    }
  };

  const getPngFileName = url => {
    try {
      const urlObj = new URL(url);
      const name = path.basename(urlObj.pathname);
      return name.toLowerCase().endsWith(".png")
        ? name
        : `${name || `img_${Date.now()}`}.png`;
    } catch (e) {
      return `img_${Date.now()}.png`;
    }
  };

  const fileExists = targetPath => fs.existsSync(targetPath);
  const cleanBadFile = targetPath =>
    fileExists(targetPath) && fs.unlinkSync(targetPath);

  // ========== 4. 流式下载核心 ==========
  const streamDownload = (url, targetPath) =>
    new Promise((resolve, reject) => {
      const requestModule = getRequestModule(url);
      const writeStream = fs.createWriteStream(targetPath);
      const request = requestModule.get(url, res => {
        if ([301, 302, 307, 308].includes(res.statusCode)) {
          writeStream.close();
          streamDownload(res.headers.location, targetPath)
            .then(resolve)
            .catch(reject);
          return;
        }
        if (res.statusCode !== 200) {
          writeStream.close();
          cleanBadFile(targetPath);
          reject(new Error(`响应状态码 ${res.statusCode}`));
          return;
        }
        res.pipe(writeStream);
      });

      writeStream.on("finish", () => {
        writeStream.close();
        resolve();
      });
      writeStream.on("error", err => {
        writeStream.close();
        cleanBadFile(targetPath);
        reject(err);
      });
      request.on("error", err => {
        writeStream.close();
        cleanBadFile(targetPath);
        reject(err);
      });
      request.setTimeout(30000, () => {
        request.destroy();
        writeStream.close();
        cleanBadFile(targetPath);
        reject(new Error("下载超时"));
      });
    });

  // ========== 5. 队列消费逻辑 ==========
  const consume = () => {
    if (isDestroyed) return;
    if (runningCount >= config.concurrency || pendingQueue.length === 0) {
      runningCount === 0 && config.onIdle();
      return;
    }

    const task = pendingQueue.shift();
    runningCount++;
    (async () => {
      try {
        const targetDir = path.dirname(task.targetPath);
        if (!fileExists(targetDir)) {
          fs.mkdirSync(targetDir, { recursive: true });
        }
        await streamDownload(task.downloadUrl, task.targetPath);
        successSet.add(task.targetPath);
        config.onSuccess(task);
      } catch (err) {
        if (task.remainRetry > 0) {
          task.remainRetry--;
          pendingQueue.unshift(task);
        } else {
          config.onError(task, err);
        }
      } finally {
        runningCount--;
        runningSet.delete(task.targetPath);
        consume();
      }
    })();
  };

  // ========== 6. 对外暴露方法 ==========
  return {
    addBatchList(list = []) {
      if (isDestroyed || !Array.isArray(list)) return;

      // 第一步：筛选 layoutStatus = 3 或 5 的批次
      const targetBatch = list.filter(item =>
        [3, 5].includes(item.layoutStatus)
      );
      const invalidBatchList = []; // 收集异常批次
      const fileTasks = [];

      // 第二步：逐个校验批次URL合法性
      targetBatch.forEach(batch => {
        const batchNo = batch.batchArrangeNum || "未知批次";

        // 校验1：URL 不是字符串类型
        if (typeof batch.url !== "string") {
          invalidBatchList.push({
            batchArrangeNum: batchNo,
            reason: "下载链接格式错误，非字符串类型"
          });
          return;
        }

        // 校验2：URL 为空字符串/纯空格
        if (batch.url.trim() === "") {
          invalidBatchList.push({
            batchArrangeNum: batchNo,
            reason: "下载链接为空"
          });
          return;
        }

        // 第三步：拆分多URL，过滤空值，校验PNG格式
        const urlList = batch.url
          .split(",")
          .map(u => u.trim())
          .filter(Boolean);
        const validPngUrls = urlList.filter(url => isPngUrl(url));

        // 校验3：拆分后无有效PNG链接
        if (validPngUrls.length === 0) {
          invalidBatchList.push({
            batchArrangeNum: batchNo,
            reason: "无有效PNG格式下载链接"
          });
          return;
        }

        // 第四步：生成合法下载任务，去重过滤
        const dir = path.join(
          getDTFSetting().downloadDir,
          `批次号：${batchNo}`,
          "自动排版素材"
        );
        validPngUrls.forEach(url => {
          const fileName = getPngFileName(url);
          const targetPath = path.join(dir, fileName);
          if (
            !fileExists(targetPath) &&
            !successSet.has(targetPath) &&
            !runningSet.has(targetPath)
          ) {
            fileTasks.push({
              batchArrangeNum: batchNo,
              downloadUrl: `https://factory.jomalls.com${url}`,
              targetPath,
              fileName,
              remainRetry: config.retryTimes
            });
          }
        });
      });

      // 有异常批次，通过回调抛出
      if (invalidBatchList.length > 0) {
        config.onBatchInvalid(invalidBatchList);
      }

      // 启动下载队列
      if (fileTasks.length > 0) {
        fileTasks.forEach(task => {
          runningSet.add(task.targetPath);
          pendingQueue.push(task);
        });
        consume();
      }
    },

    get isRunning() {
      return runningCount > 0 || pendingQueue.length > 0;
    },

    destroy() {
      isDestroyed = true;
      pendingQueue = [];
      runningCount = 0;
      successSet.clear();
      runningSet.clear();
    }
  };
}

/**
 * 批量下载PNG图片到指定文件夹
 * @param {string[]} urls - PNG图片链接数组
 * @param {string} targetDir - 目标文件夹绝对路径
 * @param {Object} [options] - 可选配置
 * @param {number} [options.concurrency=2] - 同时下载数量
 * @param {number} [options.timeout=30000] - 单文件超时时间(ms)
 * @param {number} [options.retryTimes=1] - 单文件失败重试次数
 * @returns {Promise<{success: string[], failed: {url: string, error: string}[]}>}
 */
export function downloadPngList(urls, targetDir, options = {}) {
  const config = {
    concurrency: 2,
    timeout: 30000,
    retryTimes: 1,
    ...options
  };

  return new Promise(resolve => {
    // 参数合法性校验
    if (!Array.isArray(urls) || urls.length === 0) {
      resolve({
        success: [],
        failed: [{ url: "", error: "下载链接数组为空" }]
      });
      return;
    }
    console.log(
      `开始批量下载，共 ${urls.length} 个链接，目标目录：${targetDir}`
    );
    // 自动创建目标目录（多级递归创建，不需要可注释掉）
    if (!fs.existsSync(targetDir)) {
      fs.mkdirSync(targetDir, { recursive: true });
    }

    const successList = []; // 下载成功的文件路径
    const failedList = []; // 下载失败的链接+原因
    let currentIndex = 0; // 当前处理到第几个链接
    let runningCount = 0; // 当前正在下载的数量
    const total = urls.length;

    // 从URL中提取PNG文件名，无后缀自动补.png
    const getPngFileName = url => {
      try {
        const urlObj = new URL(url);
        const name = path.basename(urlObj.pathname);
        return name.toLowerCase().endsWith(".png") ? name : `${name}.png`;
      } catch (e) {
        return `img_${Date.now()}_${Math.random()
          .toString(36)
          .slice(2, 6)}.png`;
      }
    };

    // 单文件流式下载核心
    const streamDownload = (url, filePath) => {
      return new Promise((resolve, reject) => {
        const requestModule = url.startsWith("https:") ? https : http;
        const writeStream = fs.createWriteStream(filePath);
        const request = requestModule.get(url, res => {
          // 自动处理301/302/307/308重定向
          if ([301, 302, 307, 308].includes(res.statusCode)) {
            writeStream.close();
            streamDownload(res.headers.location, filePath)
              .then(resolve)
              .catch(reject);
            return;
          }
          if (res.statusCode !== 200) {
            writeStream.close();
            fs.existsSync(filePath) && fs.unlinkSync(filePath);
            reject(new Error(`HTTP状态码 ${res.statusCode}`));
            return;
          }
          res.pipe(writeStream);
        });

        writeStream.on("finish", () => {
          console.log("downloadPngList---finish");

          writeStream.close();
          resolve();
        });
        writeStream.on("error", err => {
          writeStream.close();
          fs.existsSync(filePath) && fs.unlinkSync(filePath);
          reject(err);
        });
        request.on("error", err => {
          writeStream.close();
          fs.existsSync(filePath) && fs.unlinkSync(filePath);
          reject(err);
        });
        // 超时兜底
        request.setTimeout(config.timeout, () => {
          request.destroy();
          writeStream.close();
          fs.existsSync(filePath) && fs.unlinkSync(filePath);
          reject(new Error("下载超时"));
        });
      });
    };

    // 带重试的单任务执行
    const runTask = async url => {
      const fileName = getPngFileName(url);
      const filePath = path.join(targetDir, fileName);

      // 本地文件已存在，直接跳过
      if (fs.existsSync(filePath)) {
        successList.push(filePath);
        return;
      }
      console.log(`开始下载：${fileName}`);
      let remainRetry = config.retryTimes;

      try {
        await streamDownload(url, filePath);
        console.log(`下载成功：${fileName}`);
        successList.push(filePath);
      } catch (err) {
        if (remainRetry > 0) {
          remainRetry--;
        } else {
          failedList.push({ url, error: err.message });
        }
      }
    };

    // 并发调度器
    const next = () => {
      // 并发数未满且还有未处理任务，继续启动
      while (runningCount < config.concurrency && currentIndex < total) {
        runningCount++;
        const url = urls[currentIndex].trim();
        currentIndex++;

        runTask(url).finally(() => {
          runningCount--;
          next();
        });
      }

      // 所有任务执行完毕，返回结果
      if (runningCount === 0 && currentIndex >= total) {
        console.log(
          `全部任务执行完成，成功：${successList.length} 个，失败：${failedList.length} 个`
        );
        resolve({ success: successList, failed: failedList });
      }
    };

    next();
  });
}
