import type { InternalAxiosRequestConfig } from 'axios';
import { JSEncrypt } from 'jsencrypt';
import { SYSTEM_SERVICE_PREFIX } from '@/constants/service';

const API_ENCRYPT_HEADER = 'X-Api-Encrypt';
const API_ENCRYPT_HEADER_VALUE = 'true';

const API_ENCRYPT_CONFIG_ERROR_MSG = '前端加密配置异常，请联系管理员';
const API_ENCRYPT_REQUEST_ERROR_MSG = '请求加密失败，请刷新页面后重试';

const API_ENCRYPT_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2kobVo5aSDWvmWZIKeL
G7dowOLTwjdWIwy7+UmTBw3e6vJW5BNEjrnW7kiqeT97VQj2V6MMmaJufpeegACG
AmhuTnG83kVLQeiXL5rlPUmdNPM8O89gSM3iMzLSUhn+rvAaHFXjKNu2xssodYn1
F26SlVO1ewwS82AAwEPSaotL7Kq8Qxg7vmZty6RcEjp7/OaYAtHfva3uewiGMp11
ZkywKPleQ3nT7HHjQgAckbNZFMhTMMqDzW5oI3KSm3sA+pWsUfRrZxUf2ws358/F
KewDbbhwj3u731NbXlO+WUfv3FvbdhktXtU/15FC0b+Tx+YHIUhkNTRzuIpiG7+X
cwIDAQAB
-----END PUBLIC KEY-----`;

const API_ENCRYPT_RULE_MAP = {
  [`POST ${SYSTEM_SERVICE_PREFIX}/auth/login`]: ['password'],
  [`POST ${SYSTEM_SERVICE_PREFIX}/auth/register`]: ['password'],
  [`PUT ${SYSTEM_SERVICE_PREFIX}/user/profile/update-password`]: ['oldPassword', 'newPassword'],
  [`POST ${SYSTEM_SERVICE_PREFIX}/user/create`]: ['password'],
  [`PUT ${SYSTEM_SERVICE_PREFIX}/user/update-password`]: ['password']
} as const satisfies Record<string, readonly string[]>;

type ApiEncryptRuleKey = keyof typeof API_ENCRYPT_RULE_MAP;

interface ApiEncryptRequestConfig extends InternalAxiosRequestConfig {
  apiEncryptProcessed?: boolean;
}

let encryptor: JSEncrypt | null = null;

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return Object.prototype.toString.call(value) === '[object Object]';
}

function normalizeRequestPath(url: string) {
  try {
    return new URL(url, 'http://localhost').pathname;
  } catch {
    return url.split('?')[0] || '';
  }
}

function getApiEncryptRule(config: InternalAxiosRequestConfig) {
  const method = config.method?.toUpperCase();
  const url = config.url;

  if (!method || !url) {
    return null;
  }

  const ruleKey = `${method} ${normalizeRequestPath(url)}` as ApiEncryptRuleKey;

  return API_ENCRYPT_RULE_MAP[ruleKey] ?? null;
}

function getEncryptor() {
  if (encryptor) {
    return encryptor;
  }

  const publicKey = API_ENCRYPT_PUBLIC_KEY.trim();

  if (!publicKey.includes('BEGIN PUBLIC KEY') || !publicKey.includes('END PUBLIC KEY')) {
    throw new Error(API_ENCRYPT_CONFIG_ERROR_MSG);
  }

  encryptor = new JSEncrypt();
  encryptor.setPublicKey(publicKey);

  return encryptor;
}

function encryptFieldValue(value: unknown) {
  if (typeof value !== 'string' || value.length === 0) {
    throw new Error(API_ENCRYPT_REQUEST_ERROR_MSG);
  }

  const encryptedValue = getEncryptor().encrypt(value);

  if (!encryptedValue) {
    throw new Error(API_ENCRYPT_REQUEST_ERROR_MSG);
  }

  return encryptedValue;
}

export function applyApiEncrypt(config: InternalAxiosRequestConfig) {
  const encryptConfig = config as ApiEncryptRequestConfig;

  if (encryptConfig.apiEncryptProcessed) {
    return config;
  }

  const encryptFields = getApiEncryptRule(config);

  if (!encryptFields) {
    return config;
  }

  if (!isPlainObject(config.data)) {
    throw new Error(API_ENCRYPT_REQUEST_ERROR_MSG);
  }

  const nextData = { ...config.data };

  encryptFields.forEach(field => {
    nextData[field] = encryptFieldValue(nextData[field]);
  });

  config.data = nextData;
  config.headers.set(API_ENCRYPT_HEADER, API_ENCRYPT_HEADER_VALUE);
  encryptConfig.apiEncryptProcessed = true;

  return config;
}
