import { Injectable, Logger } from '@nestjs/common';
import { randomUUID } from 'crypto';
import { AdminOperationLog } from './admin_operation_log.entity';
import {
  CreateLogParams,
  LogQueryParams,
  LogStatistics,
} from './admin_operation_log.service';
import {
  LOG_QUERY_LIMITS,
  USER_QUERY_LIMITS,
  LOG_RETENTION,
  OPERATION_RESULTS,
} from './admin_constants';

@Injectable()
export class AdminOperationLogMemoryService {
  private readonly logger = new Logger(AdminOperationLogMemoryService.name);
  private readonly logs: AdminOperationLog[] = [];

  async createLog(params: CreateLogParams): Promise<AdminOperationLog> {
    const log = Object.assign(new AdminOperationLog(), {
      id: randomUUID(),
      admin_user_id: params.adminUserId,
      admin_username: params.adminUsername,
      operation_type: params.operationType,
      target_type: params.targetType,
      target_id: params.targetId,
      operation_description: params.operationDescription,
      http_method_path: params.httpMethodPath,
      request_params: params.requestParams,
      before_data: params.beforeData,
      after_data: params.afterData,
      operation_result: params.operationResult,
      error_message: params.errorMessage,
      error_code: params.errorCode,
      duration_ms: params.durationMs,
      client_ip: params.clientIp,
      user_agent: params.userAgent,
      request_id: params.requestId,
      context: params.context,
      is_sensitive: params.isSensitive || false,
      affected_records: params.affectedRecords || 0,
      batch_id: params.batchId,
      created_at: new Date(),
    });

    this.logs.push(log);
    this.logger.debug('内存操作日志记录成功', { logId: log.id });
    return log;
  }

  async queryLogs(params: LogQueryParams): Promise<{ logs: AdminOperationLog[]; total: number }> {
    const filteredLogs = this.filterLogs(params).sort(
      (a, b) => b.created_at.getTime() - a.created_at.getTime(),
    );
    const limit = params.limit || LOG_QUERY_LIMITS.DEFAULT_LOG_QUERY_LIMIT;
    const offset = params.offset || 0;

    return {
      logs: filteredLogs.slice(offset, offset + limit),
      total: filteredLogs.length,
    };
  }

  async getLogById(id: string): Promise<AdminOperationLog | null> {
    return this.logs.find(log => log.id === id) || null;
  }

  async getStatistics(startDate?: Date, endDate?: Date): Promise<LogStatistics> {
    const logs = this.filterLogs({ startDate, endDate });
    const successfulOperations = logs.filter(
      log => log.operation_result === OPERATION_RESULTS.SUCCESS,
    ).length;
    const totalDuration = logs.reduce((sum, log) => sum + (log.duration_ms || 0), 0);

    return {
      totalOperations: logs.length,
      successfulOperations,
      failedOperations: logs.length - successfulOperations,
      operationsByType: this.countBy(logs, log => String(log.operation_type)),
      operationsByTarget: this.countBy(logs, log => log.target_type),
      operationsByAdmin: this.countBy(logs, log => log.admin_user_id),
      averageDuration: logs.length > 0 ? totalDuration / logs.length : 0,
      sensitiveOperations: logs.filter(log => log.is_sensitive).length,
      uniqueAdmins: new Set(logs.map(log => log.admin_user_id)).size,
    };
  }

  async cleanupExpiredLogs(daysToKeep: number = LOG_RETENTION.DEFAULT_DAYS): Promise<number> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);

    const originalCount = this.logs.length;
    for (let index = this.logs.length - 1; index >= 0; index--) {
      const log = this.logs[index];
      if (!log.is_sensitive && log.created_at < cutoffDate) {
        this.logs.splice(index, 1);
      }
    }

    return originalCount - this.logs.length;
  }

  async getAdminOperationHistory(
    adminUserId: string,
    limit: number = USER_QUERY_LIMITS.ADMIN_HISTORY_DEFAULT_LIMIT,
  ): Promise<AdminOperationLog[]> {
    return this.logs
      .filter(log => log.admin_user_id === adminUserId)
      .sort((a, b) => b.created_at.getTime() - a.created_at.getTime())
      .slice(0, limit);
  }

  async getSensitiveOperations(
    limit: number = LOG_QUERY_LIMITS.SENSITIVE_LOG_DEFAULT_LIMIT,
    offset: number = 0,
  ): Promise<{ logs: AdminOperationLog[]; total: number }> {
    const sensitiveLogs = this.logs
      .filter(log => log.is_sensitive)
      .sort((a, b) => b.created_at.getTime() - a.created_at.getTime());

    return {
      logs: sensitiveLogs.slice(offset, offset + limit),
      total: sensitiveLogs.length,
    };
  }

  private filterLogs(params: LogQueryParams): AdminOperationLog[] {
    return this.logs.filter(log => {
      if (params.adminUserId && log.admin_user_id !== params.adminUserId) return false;
      if (params.operationType && log.operation_type !== params.operationType) return false;
      if (params.targetType && log.target_type !== params.targetType) return false;
      if (params.operationResult && log.operation_result !== params.operationResult) return false;
      if (params.isSensitive !== undefined && log.is_sensitive !== params.isSensitive) return false;
      if (params.startDate && log.created_at < params.startDate) return false;
      if (params.endDate && log.created_at > params.endDate) return false;
      return true;
    });
  }

  private countBy(
    logs: AdminOperationLog[],
    getKey: (log: AdminOperationLog) => string,
  ): Record<string, number> {
    return logs.reduce((acc, log) => {
      const key = getKey(log) || 'unknown';
      acc[key] = (acc[key] || 0) + 1;
      return acc;
    }, {} as Record<string, number>);
  }
}
