import { sm2Decrypt, sm2Encrypt, sm3Encrypt, sm4Decrypt, sm4Encrypt } from './sm';
import { v4 as uuidv4 } from 'uuid';
import CryptoJS from 'crypto-js';
import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { AxiosHeaders } from 'axios';
import { getCryptoConfig } from './cryptoConfig';

/**
 * HTTP头部常量
 */
export const SECURITY_HEADERS = {
  CONTENT_TYPE: 'content-type',
  CLIENT_ID: 'clientid',
  REQUEST_TIME: 'request-time',
  RESPONSE_TIME: 'response-time',
  NONCE: 'nonce',
  VALID_TIME: 'valid-time',
  SAFE_MODE: 'safe-mode',
  ENCRYPT_KEY: 'encrypt-key',
  SIGNATURE: 'signature',
  AUTH_MODE: 'auth-mode',
  AUTHORIZATION: 'authorization2',
  CRYPTO_TYPE: 'crypto-type'
} as const;

/**
 * 加密配置常量
 * SM4密钥长度（16字节的hex表示）
 */
export const CRYPTO_CONFIG = { SM4_KEY_LENGTH: 32 } as const;

/**
 * 工具函数
 */
export class SecurityUtils {
  /**
   * 格式化日期时间为标准格式：yyyy-MM-ddTHH:mm:ss.SSS
   * 使用 toLocaleString，更简洁（本地时间）
   */
  static formatDateTime(date: Date): string {
    const y = date.getFullYear();
    const mo = date.getMonth() + 1;
    const d = date.getDate();
    const h = date.getHours();
    const mi = date.getMinutes();
    const s = date.getSeconds();
    const ms = date.getMilliseconds();

    return `${y}-${mo < 10 ? '0' + mo : mo}-${d < 10 ? '0' + d : d}T${h < 10 ? '0' + h : h}:${mi < 10 ? '0' + mi : mi}:${s < 10 ? '0' + s : s}.${ms < 10 ? '00' + ms : ms < 100 ? '0' + ms : ms}`;
  }

  /**
   * 生成随机16进制字符串
   */
  static generateRandomHexString(length: number): string {
    const bytes = new Uint8Array(Math.ceil(length / 2));
    crypto.getRandomValues(bytes);
    return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0'))
      .join('')
      .slice(0, length);
  }
}

/**
 * 国密安全处理器
 * 专门处理国密SM算法相关的加密、签名等操作
 */
export class SecurityHandler {
  private cryptoConfig = getCryptoConfig();

  constructor() {}

  /**
   * 判断是否需要加密数据内容
   * @param config 请求配置
   * @returns 是否需要加密数据
   */
  private shouldEncryptData(config: InternalAxiosRequestConfig): boolean {
    // 如果全局禁用了加密，直接返回false
    if (!this.cryptoConfig.isEncryptEnabled()) {
      return false;
    }
    // 兼容旧的isEncrypt控制（如果存在且为false，则不加密）
    const legacyEncryptFlag = config.headers?.get('isEncrypt');
    if (legacyEncryptFlag === 'false') {
      return false;
    }
    // 默认根据全局配置决定
    return true;
  }

  /**
   * 添加国密基础头部信息
   */
  private addGmHeaders(headers: AxiosHeaders, isDataEncrypted: boolean): void {
    const now = new Date();
    const requestTime = SecurityUtils.formatDateTime(now);
    const nonce = uuidv4();
    headers.set(SECURITY_HEADERS.REQUEST_TIME, requestTime);
    headers.set(SECURITY_HEADERS.NONCE, nonce);
    headers.set(SECURITY_HEADERS.CRYPTO_TYPE, 'GM');
    // 设置安全模式标识：1表示数据已加密，0表示明文
    headers.set(SECURITY_HEADERS.SAFE_MODE, isDataEncrypted ? '1' : '0');
    // 设置客户端ID
    const clientId = import.meta.env.VITE_APP_CLIENT_ID;
    if (clientId) {
      headers.set(SECURITY_HEADERS.CLIENT_ID, clientId);
    }
  }

  /**
   * 国密加密请求数据
   */
  private async encryptDataWithGm(data: any, headers: AxiosHeaders): Promise<string> {
    try {
      // 生成随机SM4密钥
      const sm4Key = SecurityUtils.generateRandomHexString(CRYPTO_CONFIG.SM4_KEY_LENGTH);
      // 直接使用环境变量中的原始SM2公钥
      const sm2PublicKey = import.meta.env.VITE_APP_S2_PUB_K;
      if (!sm2PublicKey) {
        throw new Error('国密公钥未配置');
      }
      // SM2加密SM4密钥
      const encryptedKey = sm2Encrypt(sm4Key, sm2PublicKey);
      if (!encryptedKey) {
        throw new Error('国密加密SM4密钥失败');
      }
      headers.set(SECURITY_HEADERS.ENCRYPT_KEY, '04' + encryptedKey);
      // SM4加密请求体 - 统一处理，不判断是否为空
      const bodyString = typeof data === 'string' ? data : JSON.stringify(data || {});
      // 空对象不加密，直接返回
      if (bodyString === '{}' || bodyString === '') {
        return '{}';
      }
      return sm4Encrypt(bodyString, sm4Key);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      throw new Error(`国密加密失败: ${errorMessage}`);
    }
  }

  /**
   * 生成认证签名（用于身份验证）
   */
  private generateAuthSignature(method: string, path: string, headers: AxiosHeaders): string {
    try {
      const clientId = headers.get(SECURITY_HEADERS.CLIENT_ID) || '';
      const requestTime = headers.get(SECURITY_HEADERS.REQUEST_TIME) || '';
      const nonce = headers.get(SECURITY_HEADERS.NONCE) || '';
      const stringParams = [method.toUpperCase(), path, clientId, requestTime, nonce, clientId];
      const stringToSign = stringParams.join(';');
      return sm3Encrypt(stringToSign);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      throw new Error(`认证签名生成失败: ${errorMessage}`);
    }
  }

  /**
   * 生成数据签名（用于完整性校验）
   */
  private generateDataSignature(data: string, headers: AxiosHeaders): string {
    try {
      const clientId = headers.get(SECURITY_HEADERS.CLIENT_ID) || '';
      const requestTime = headers.get(SECURITY_HEADERS.REQUEST_TIME) || '';
      const nonce = headers.get(SECURITY_HEADERS.NONCE) || '';
      const validTime = headers.get(SECURITY_HEADERS.VALID_TIME) || '';
      const safeMode = headers.get(SECURITY_HEADERS.SAFE_MODE) || '';
      const encryptKey = headers.get(SECURITY_HEADERS.ENCRYPT_KEY) || '';

      const stringParams = [clientId, requestTime, nonce, validTime, safeMode, encryptKey];
      const stringToSign = stringParams.join(';') + ';' + data + clientId;
      const md5Hash = CryptoJS.MD5(stringToSign).toString();
      const sm3Result = sm3Encrypt(md5Hash);

      return sm3Result;
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      throw new Error(`数据签名生成失败: ${errorMessage}`);
    }
  }

  /**
   * 生成国密双重签名
   */
  private generateGmSignatures(method: string, path: string, data: string, headers: AxiosHeaders): void {
    // 生成认证签名
    const authSignature = this.generateAuthSignature(method, path, headers);
    headers.set(SECURITY_HEADERS.AUTHORIZATION, `AK-SK-V1/${authSignature}`);
    // 生成数据签名
    const dataSignature = this.generateDataSignature(data, headers);
    headers.set(SECURITY_HEADERS.SIGNATURE, dataSignature);
  }

  /**
   * 提取URL路径（不含域名和参数）
   * 确保返回的路径以 / 开头
   */
  private extractUrlPath(url: string): string {
    let path: string;
    try {
      const urlObj = new URL(url);
      path = urlObj.pathname;
    } catch {
      // 如果不是完整URL，直接返回并去掉查询参数
      path = url.split('?')[0];
    }
    // 确保路径以 / 开头
    if (!path.startsWith('/')) {
      path = '/' + path;
    }
    return path;
  }

  /**
   * 处理国密请求配置
   */
  async processRequest(config: InternalAxiosRequestConfig): Promise<void> {
    try {
      if (!config.headers) {
        config.headers = new AxiosHeaders();
      }
      // 判断是否需要加密数据
      const shouldEncrypt = this.shouldEncryptData(config);
      // 添加国密基础头部信息（根据是否实际加密设置SAFE_MODE）
      this.addGmHeaders(config.headers, shouldEncrypt);
      let processedData: string;
      // 处理数据：如果需要加密则加密，否则只是序列化
      if (shouldEncrypt) {
        processedData = await this.encryptDataWithGm(config.data, config.headers);
      } else {
        processedData = typeof config.data === 'string' ? config.data : JSON.stringify(config.data || {});
      }
      // 生成国密签名（无论是否加密都需要签名）
      const method = config.method || 'GET';
      const path = this.extractUrlPath(config.url || '');
      this.generateGmSignatures(method, path, processedData, config.headers);
      // 关键修复：设置transformRequest为空数组，阻止Axios的默认数据转换
      config.transformRequest = [];
      config.data = processedData;
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      throw new Error(`国密请求处理失败: ${errorMessage}`);
    }
  }

  /**
   * 处理国密响应数据解密
   */
  processResponse(response: AxiosResponse, requestConfig: InternalAxiosRequestConfig): AxiosResponse {
    try {
      // 从请求配置中获取加密密钥
      let encryptedKeyStr = requestConfig?.headers?.get?.(SECURITY_HEADERS.ENCRYPT_KEY) || requestConfig?.headers?.[SECURITY_HEADERS.ENCRYPT_KEY];
      if (!encryptedKeyStr || !response.data) {
        return response;
      }
      // 直接使用环境变量中的原始SM2私钥
      const sm2PrivateKey = import.meta.env.VITE_APP_S2_PRI_K;
      if (!sm2PrivateKey) {
        throw new Error('国密私钥未配置');
      }
      encryptedKeyStr = encryptedKeyStr.substring(2);
      // SM2解密获取SM4密钥
      const decryptedKey = sm2Decrypt(encryptedKeyStr, sm2PrivateKey);
      if (!decryptedKey || decryptedKey.length < 32) {
        throw new Error('SM2解密SM4密钥失败');
      }
      const sm4Key = decryptedKey.substring(0, 32);
      // SM4解密响应数据
      const decryptedData = sm4Decrypt(response.data, sm4Key);
      if (!decryptedData) {
        throw new Error('SM4解密响应数据失败');
      }
      // JSON解析
      response.data = JSON.parse(decryptedData);
      return response;
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      throw new Error(`国密响应处理失败: ${errorMessage}`);
    }
  }
}

/**
 * 全局国密安全处理器实例
 */
let globalSecurityHandler: SecurityHandler | null = null;

/**
 * 获取全局国密安全处理器
 */
export function getSecurity(): SecurityHandler {
  if (!globalSecurityHandler) {
    globalSecurityHandler = new SecurityHandler();
  }
  return globalSecurityHandler;
}

/**
 * 处理国密请求的便捷函数 - 直接修改config，不返回对象
 */
export async function processSecurityRequest(config: InternalAxiosRequestConfig): Promise<void> {
  await getSecurity().processRequest(config);
}

/**
 * 处理国密响应的便捷函数
 * @param response 响应对象
 * @param requestConfig 对应的请求配置对象
 */
export function processSecurityResponse(response: AxiosResponse, requestConfig: InternalAxiosRequestConfig): AxiosResponse {
  return getSecurity().processResponse(response, requestConfig);
}
