import { Injectable } from '@nestjs/common';
import { createHash, randomBytes, createCipheriv, createDecipheriv, hkdfSync } from 'crypto';
import { createLogger } from '@core/observability/logging/config/winston.config';

const logger = createLogger('EnvelopeEncryption');

/**
 * PR10.5 envelope encryption（per-user key derivation）骨架。
 *
 * **真实生产**（PRD §1.11 / PR10.5 v0.5 要求三因子派生）：
 *   user secret + KMS root + per-org salt → HKDF-SHA256 → wrapping key
 *   wrapping key 解密 wrapped DEK → DEK 解 ciphertext。
 *   KMS root 单点失陷下用户 token 仍不可解（三因子缺一）。
 *
 * **当前（slot-5 demo）**：用 process.env `FFAI_ENVELOPE_KMS_ROOT` + org salt + user secret
 * 三因子 HKDF。无 HSM，不算生产强度，但**结构对了**——上 HSM/AWS KMS 时只换 root 来源。
 */
@Injectable()
export class EnvelopeEncryptionService {
  private readonly kmsRoot: Buffer;

  constructor() {
    const raw = process.env.FFAI_ENVELOPE_KMS_ROOT;
    if (!raw) {
      // dev 兜底：固定 dev key（**生产必须改用真 KMS**）
      logger.warn('FFAI_ENVELOPE_KMS_ROOT 未配置 — 使用 dev fallback root（不安全）');
      this.kmsRoot = createHash('sha256').update('ffai-dev-kms-root-do-not-use-in-prod').digest();
    } else {
      this.kmsRoot = Buffer.from(raw, 'hex');
    }
  }

  /**
   * HKDF-SHA256 三因子派生：kmsRoot (IKM) + orgSalt (salt) + userSecret (info) → 32 byte wrapping key。
   * 用 Node 内置 crypto.hkdfSync 兑现 RFC 5869 extract+expand 两阶段，避免单次 sha256 的派生强度不足。
   */
  deriveWrappingKey(args: { organizationId: string; userId: string }): Buffer {
    const orgSalt = createHash('sha256').update(`org:${args.organizationId}`).digest();
    const info = Buffer.from(`ffai-envelope|user:${args.userId}`, 'utf8');
    // hkdfSync(digest, ikm, salt, info, keylen) → ArrayBuffer，转 Buffer
    const derived = hkdfSync('sha256', this.kmsRoot, orgSalt, info, 32);
    return Buffer.from(derived);
  }

  /**
   * 加密 plaintext（如 OneDrive refresh_token）→ base64( iv || ciphertext || authTag )
   */
  encrypt(args: { organizationId: string; userId: string; plaintext: string }): { ciphertext: string; keyId: string } {
    const wrapKey = this.deriveWrappingKey({ organizationId: args.organizationId, userId: args.userId });
    const iv = randomBytes(12);
    const cipher = createCipheriv('aes-256-gcm', wrapKey, iv);
    const ct = Buffer.concat([cipher.update(args.plaintext, 'utf8'), cipher.final()]);
    const tag = cipher.getAuthTag();
    const blob = Buffer.concat([iv, ct, tag]).toString('base64');
    // keyId = sha256(org || user) prefix —— 用于解密时定位"哪一对参数派生的"，不泄露 kmsRoot
    const keyId = createHash('sha256').update(`${args.organizationId}:${args.userId}`).digest('hex').slice(0, 16);
    return { ciphertext: blob, keyId };
  }

  decrypt(args: { organizationId: string; userId: string; ciphertext: string }): string {
    const wrapKey = this.deriveWrappingKey({ organizationId: args.organizationId, userId: args.userId });
    const blob = Buffer.from(args.ciphertext, 'base64');
    const iv = blob.subarray(0, 12);
    const tag = blob.subarray(blob.length - 16);
    const ct = blob.subarray(12, blob.length - 16);
    const decipher = createDecipheriv('aes-256-gcm', wrapKey, iv);
    decipher.setAuthTag(tag);
    return Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8');
  }
}
