import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import * as crypto from 'crypto';

@Provide()
@Scope(ScopeEnum.Singleton)
export class GeoEncryptService {
  private readonly algorithm = 'aes-256-gcm';
  /** 本地开发兜底 key（生产请配 SKILL_SECRET_KEY 或 APP_SECRET 环境变量） */
  private static readonly DEV_FALLBACK_KEY = 'gpu-guard-geo-encrypt-dev-only-change-in-production-2026';
  private warned = false;

  /** 从环境变量派生 AES-256 密钥（SHA-256 哈希）。未配置环境变量时用兜底 key（开发场景） */
  private deriveKey(): Buffer {
    const raw = process.env.SKILL_SECRET_KEY || process.env.APP_SECRET || GeoEncryptService.DEV_FALLBACK_KEY;
    if (!process.env.SKILL_SECRET_KEY && !process.env.APP_SECRET && !this.warned) {
      this.warned = true;
      // eslint-disable-next-line no-console
      console.warn('[GeoEncrypt] 未配置 SKILL_SECRET_KEY / APP_SECRET，使用开发兜底密钥（生产环境必须配置）');
    }
    return crypto.createHash('sha256').update(raw).digest();
  }

  /**
   * 加密字符串，返回 base64 编码的密文
   * 格式: IV(12字节) + AuthTag(16字节) + 密文
   */
  encrypt(plain: string): string {
    const key = this.deriveKey();
    const iv = crypto.randomBytes(12);
    const cipher = crypto.createCipheriv(this.algorithm, key, iv);
    const ct = Buffer.concat([cipher.update(plain, 'utf8'), cipher.final()]);
    const tag = cipher.getAuthTag();
    return Buffer.concat([iv, tag, ct]).toString('base64');
  }

  /**
   * 解密 base64 密文，返回原始字符串
   * 若密文被篡改，GCM 认证标签校验会抛出错误
   */
  decrypt(cipherText: string): string {
    const key = this.deriveKey();
    const buf = Buffer.from(cipherText, 'base64');
    if (buf.length < 28) throw new Error('Invalid ciphertext: too short');
    const iv = buf.subarray(0, 12);
    const tag = buf.subarray(12, 28);
    const ct = buf.subarray(28);
    const decipher = crypto.createDecipheriv(this.algorithm, key, iv);
    decipher.setAuthTag(tag);
    return Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8');
  }
}
