import path from "path";
import _ from "lodash";
import mime from "mime";
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";
import { SocksProxyAgent } from "socks-proxy-agent";

import APIException from "@/lib/exceptions/APIException.ts";
import EX from "@/api/consts/exceptions.ts";
import logger from "@/lib/logger.ts";
import util from "@/lib/util.ts";
import { JimengErrorHandler, JimengErrorResponse } from "@/lib/error-handler.ts";
import { BASE_URL_DREAMINA_US, BASE_URL_DREAMINA_HK, DA_VERSION, WEB_VERSION } from "@/api/consts/dreamina.ts";

import {
  BASE_URL_CN,
  BASE_URL_US_COMMERCE,
  BASE_URL_HK_COMMERCE,
  BASE_URL_HK,
  DEFAULT_ASSISTANT_ID_CN,
  DEFAULT_ASSISTANT_ID_US,
  DEFAULT_ASSISTANT_ID_HK,
  DEFAULT_ASSISTANT_ID_JP,
  DEFAULT_ASSISTANT_ID_SG,
  PLATFORM_CODE,
  REGION_CN,
  REGION_US,
  REGION_HK,
  REGION_JP,
  REGION_SG,
  VERSION_CODE,
  RETRY_CONFIG
} from "@/api/consts/common.ts";

// 模型名称
const MODEL_NAME = "jimeng";
// 设备ID
const DEVICE_ID = Math.random() * 999999999999999999 + 7000000000000000000;
// WebID
const WEB_ID = Math.random() * 999999999999999999 + 7000000000000000000;
// 用户ID（32位hex，无横线）
const USER_ID = util.uuid(false);
// 伪装headers
const FAKE_HEADERS = {
  Accept: "application/json, text/plain, */*",
  "Accept-Encoding": "gzip, deflate, br, zstd",
  "Accept-language": "zh-CN,zh;q=0.9",
  "Cache-control": "no-cache",
  Appvr: VERSION_CODE,
  Pragma: "no-cache",
  Priority: "u=1, i",
  Pf: PLATFORM_CODE,
  "Sec-Ch-Ua": '"Google Chrome";v="142", "Chromium";v="142", "Not_A Brand";v="99"',
  "Sec-Ch-Ua-Mobile": "?0",
  "Sec-Ch-Ua-Platform": '"Windows"',
  "Sec-Fetch-Dest": "empty",
  "Sec-Fetch-Mode": "cors",
  "Sec-Fetch-Site": "same-origin",
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
};
// 文件最大大小
const FILE_MAX_SIZE = 100 * 1024 * 1024;

/**
 * 获取缓存中的access_token
 *
 * 目前jimeng的access_token是固定的，暂无刷新功能
 *
 * @param refreshToken 用于刷新access_token的refresh_token
 */
export async function acquireToken(refreshToken: string): Promise<string> {
  return refreshToken;
}

/**
 * 解析 token 中的地区信息
 *
 * @param refreshToken 刷新令牌
 * @returns 地区信息对象
 */
export interface RegionInfo {
  isUS: boolean;
  isHK: boolean;
  isJP: boolean;
  isSG: boolean;
  isInternational: boolean;
  isCN: boolean;
}

export interface TokenWithProxy {
  token: string;
  proxyUrl: string | null;
}

export function parseProxyFromToken(rawToken: string): TokenWithProxy {
  const tokenValue = rawToken.trim();
  const proxyPattern = /^(https?|socks(?:4|5)?):\/\//i;
  if (!proxyPattern.test(tokenValue)) return { token: tokenValue, proxyUrl: null };

  const lastAtIndex = tokenValue.lastIndexOf("@");
  if (lastAtIndex <= 0 || lastAtIndex === tokenValue.length - 1)
    return { token: tokenValue, proxyUrl: null };

  const proxyUrl = tokenValue.slice(0, lastAtIndex);
  const token = tokenValue.slice(lastAtIndex + 1);
  if (!proxyUrl || !token) return { token: tokenValue, proxyUrl: null };

  return { token, proxyUrl };
}

export function parseRegionFromToken(refreshToken: string): RegionInfo {
  const { token: parsedToken } = parseProxyFromToken(refreshToken);
  const token = parsedToken.toLowerCase();
  const isUS = token.startsWith('us-');
  const isHK = token.startsWith('hk-');
  const isJP = token.startsWith('jp-');
  const isSG = token.startsWith('sg-');
  const isInternational = isUS || isHK || isJP || isSG;

  return {
    isUS,
    isHK,
    isJP,
    isSG,
    isInternational,
    isCN: !isInternational
  };
}

/**
 * 根据地区获取 Referer
 *
 * @param refreshToken 刷新令牌
 * @param cnPath 国内站路径
 * @returns Referer URL
 */
export function getRefererByRegion(refreshToken: string, cnPath: string): string {
  const { isInternational } = parseRegionFromToken(refreshToken);
  return isInternational
    ? "https://dreamina.capcut.com/"
    : `https://jimeng.jianying.com${cnPath}`;
}

/**
 * 根据地区获取 AssistantID
 *
 * @param regionInfo 地区信息
 * @returns AssistantID
 */
export function getAssistantId(regionInfo: RegionInfo): number {
  if (regionInfo.isUS) return DEFAULT_ASSISTANT_ID_US;
  if (regionInfo.isJP) return DEFAULT_ASSISTANT_ID_JP;
  if (regionInfo.isSG) return DEFAULT_ASSISTANT_ID_SG;
  if (regionInfo.isHK) return DEFAULT_ASSISTANT_ID_HK;
  return DEFAULT_ASSISTANT_ID_CN;
}

/**
 * 生成cookie
 */
export function generateCookie(refreshToken: string) {
  const { token: tokenWithRegion } = parseProxyFromToken(refreshToken);
  const { isUS, isHK, isJP, isSG } = parseRegionFromToken(tokenWithRegion);
  const token = (isUS || isHK || isJP || isSG)
    ? tokenWithRegion.substring(3)
    : tokenWithRegion;

  return [
    `_tea_web_id=${WEB_ID}`,
    `is_staff_user=false`,
    `sid_guard=${token}%7C${util.unixTimestamp()}%7C5184000%7CMon%2C+03-Feb-2025+08%3A17%3A09+GMT`,
    `uid_tt=${USER_ID}`,
    `uid_tt_ss=${USER_ID}`,
    `sid_tt=${token}`,
    `sessionid=${token}`,
    `sessionid_ss=${token}`,
  ].join("; ");
}

/**
 * 获取积分信息
 *
 * @param refreshToken 用于刷新access_token的refresh_token
 */
export async function getCredit(refreshToken: string) {
  const referer = getRefererByRegion(refreshToken, "/ai-tool/image/generate");

  const {
    credit: { gift_credit, purchase_credit, vip_credit }
  } = await request("POST", "/commerce/v1/benefits/user_credit", refreshToken, {
    data: {},
    headers: {
      Referer: referer,
    },
    noDefaultParams: true
  });
  logger.info(`\n积分信息: \n赠送积分: ${gift_credit}, 购买积分: ${purchase_credit}, VIP积分: ${vip_credit}`);
  return {
    giftCredit: gift_credit,
    purchaseCredit: purchase_credit,
    vipCredit: vip_credit,
    totalCredit: gift_credit + purchase_credit + vip_credit
  }
}

/**
 * 接收今日积分（仅在积分为 0 时调用）
 *
 * @param refreshToken 用于刷新access_token的refresh_token
 */
export async function receiveCredit(refreshToken: string) {
  logger.info("正在尝试收取今日积分...")
  const referer = getRefererByRegion(refreshToken, "/ai-tool/home");

  const { receive_quota } = await request("POST", "/commerce/v1/benefits/credit_receive", refreshToken, {
    data: {
      time_zone: "Asia/Shanghai"
    },
    headers: {
      Referer: referer
    }
  });
  logger.info(`今日${receive_quota}积分收取成功`);
  return receive_quota;
}

/**
 * 请求jimeng
 *
 * @param method 请求方法
 * @param uri 请求路径
 * @param params 请求参数
 * @param headers 请求头
 */
export async function request(
  method: string,
  uri: string,
  refreshToken: string,
  options: AxiosRequestConfig & { noDefaultParams?: boolean } = {}
) {
  const { token: tokenWithRegion, proxyUrl } = parseProxyFromToken(refreshToken);
  const regionInfo = parseRegionFromToken(tokenWithRegion);
  const { isUS, isHK, isJP, isSG } = regionInfo;
  await acquireToken(regionInfo.isInternational ? tokenWithRegion.substring(3) : tokenWithRegion);
  const deviceTime = util.unixTimestamp();
  const sign = util.md5(
    `9e2c|${uri.slice(-7)}|${PLATFORM_CODE}|${VERSION_CODE}|${deviceTime}||11ac`
  );

  let baseUrl: string;
  let aid: number;
  let region: string;

  if (isUS) {
    if (uri.startsWith("/commerce/")) {
      baseUrl = BASE_URL_US_COMMERCE;
    } else {
      baseUrl = BASE_URL_DREAMINA_US;
    }
    aid = DEFAULT_ASSISTANT_ID_US;
    region = REGION_US;
  } else if (isHK || isJP || isSG) {
    // HK, JP and SG regions use the same SG base URL
    if (uri.startsWith("/commerce/")) {
      baseUrl = BASE_URL_HK_COMMERCE;
    } else {
      baseUrl = BASE_URL_DREAMINA_HK;
    }
    if (isJP) {
      aid = DEFAULT_ASSISTANT_ID_JP;
      region = REGION_JP;
    } else if (isSG) {
      aid = DEFAULT_ASSISTANT_ID_SG;
      region = REGION_SG;
    } else {
      aid = DEFAULT_ASSISTANT_ID_HK;
      region = REGION_HK;
    }
  } else {
    // CN region
    baseUrl = BASE_URL_CN;
    aid = DEFAULT_ASSISTANT_ID_CN;
    region = REGION_CN;
  }

  const origin = new URL(baseUrl).origin;

  const fullUrl = `${baseUrl}${uri}`;
  const requestParams = options.noDefaultParams ? (options.params || {}) : {
    aid: aid,
    device_platform: "web",
    region: region,
    ...(isUS || isHK || isJP || isSG ? {} : { webId: WEB_ID }),
    da_version: DA_VERSION,
    os: "windows",
    web_component_open_flag: 1,
    web_version: WEB_VERSION,
    aigc_features: "app_lip_sync",
    ...(options.params || {}),
  };

  const headers = {
    ...FAKE_HEADERS,
    Origin: origin,
    Referer: origin,
    "App-Sdk-Version": "48.0.0",
    Appid: aid,
    Cookie: generateCookie(tokenWithRegion),
    "Device-Time": deviceTime,
    Lan: isUS ? "en" : isJP ? "ja" : (isHK || isSG) ? "en" : "zh-Hans",
    Loc: isUS ? "us" : isJP ? "jp" : isHK ? "hk" : isSG ? "sg" : "cn",
    Sign: sign,
    "Sign-Ver": "1",
    Tdid: "",
    ...(options.headers || {}),
  };

  logger.info(`发送请求: ${method.toUpperCase()} ${fullUrl}`);
  if (proxyUrl) {
    const maskedProxyUrl = proxyUrl.replace(/\/\/([^@/]+)@/i, "//***@");
    logger.info(`使用代理: ${maskedProxyUrl}`);
  }
  logger.info(`请求参数: ${JSON.stringify(requestParams)}`);
  logger.info(`请求数据: ${JSON.stringify(options.data || {})}`);

  const proxyAgent = proxyUrl
    ? (proxyUrl.toLowerCase().startsWith("socks")
      ? new SocksProxyAgent(proxyUrl)
      : new HttpsProxyAgent(proxyUrl))
    : undefined;

  // 添加重试逻辑
  let retries = 0;
  const maxRetries = RETRY_CONFIG.MAX_RETRY_COUNT;
  let lastError = null;

  while (retries <= maxRetries) {
    try {
      if (retries > 0) {
        logger.info(`第 ${retries} 次重试请求: ${method.toUpperCase()} ${fullUrl}`);
        // 重试前等待一段时间
        await new Promise(resolve => setTimeout(resolve, RETRY_CONFIG.RETRY_DELAY));
      }

      const response = await axios.request({
        method,
        url: fullUrl,
        params: requestParams,
        headers: headers,
        timeout: 45000, // 增加超时时间到45秒
        validateStatus: () => true, // 允许任何状态码
        ..._.omit(options, "params", "headers", "noDefaultParams"),
        ...(proxyAgent ? { httpAgent: proxyAgent, httpsAgent: proxyAgent, proxy: false } : {}),
      });

      // 记录响应状态和头信息
      logger.info(`响应状态: ${response.status} ${response.statusText}`);

      // 流式响应直接返回response
      if (options.responseType == "stream") return response;

      // 记录响应数据摘要
      const responseDataSummary = JSON.stringify(response.data).substring(0, 500) +
        (JSON.stringify(response.data).length > 500 ? "..." : "");
      //const responseDataSummary = JSON.stringify(response.data)
      logger.info(`响应数据摘要: ${responseDataSummary}`);

      // 检查HTTP状态码
      if (response.status >= 400) {
        logger.warn(`HTTP错误: ${response.status} ${response.statusText}`);
        if (retries < maxRetries) {
          retries++;
          continue;
        }
      }

      return checkResult(response);
    }
    catch (error) {
      lastError = error;
      logger.error(`请求失败 (尝试 ${retries + 1}/${maxRetries + 1}): ${error.message}`);

      // 如果是网络错误或超时，尝试重试
      // 包含常见的网络错误：ECONNRESET（连接重置）、ENOTFOUND（DNS解析失败）、
      // ECONNREFUSED（连接被拒绝）、EAI_AGAIN（DNS临时失败）、EPIPE（管道破裂）
      const retryableErrorCodes = [
        'ECONNABORTED', 'ETIMEDOUT', 'ECONNRESET', 'ENOTFOUND',
        'ECONNREFUSED', 'EAI_AGAIN', 'EPIPE', 'ENETUNREACH', 'EHOSTUNREACH'
      ];
      const isRetryableError = retryableErrorCodes.includes(error.code) ||
        error.message.includes('timeout') ||
        error.message.includes('network') ||
        error.message.includes('ECONNRESET') ||
        error.message.includes('socket hang up') ||
        error.message.includes('Proxy connection');

      if (isRetryableError && retries < maxRetries) {
        retries++;
        continue;
      }

      // 其他错误直接抛出
      break;
    }
  }

  // 所有重试都失败了，抛出最后一个错误
  if (lastError) {
    logger.error(`请求失败，已重试 ${retries} 次: ${lastError.message}`);
    if (lastError.response) {
      logger.error(`响应状态: ${lastError.response.status}`);
      logger.error(`响应数据: ${JSON.stringify(lastError.response.data)}`);
    }
    throw lastError;
  } else {
    // 这种情况理论上不应该发生，但为了安全起见
    const error = new Error(`请求失败，已重试 ${retries} 次，但没有具体错误信息`);
    logger.error(error.message);
    throw error;
  }
 }

/**
 * 检测上传图片内容合规性（仅国内站）
 * 调用 algo_proxy 接口进行图片安全检测，不通过则抛出异常
 *
 * @param imageUri 已上传图片的 URI
 * @param refreshToken 刷新令牌
 * @param regionInfo 区域信息
 */
export async function checkImageContent(
  imageUri: string,
  refreshToken: string,
  regionInfo: RegionInfo
): Promise<void> {
  // 仅国内站需要内容检测
  if (regionInfo.isInternational) return;

  const babiParam = JSON.stringify({
    scenario: "image_video_generation",
    feature_key: "aigc_to_image",
    feature_entrance: "to-generate",
    feature_entrance_detail: "to-generate-algo_proxy",
  });

  logger.info(`开始图片内容安全检测: ${imageUri}`);

  try {
    await request("post", "/mweb/v1/algo_proxy", refreshToken, {
      params: {
        babi_param: babiParam,
      },
      data: {
        scene: "image_face_ip",
        options: { ip_check: true },
        req_key: "benchmark_test_user_upload_image_input",
        file_list: [{ file_uri: imageUri }],
        req_params: {},
      },
    });
    logger.info(`图片内容安全检测通过: ${imageUri}`);
  } catch (error: any) {
    // 区分内容违规(ret=2003等) vs 网络/服务异常
    const isContentViolation = error.message && (
      error.message.includes('2003') ||
      error.message.includes('risk not pass') ||
      error.message.includes('detected risk')
    );
    if (isContentViolation) {
      logger.error(`图片内容安全检测未通过: ${imageUri}, ${error.message}`);
      throw new APIException(
        EX.API_REQUEST_FAILED,
        `图片内容检测未通过，该图片可能包含违规内容`
      );
    }
    // 网络/服务异常不阻塞，仅记录警告
    logger.warn(`图片内容安全检测服务异常(不阻塞): ${imageUri}, ${error.message}`);
  }
}

 /**
  * 预检查文件URL有效性
  *
  * @param fileUrl 文件URL
  */
 export async function checkFileUrl(fileUrl: string) {
  if (util.isBASE64Data(fileUrl)) return;
  const result = await axios.head(fileUrl, {
    timeout: 15000,
    validateStatus: () => true,
  });
  if (result.status >= 400)
    throw new APIException(
      EX.API_FILE_URL_INVALID,
      `File ${fileUrl} is not valid: [${result.status}] ${result.statusText}`
    );
  // 检查文件大小
  if (result.headers && result.headers["content-length"]) {
    const fileSize = parseInt(result.headers["content-length"], 10);
    if (fileSize > FILE_MAX_SIZE)
      throw new APIException(
        EX.API_FILE_EXECEEDS_SIZE,
        `File ${fileUrl} is not valid`
      );
  }
}

/**
 * 上传文件
 *
 * @param refreshToken 用于刷新access_token的refresh_token
 * @param fileUrl 文件URL或BASE64数据
 * @param isVideoImage 是否是用于视频图像
 * @returns 上传结果，包含image_uri
 */
export async function uploadFile(
  refreshToken: string,
  fileUrl: string,
  isVideoImage: boolean = false
) {
  try {
    logger.info(`开始上传文件: ${fileUrl}, 视频图像模式: ${isVideoImage}`);

    // 预检查远程文件URL可用性
    await checkFileUrl(fileUrl);

    let filename, fileData, mimeType;
    // 如果是BASE64数据则直接转换为Buffer
    if (util.isBASE64Data(fileUrl)) {
      mimeType = util.extractBASE64DataFormat(fileUrl);
      const ext = mime.getExtension(mimeType);
      filename = `${util.uuid()}.${ext}`;
      fileData = Buffer.from(util.removeBASE64DataHeader(fileUrl), "base64");
      logger.info(`处理BASE64数据，文件名: ${filename}, 类型: ${mimeType}, 大小: ${fileData.length}字节`);
    }
    // 下载文件到内存，如果您的服务器内存很小，建议考虑改造为流直传到下一个接口上，避免停留占用内存
    else {
      filename = path.basename(fileUrl);
      logger.info(`开始下载远程文件: ${fileUrl}`);
      ({ data: fileData } = await axios.get(fileUrl, {
        responseType: "arraybuffer",
        // 100M限制
        maxContentLength: FILE_MAX_SIZE,
        // 60秒超时
        timeout: 60000,
      }));
      logger.info(`文件下载完成，文件名: ${filename}, 大小: ${fileData.length}字节`);
    }

    // 获取文件的MIME类型
    mimeType = mimeType || mime.getType(filename);
    logger.info(`文件MIME类型: ${mimeType}`);

    // 构建FormData
    const formData = new FormData();
    const blob = new Blob([fileData], { type: mimeType });
    formData.append('file', blob, filename);

    // 获取上传凭证
    logger.info(`请求上传凭证，场景: ${isVideoImage ? 'video_cover' : 'aigc_image'}`);
    const uploadProofUrl = 'https://imagex.bytedanceapi.com/';
    const proofResult = await request(
      'POST',
      '/mweb/v1/get_upload_image_proof',
      refreshToken,
      {
        data: {
          scene: isVideoImage ? 'video_cover' : 'aigc_image',
          file_name: filename,
          file_size: fileData.length,
        }
      }
    );

    if (!proofResult || !proofResult.proof_info) {
      logger.error(`获取上传凭证失败: ${JSON.stringify(proofResult)}`);
      throw new APIException(EX.API_REQUEST_FAILED, '获取上传凭证失败');
    }

    logger.info(`获取上传凭证成功`);

    // 上传文件
    const { proof_info } = proofResult;
    logger.info(`开始上传文件到: ${uploadProofUrl}`);

    const uploadResult = await axios.post(
      uploadProofUrl,
      formData,
      {
        headers: {
          ...proof_info.headers,
          'Content-Type': 'multipart/form-data',
        },
        params: proof_info.query_params,
        timeout: 60000,
        validateStatus: () => true, // 允许任何状态码以便详细处理
      }
    );

    logger.info(`上传响应状态: ${uploadResult.status}`);

    if (!uploadResult || uploadResult.status !== 200) {
      logger.error(`上传文件失败: 状态码 ${uploadResult?.status}, 响应: ${JSON.stringify(uploadResult?.data)}`);
      throw new APIException(EX.API_REQUEST_FAILED, `上传文件失败: 状态码 ${uploadResult?.status}`);
    }

    // 验证 proof_info.image_uri 是否存在
    if (!proof_info.image_uri) {
      logger.error(`上传凭证中缺少 image_uri: ${JSON.stringify(proof_info)}`);
      throw new APIException(EX.API_REQUEST_FAILED, '上传凭证中缺少 image_uri');
    }

    logger.info(`文件上传成功: ${proof_info.image_uri}`);

    // 返回上传结果
    return {
      image_uri: proof_info.image_uri,
      uri: proof_info.image_uri,
    }
  } catch (error) {
    logger.error(`文件上传过程中发生错误: ${error.message}`);
    throw error;
  }
}

/**
 * 检查请求结果
 *
 * @param result 结果
 */
export function checkResult(result: AxiosResponse) {
  const { ret, errmsg, data } = result.data;
  if (!_.isFinite(Number(ret))) return result.data;
  if (ret === '0') return data;

  // 使用统一错误处理器
  JimengErrorHandler.handleApiResponse(result.data as JimengErrorResponse, {
    context: '即梦API请求',
    operation: '请求'
  });
}

/**
 * Token切分
 *
 * @param authorization 认证字符串
 */
export function tokenSplit(authorization: string) {
  return authorization.replace("Bearer ", "").split(",");
}

/**
 * 获取Token存活状态
 */
export async function getTokenLiveStatus(refreshToken: string) {
  try {
    const result = await request(
      "POST",
      "/passport/account/info/v2",
      refreshToken,
      {
        params: {
          account_sdk_source: "web",
        },
      }
    );
    // request 内部已调用 checkResult，直接使用返回值
    return !!result?.user_id;
  } catch (err) {
    return false;
  }
}
