import _ from "lodash";
import crypto from "crypto";
import zlib from "zlib";

import APIException from "@/lib/exceptions/APIException.ts";
import EX from "@/api/consts/exceptions.ts";
import util from "@/lib/util.ts";
import { getCredit, receiveCredit, request, checkResult } from "./core.ts";
import logger from "@/lib/logger.ts";
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";

const DEFAULT_ASSISTANT_ID = "513695";
const DRAFT_VERSION = "3.0.2";

const constant = {
  "algorithm": "AWS4-HMAC-SHA256",
  "v4Identifier": "aws4_request",
  "dateHeader": "X-Amz-Date",
  "tokenHeader": "x-amz-security-token",
  "contentSha256Header": "X-Amz-Content-Sha256",
  "kDatePrefix": "AWS4"
}
const ff = [
  "authorization",
  "content-type",
  "content-length",
  "user-agent",
  "presigned-expires",
  "expect",
  "x-amzn-trace-id"
]
// 伪装headers
const FAKE_HEADERS = {  
  // authorization: "AWS4-HMAC-SHA256 Credential=AKTPNTcwMWUwZmE2ODNhNDk3OWE1M2ViNjU1ZDllNjcyMWI/20250529/cn-north-1/imagex/aws4_request, SignedHeaders=x-amz-date;x-amz-security-token, Signature=ca3fb22fa6b1940e45d7c744964d06af50053b3eb1105ae0b840027001190845",
  "accept": "*/*",
  "accept-encoding": "gzip, deflate, br, zstd",
  "accept-language": "zh-CN,zh;q=0.9",
  "cache-control": "no-cache",
  "last-event-id": "undefined",
  "origin": "https://jimeng.jianying.com",
  "priority": "u=1, i",
  "referer": "https://jimeng.jianying.com",
  "sec-ch-ua":'"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
  "sec-ch-ua-mobile": "?0",
  "sec-ch-ua-platform": '"Windows"',
  "sec-fetch-dest": "empty",
  "sec-fetch-mode": "cors",
  "sec-fetch-site": "cors-site",
  "user-agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
};
const hostStr = "https://imagex.bytedanceapi.com/";
const regionStr = "cn-north-1";
const serviceNameStr = "imagex";


// const te = {
//   "accessKeyId": "AKTPMTZjYTJkNWMzOWY1NDYxMmFlMmRlNDUxZTc3ODI4Yzk",
//   "secretAccessKey": "gWRJ+a7OLWDH+rsGUYNCSlV2r1zltBu+KNvO9TuLgzG+jrFv4ClLyVH/awi1Q3Uq",
//   "sessionToken": "STS2eyJMVEFjY2Vzc0tleUlEIjoiQUtMVFpUQm1ZbVl4TlRsa1ptVmpOREJqWVRrM09UUTNZbU5pTmprMk1EUXdaV00iLCJBY2Nlc3NLZXlJRCI6IkFLVFBNVFpqWVRKa05XTXpPV1kxTkRZeE1tRmxNbVJsTkRVeFpUYzNPREk0WXprIiwiU2lnbmVkU2VjcmV0QWNjZXNzS2V5IjoiN25ZU0o3T0FCMGZubHlKSENBbzcrOTMyV1FPbkUxck00ZnRuNHpPcVc2Unc1NTJJSWRnY3E1YkF6dWd0VUZaRTZPSzhUa3JBb2c3TW9GV3UzeWplZjV2Y3I2cndUWWM4dklYMzdiZ1BuSWM9IiwiRXhwaXJlZFRpbWUiOjE3NDg1OTEzMDksIlBvbGljeVN0cmluZyI6IntcIlN0YXRlbWVudFwiOlt7XCJFZmZlY3RcIjpcIkFsbG93XCIsXCJBY3Rpb25cIjpbXCJ2b2Q6QXBwbHlVcGxvYWRcIixcInZvZDpBcHBseVVwbG9hZElubmVyXCIsXCJ2b2Q6Q29tbWl0VXBsb2FkXCIsXCJ2b2Q6Q29tbWl0VXBsb2FkSW5uZXJcIixcInZvZDpHZXRVcGxvYWRDYW5kaWRhdGVzXCIsXCJJbWFnZVg6QXBwbHlJbWFnZVVwbG9hZFwiLFwiSW1hZ2VYOkNvbW1pdEltYWdlVXBsb2FkXCIsXCJJbWFnZVg6QXBwbHlVcGxvYWRJbWFnZUZpbGVcIixcIkltYWdlWDpDb21taXRVcGxvYWRJbWFnZUZpbGVcIl0sXCJSZXNvdXJjZVwiOltcIipcIl0sXCJDb25kaXRpb25cIjpcIntcXFwiUFNNXFxcIjpcXFwidmlkZW9jdXQubXdlYi5hcGlcXFwifVwifV19IiwiU2lnbmF0dXJlIjoiZTkyNGRmY2Q2ZjE4OTM1NTg5ZjBjYzk5NDc3MjkxZjU5MDk5NGE0ZGZmMGU1Mjc5YjkwZTk1NmUwY2Q0MWFlMiJ9"
// }
//       const tq = {
//           "Action": "ApplyImageUpload",
//           "Version": "2018-08-01",
//           "ServiceId": "tb4s082cfz",
//           "FileSize": 1930510,
//           "s": "5sohmgy6e1p"
//       }
//       const ttt = "20250530T064830Z";

function toQueryString(any) {
  var e = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {};
  return Object.keys(e).map(function(t) {
      return "".concat(t, "=").concat(encodeURIComponent(e[t]))
  }).join("&")
}
function iso8601(e) {
  return void 0 === e && (e = new Date),
  e.toISOString().replace(/\.\d{3}Z$/, "Z")
}

function createScope(e, t, r) {
  return [e.substr(0, 8), t, r, constant.v4Identifier].join("/")
}
function credentialString(e) {
  return createScope(e.substr(0, 8), regionStr, serviceNameStr)
}
function p1(str: any): string { // Changed 'e' to 'str' and added 'any' type for broader compatibility initially
    let s = String(str); // Ensure it's a string, as query params can be numbers (e.g., FileSize)
    // encodeURIComponent does not encode: A-Z a-z 0-9 - _ . ! ~ * ' ( )
    // AWS Signature V4 requires specific encoding for certain characters (RFC3986).
    // Notably, '*' should be %2A. '!' ' ( ) ' should also be percent-encoded.
    // '~' is allowed unencoded by RFC3986.
    return encodeURIComponent(s).replace(/[!'()*]/g, function (c) {
        return '%' + c.charCodeAt(0).toString(16).toUpperCase();
    });
}
function help(e) {
  return Object.keys(e).sort().map(function(t) {
      var r = e[t];
      if (null != r) {
          var n = p1(t);
          if (n)
              return Array.isArray(r) ? "".concat(n, "=").concat(r.map(p1).sort().join("&".concat(n, "="))) : "".concat(n, "=").concat(p1(r))
      }
  }).filter(function(e) {
      return e
  }).join("&")
}
function isSignableHeader(e) {
  return 0 === e.toLowerCase().indexOf("x-amz-") || 0 > ff.indexOf(e)
}
function canonicalHeaderValues(e) {
  return e.replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "")
}
function canonicalHeaders(headers) {
  var t = [];
  Object.keys(headers).forEach(function(r) {
      t.push([r, headers[r]])
  }),
  t.sort(function(e, t) {
      return e[0].toLowerCase() < t[0].toLowerCase() ? -1 : 1
  });
  var r = [];
  return t.forEach((t)=>{
      var n = t[0].toLowerCase();
      if (isSignableHeader(n)) {
          var i = t[1];
          if (null == i || "function" != typeof i.toString)
              throw Error("Header ".concat(n, " contains invalid value"));
          r.push("".concat(n, ":").concat(canonicalHeaderValues(i.toString())))
      }
  }),
  r.join("\n")
}
function canonicalString(query, headers) {
  var e = []
    , t = "/";
  return e.push("get".toUpperCase()),
  e.push(t),
  e.push(help(query) || ""),
  e.push("".concat(canonicalHeaders(headers), "\n")),
  e.push(signedHeaders(headers)),
  e.push(hexEncodedHash("")),
  e.join("\n")
}
function signedHeaders(headers) {
  var t = [];
  return Object.keys(headers).forEach((r)=>{
      r = r.toLowerCase(),
      isSignableHeader(r) && t.push(r)
  }),
  t.sort().join(";")
}
function stringToSign(e, query, headers) {
  let cs = canonicalString(query, headers);
  // console.log("");
  // console.log("cs", cs);
  let cs2 = hexEncodedHash(cs);
  // console.log("");
  // console.log("cs2", cs2);
  // console.log("");
    var t = [];
    return t.push(constant.algorithm),
    t.push(e),
    t.push(credentialString(e)),
    t.push(cs2),
    t.join("\n")
}

// Overloads for hmac to provide better type inference. Note: message is first, key is second.
function hmac(message: string, key: string, encoding: crypto.BinaryToTextEncoding): string;
function hmac(message: string, key: string): crypto.BinaryLike;
function hmac(message: string, key: crypto.BinaryLike, encoding: crypto.BinaryToTextEncoding): string;
function hmac(message: string, key: crypto.BinaryLike): crypto.BinaryLike;
function hmac(message: string, key: crypto.KeyObject, encoding: crypto.BinaryToTextEncoding): string;
function hmac(message: string, key: crypto.KeyObject): crypto.BinaryLike;
// Implementation
function hmac(message: string, key: crypto.BinaryLike | crypto.KeyObject, encoding?: crypto.BinaryToTextEncoding): string | Buffer | crypto.BinaryLike  {
  const hash = crypto.createHmac('sha256', key).update(message, 'utf-8');
  if (encoding) {
    return hash.digest(encoding);
  }
  return hash.digest(); // Returns Buffer by default
}
function hexEncodedHash(k:string) {
  return crypto.createHash("sha256").update(k, 'utf-8').digest("hex");
}

function getSigningKey(secretAccessKey: string, dateStamp: string, regionName: string, serviceName: string): crypto.BinaryLike {
  const kSecret = "AWS4" + secretAccessKey; // kSecret is string
  // Corrected parameter order: hmac(message, key)
  const kDate: crypto.BinaryLike = hmac(dateStamp, kSecret); // hmac(string, string) -> Buffer
  const kRegion: crypto.BinaryLike = hmac(regionName, kDate); // hmac(string, Buffer) -> Buffer
  const kService: crypto.BinaryLike = hmac(serviceName, kRegion); // hmac(string, Buffer) -> Buffer
  const kSigning: crypto.BinaryLike = hmac(constant.v4Identifier, kService); // hmac(string, Buffer) -> Buffer
  return kSigning;
}

function signature(secretAccessKey:string, amzDate:string, query: any, headers: any): string {
  const dateStamp = amzDate.substring(0, 8); // Extract YYYYMMDD from YYYYMMDDTHHMMSSZ
  const signingKey: crypto.BinaryLike = getSigningKey(secretAccessKey, dateStamp, regionStr, serviceNameStr);
  const stringToSignValue = stringToSign(amzDate, query, headers);
  // The final signature must be a hex string.
  // Corrected parameter order: hmac(message, key, encoding)
  return hmac(stringToSignValue, signingKey, 'hex');
}

function authorizationSign(accessKeyId, secretAccessKey, t, query, headers) {
  var s = signature(secretAccessKey, t, query, headers);
  // console.log("signature", s);
  var r = []
    , n = credentialString(t);
  return r.push("".concat(constant.algorithm, " Credential=").concat(accessKeyId, "/").concat(n)),
  r.push("SignedHeaders=".concat(signedHeaders(headers))),
  r.push("Signature=".concat(s)),
  r.join(", ")
}

/**
 * 请求jimeng
 *
 * @param method 请求方法
 * @param uri 请求路径
 * @param params 请求参数
 * @param headers 请求头
 */
async function requestUpload(
  method: string,
  uri: string,
  data: any, // Added data parameter for request body
  options: AxiosRequestConfig = {},
  host: string= "",
) {
  let h = {
    ...(options.headers || {}),
  }
  // console.log("requestUpload headers", h);
  // return ""
  const response = await axios.request({
    method,
    url: `${host||'https://jimeng.jianying.com'}${uri}`,
    data, // Use the new data parameter for the request body
    params: {
      ...(options.params || {}),
    },
    headers: h,
    timeout: 60000, // 增加超时时间到60秒，匹配客户端期望
    validateStatus: () => true,
    ..._.omit(options, "params", "headers"),
  });
  // 流式响应直接返回response
  if (options.responseType == "stream") return response;    
  // console.log("response", response);
  return checkResult(response);
}

export async function getUploadToken(refreshToken: string){
  const res = await request(
    "post",
    "/mweb/v1/get_upload_token",
    refreshToken,
    {
      params: {},
      data: {
        scene: 2,
      },
    }
  );
  // console.log("get_upload_token", res);
  return res;
}

export async function getUploadService(
  access_key_id:string, 
  secret_access_key:string, 
  session_token:string, 
  space_name:string, 
  upload_domain:string, 
  file_size:number,
){  
  // console.log("access_key_id", access_key_id);
  // console.log("secret_access_key", secret_access_key);
  // console.log("session_token", session_token);
  // console.log("space_name", space_name);
  // console.log("upload_domain", upload_domain);
  // console.log("file_size", file_size);
  // console.log("");

  let query = {
    Action: "ApplyImageUpload",
    Version: "2018-08-01",
    ServiceId: space_name,
    FileSize: file_size,
    // s :Math.random().toString(36).substring(2)
    s :"skloxwd3qfb"
  };

  // query = tq;

  // console.log("query",query);
  // console.log("");
  let url = ""+toQueryString(query);
  let time = new Date();
  let t = iso8601(time);
  var r = t.replace(/[:\-]|\.\d{3}/g, "");
  // var r = "20250530T055559Z";
  // var r = ttt;
  // console.log("r", r);
  let headers = {
    // "x-amz-security-token": te.sessionToken,
    "x-amz-security-token": session_token,
    "x-amz-date": r,
  };
  let authorization = authorizationSign(
    // te.accessKeyId,
    // te.secretAccessKey,
    access_key_id, 
    secret_access_key, 
    r, 
    query, 
    headers);
  // console.log("url", url);
  // console.log("authorization", authorization);
  // console.log("headers", headers);
  return await requestUpload(
    "get", 
    "?"+url, 
    null,
    {
    headers: {
      ...FAKE_HEADERS,
      ...headers,
      "authorization": authorization,
    },
  }, `https://${upload_domain}/`);
}

export function calculateCrc32Hex(data) {
  // 计算 CRC32 值，Node.js 的 zlib.crc32 返回无符号整数
  //@ts-ignore
  const crc32Value = zlib.crc32(data);

  // 将无符号整数转换为十六进制字符串
  // 使用 >>> 0 确保是无符号操作，尽管 zlib.crc32 已经返回无符号
  // 使用 toString(16) 转换为十六进制
  let hexString = (crc32Value >>> 0).toString(16);

  // 补零以确保是 8 位十六进制字符串（如果需要的话，取决于具体的格式要求）
  // Python 的 hex()[2:] 对于 32 位整数会输出 8 位十六进制（不包括 0x）
  while (hexString.length < 8) {
    hexString = '0' + hexString;
  }

  return hexString;
}


// 伪装headers
const FAKE_OSS_HEADERS = {  
  "accept": "*/*",
  "accept-encoding": "gzip, deflate, br, zstd",
  "accept-language": "zh-CN,zh;q=0.9",
  "cache-control": "no-cache",
  "last-event-id": "undefined",
  "origin": "https://jimeng.jianying.com",
  "priority": "u=1, i",
  "referer": "https://jimeng.jianying.com",
  "sec-ch-ua":'"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
  "sec-ch-ua-mobile": "?0",
  "sec-ch-ua-platform": '"Windows"',
  "sec-fetch-dest": "empty",
  "sec-fetch-mode": "cors",
  "sec-fetch-site": "cors-site",
  "user-agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
};
export async function sendFile(url:string, crc32:string, authorization:string, fileBuffer:any){
  let headers = {    
    "content-type": "application/octet-stream",
    "content-disposition": 'attachment; filename="undefined"',
    "content-crc32": crc32,
    "authorization": authorization,
  }
  return await requestUpload(
    "post", 
    "",
    fileBuffer, // data is the second param
    {
    headers: {
      ...FAKE_OSS_HEADERS,
      ...headers,
      "authorization": authorization,
    },
  }, url);
}


export async function uploadImages(
  _model: string,
  prompt: string,
  {
    width = 1024,
    height = 1024,
    sampleStrength = 0.5,
    negativePrompt = "",
  }: {
    width?: number;
    height?: number;
    sampleStrength?: number;
    negativePrompt?: string;
  },
  refreshToken: string
) {
  
}

