const path = require("path");
const fs = require("fs");
const http = require("http");
const https = require("https");
const url = require("url");

/* ===================== 基础工具函数 ===================== */

/**
 * 从URL提取文件名
 */
function getFileName(urlStr) {
  const formatUrl = urlStr.replace(/\\/g, "/");
  return path.basename(formatUrl);
}

/**
 * 判断是否为绝对URL
 */
function isAbsoluteUrl(urlStr) {
  return /^https?:\/\//i.test(urlStr);
}

/**
 * 补全相对路径为完整URL
 */
function resolveUrl(urlStr, baseUrl) {
  if (isAbsoluteUrl(urlStr)) return urlStr;
  return `${baseUrl.replace(/\/$/, "")}/${urlStr.replace(/^\//, "")}`;
}

/**
 * 递归创建目录
 */
async function ensureDir(dirPath) {
  await fs.promises.mkdir(dirPath, { recursive: true });
}

/**
 * 判断文件是否存在
 */
async function isFileExists(filePath) {
  try {
    await fs.promises.access(filePath, fs.constants.F_OK);
    return true;
  } catch (e) {
    return false;
  }
}

/**
 * 并发限制器
 */
function pLimit(concurrency) {
  let activeCount = 0;
  const queue = [];
  const next = () => {
    if (queue.length > 0 && activeCount < concurrency) {
      activeCount++;
      const task = queue.shift();
      task().finally(() => {
        activeCount--;
        next();
      });
    }
  };
  return fn => {
    return new Promise((resolve, reject) => {
      queue.push(() =>
        fn()
          .then(resolve)
          .catch(reject)
      );
      next();
    });
  };
}

/* ===================== 核心下载能力 ===================== */

/**
 * 单文件流式下载（支持重定向、超时、自动清理残文件）
 * @param {string} fileUrl 完整下载链接
 * @param {string} filePath 本地保存路径
 * @param {number} timeout 超时时间ms
 * @param {number} redirectCount 已重定向次数
 */
function streamDownload(fileUrl, filePath, timeout = 30000, redirectCount = 0) {
  return new Promise((resolve, reject) => {
    try {
      const requestModule = fileUrl.startsWith("https") ? https : http;
      const req = requestModule.get(fileUrl, { timeout }, res => {
        // 重定向处理，最多3次防死循环
        if ([301, 302, 307, 308].includes(res.statusCode)) {
          if (redirectCount >= 3) {
            res.resume();
            reject(new Error("重定向次数超过3次"));
            return;
          }
          const redirectUrl = url.resolve(fileUrl, res.headers.location);
          streamDownload(redirectUrl, filePath, timeout, redirectCount + 1)
            .then(resolve)
            .catch(reject);
          return;
        }

        if (res.statusCode !== 200) {
          res.resume();
          reject(new Error(`HTTP状态码异常: ${res.statusCode}`));
          return;
        }

        let writeStream;
        try {
          writeStream = fs.createWriteStream(filePath);
        } catch (err) {
          res.resume();
          reject(new Error(`路径创建失败：${err.message}`));
          return;
        }

        res.pipe(writeStream);

        writeStream.on("finish", () => {
          writeStream.close(() => resolve(filePath));
        });

        writeStream.on("error", err => {
          writeStream.destroy();
          fs.promises.unlink(filePath).catch(() => {});
          reject(err);
        });

        res.on("error", err => {
          writeStream.destroy();
          fs.promises.unlink(filePath).catch(() => {});
          reject(new Error(`网络响应异常: ${err.message}`));
        });
      });

      req.on("error", err => reject(new Error(`网络请求失败: ${err.message}`)));
      req.on("timeout", () => {
        req.destroy();
        reject(new Error("下载超时"));
      });
    } catch (syncErr) {
      reject(new Error(`请求初始化失败: ${syncErr.message}`));
    }
  });
}

/**
 * 带失败重试的单文件下载
 */
async function downloadWithRetry(
  fileUrl,
  filePath,
  retryTimes = 0,
  timeout = 30000
) {
  const attempt = async remain => {
    try {
      return await streamDownload(fileUrl, filePath, timeout);
    } catch (err) {
      if (remain > 0) {
        console.log(`[下载重试] 剩余 ${remain} 次：${path.basename(filePath)}`);
        return attempt(remain - 1);
      }
      throw err;
    }
  };
  return attempt(retryTimes);
}

/* ===================== 批量下载封装 ===================== */

/**
 * 批量下载（URL数组版本）
 * @param {string[]} urlList URL数组
 * @param {string} targetDir 目标目录
 * @param {Object} options 配置
 * @returns {Promise<{success: string[], failed: Array<{url:string, error:string}>}>}
 */
async function batchDownloadByList(urlList, targetDir, options = {}) {
  const {
    concurrency = 5,
    timeout = 30000,
    retryTimes = 0,
    baseUrl = "",
    skipExists = true,
    fileNameFormatter = null // 新增：自定义文件名回调
  } = options;

  const success = [];
  const failed = [];

  // 1. 创建目标目录
  try {
    await ensureDir(targetDir);
  } catch (dirErr) {
    const errMsg = `目标目录创建失败：${dirErr.message}`;
    console.error(`[批量下载][路径错误] ${errMsg} | 目录: ${targetDir}`);
    return {
      success: [],
      failed: [{ url: "", error: errMsg }]
    };
  }

  // 2. 并发执行下载
  const limit = pLimit(concurrency);
  const tasks = urlList.map(urlStr =>
    limit(async () => {
      const fullUrl = baseUrl ? resolveUrl(urlStr, baseUrl) : urlStr;
      const fileName = fileNameFormatter
        ? fileNameFormatter(urlStr, baseUrl)
        : getFileName(urlStr);
      const savePath = path.join(targetDir, fileName);

      try {
        // 已存在则跳过
        if (skipExists && (await isFileExists(savePath))) {
          console.log(`[批量下载][跳过] 文件已存在: ${fileName}`);
          success.push(savePath);
          return;
        }

        await downloadWithRetry(fullUrl, savePath, retryTimes, timeout);
        console.log(`[批量下载][成功] ${fileName}`);
        success.push(savePath);
      } catch (err) {
        // 识别路径类错误
        const isPathError =
          ["ENOENT", "EINVAL", "ENAMETOOLONG", "EPERM", "EACCES"].includes(
            err.code
          ) || /路径|path|file/i.test(err.message);
        const errorType = isPathError ? "路径错误" : "下载失败";

        console.error(
          `[批量下载][${errorType}] 跳过文件: ${fileName} | 链接: ${fullUrl} | 原因: ${err.message}`
        );
        failed.push({ url: fullUrl, error: err.message });
      }
    })
  );

  await Promise.all(tasks);
  console.log(`批量下载完成，成功: ${success.length}，失败: ${failed.length}`);
  return { success, failed };
}

/**
 * 批量下载（逗号分隔URL字符串版本）
 * @param {string} urlStr 逗号分隔的URL字符串
 * @param {string} targetDir 目标目录
 * @param {Object} options 配置
 */
async function batchDownloadByStr(urlStr, targetDir, options = {}) {
  // 参数校验
  if (!urlStr || typeof urlStr !== "string") {
    console.error("[批量下载][参数错误] 传入的URL字符串为空或格式无效");
    return { success: [], failed: [{ url: "", error: "URL字符串无效" }] };
  }

  // 拆分URL
  const urlList = urlStr
    .split(",")
    .map(u => u.trim())
    .filter(u => u);

  if (urlList.length === 0) {
    console.warn("[批量下载][无有效链接] 拆分后无可用URL，直接跳过");
    return { success: [], failed: [] };
  }

  return batchDownloadByList(urlList, targetDir, options);
}

module.exports = {
  // 工具函数
  getFileName,
  isAbsoluteUrl,
  resolveUrl,
  ensureDir,
  isFileExists,
  pLimit,
  // 核心下载
  streamDownload,
  downloadWithRetry,
  // 批量下载
  batchDownloadByList,
  batchDownloadByStr
};
