import { Injectable, Logger } from '@nestjs/common';
import { createHash, createHmac } from 'crypto';
import { ConfigService } from '@nestjs/config';

/**
 * 哈希链服务
 * 用于生成和验证审计日志的哈希链，确保数据完整性
 * 支持 HMAC 数字签名
 */
@Injectable()
export class HashChainService {
  private readonly logger = new Logger(HashChainService.name);
  private readonly hmacSecret: string;

  constructor(private readonly configService: ConfigService) {
    // 从环境变量获取 HMAC密钥，如果不存在则使用默认值（生产环境必须配置）
    this.hmacSecret = this.configService.get<string>('AUDIT_HMAC_SECRET') ||  
      'default-audit-hmac-secret-change-in-production-2025';
    
    if (this.hmacSecret === 'default-audit-hmac-secret-change-in-production-2025') {
      this.logger.warn('Using default HMAC secret. Please set AUDIT_HMAC_SECRET in production!');
    }
  }

  /**
   * 生成日志记录的 SHA-256 哈希
   * @param data 要计算哈希的数据
   * @returns 哈希字符串
   */
  async generateHash(data: any): Promise<string> {
    try {
      // 排除 currentHash 和 signature 字段本身
      const { currentHash, signature, createdAt, archivedAt, ...hashData } = data;
      
      // 确保字段顺序一致（对象键排序）
      const sortedData = this.sortObject(hashData);
      
      // 转换为字符串并计算哈希
      const dataString = JSON.stringify(sortedData);
      const hash = createHash('sha256').update(dataString, 'utf8').digest('hex');
      
      return hash;
    } catch (error) {
      this.logger.error('Failed to generate hash', error);
      throw error;
    }
  }

  /**
   * 对象键排序（确保哈希一致性）
   * @param obj 要排序的对象
   * @returns 排序后的对象
   */
  private sortObject(obj: any): any {
    if (obj === null || obj === undefined) {
      return obj;
    }
    
    if (typeof obj !== 'object') {
      return obj;
    }
    
    if (Array.isArray(obj)) {
      return obj.map(item => this.sortObject(item));
    }
    
    if (obj instanceof Date) {
      return obj.toISOString();
    }
    
    const sorted: any = {};
    const keys = Object.keys(obj).sort();
    
    for (const key of keys) {
      sorted[key] = this.sortObject(obj[key]);
    }
    
    return sorted;
  }

  /**
   * 生成 HMAC 签名
   * @param data 要签名的数据
   * @returns HMAC 签名字符串
   */
  async generateSignature(data: any): Promise<string> {
    try {
      // 排除签名字段本身
      const { signature: _sig, createdAt, archivedAt, ...signData } = data;
      
      // 确保字段顺序一致
      const sortedData = this.sortObject(signData);
      
      // 转换为字符串并计算 HMAC-SHA256 签名
      const dataString = JSON.stringify(sortedData);
      const hmac = createHmac('sha256', this.hmacSecret);
      hmac.update(dataString, 'utf8');
      const signatureResult = hmac.digest('hex');
      
      return signatureResult;
    } catch (error) {
      this.logger.error('Failed to generate HMAC signature', error);
      throw error;
    }
  }

  /**
   * 验证 HMAC 签名
   * @param data 要验证的数据
   * @param expectedSignature 期望的签名值
   * @returns 是否匹配
   */
  async verifySignature(data: any, expectedSignature: string): Promise<boolean> {
    try {
      if (!expectedSignature) {
        this.logger.warn('No signature provided for verification');
        return false;
      }
      
      const calculatedSignature = await this.generateSignature(data);
      return calculatedSignature === expectedSignature;
    } catch (error) {
      this.logger.error('Failed to verify HMAC signature', error);
      return false;
    }
  }

  /**
   * 验证哈希链
   * @param logs 按时间顺序排列的日志数组
   * @returns 验证结果
   *
   * 注意：返回 failures[].type 为当前 4 值联合，但 audit_integrity_check_log.failures (Json)
   * 历史数据中可能存有已废弃的值（如 MISSING_HASH）。读取历史记录时不要做穷举 switch，
   * 否则会漏 case；按字符串展示即可。
   */
  async verifyHashChain(logs: any[]): Promise<{
    success: boolean;
    failures: Array<{
      index: number;
      logId: string;
      type: 'HASH_CHAIN_BROKEN' | 'HASH_MISMATCH' | 'INVALID_GENESIS' | 'SIGNATURE_INVALID';
      message: string;
      expectedHash?: string;
      actualHash?: string;
    }>;
  }> {
    const failures: Array<{
      index: number;
      logId: string;
      type: 'HASH_CHAIN_BROKEN' | 'HASH_MISMATCH' | 'INVALID_GENESIS' | 'SIGNATURE_INVALID';
      message: string;
      expectedHash?: string;
      actualHash?: string;
    }> = [];

    for (let i = 0; i < logs.length; i++) {
      const log = logs[i];

      if (i > 0) {
        const prevLog = logs[i - 1];
        if (log.previousHash !== prevLog.currentHash) {
          failures.push({
            index: i,
            logId: log.id,
            type: 'HASH_CHAIN_BROKEN',
            message: `Hash chain broken between ${prevLog.id} and ${log.id}`,
            expectedHash: prevLog.currentHash,
            actualHash: log.previousHash,
          });
        }
      } else {
        if (log.previousHash !== 'GENESIS' && log.previousHash !== null) {
          failures.push({
            index: i,
            logId: log.id,
            type: 'INVALID_GENESIS',
            message: `First record should have GENESIS or null as previousHash, got: ${log.previousHash}`,
            expectedHash: 'GENESIS',
            actualHash: log.previousHash,
          });
        }
      }

      const recalculatedHash = await this.generateHash(log);
      if (recalculatedHash !== log.currentHash) {
        failures.push({
          index: i,
          logId: log.id,
          type: 'HASH_MISMATCH',
          message: `Calculated hash does not match stored hash for log ${log.id}`,
          expectedHash: recalculatedHash,
          actualHash: log.currentHash,
        });
      }

      if (log.signature) {
        const isSignatureValid = await this.verifySignature(log, log.signature);
        if (!isSignatureValid) {
          failures.push({
            index: i,
            logId: log.id,
            type: 'SIGNATURE_INVALID',
            message: `HMAC signature verification failed for log ${log.id}`,
          });
        }
      }
    }

    return {
      success: failures.length === 0,
      failures,
    };
  }
}

