/**
 * DDL 실행 감사 로깅 서비스
 * 모든 DDL 실행을 추적하고 기록
 */

import { query, queryOne } from "../database/db";
import { logger } from "../utils/logger";

export class DDLAuditLogger {
  /**
   * DDL 실행 로그 기록
   */
  static async logDDLExecution(
    userId: string,
    companyCode: string,
    ddlType: "CREATE_TABLE" | "ADD_COLUMN" | "DROP_TABLE" | "DROP_COLUMN",
    tableName: string,
    ddlQuery: string,
    success: boolean,
    error?: string,
    additionalInfo?: Record<string, any>
  ): Promise<void> {
    try {
      // DDL 실행 로그 데이터베이스에 저장
      await query(
        `INSERT INTO ddl_execution_log (
          user_id, 
          company_code, 
          ddl_type, 
          table_name, 
          ddl_query, 
          success, 
          error_message, 
          executed_at
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())`,
        [
          userId,
          companyCode,
          ddlType,
          tableName,
          ddlQuery,
          success,
          error || null,
        ]
      );

      // 추가 로깅 (파일 로그)
      const logData = {
        userId,
        companyCode,
        ddlType,
        tableName,
        success,
        queryLength: ddlQuery.length,
        error: error || null,
        additionalInfo: additionalInfo || null,
        timestamp: new Date().toISOString(),
      };

      if (success) {
        logger.info("DDL 실행 성공", logData);
      } else {
        logger.error("DDL 실행 실패", { ...logData, ddlQuery });
      }

      // 중요한 DDL 실행은 별도 알림 (필요시)
      if (ddlType === "CREATE_TABLE" || ddlType === "DROP_TABLE") {
        logger.warn("중요 DDL 실행", {
          ...logData,
          severity: "HIGH",
          action: "TABLE_STRUCTURE_CHANGE",
        });
      }
    } catch (logError) {
      // 로그 기록 실패는 시스템에 영향을 주지 않도록 처리
      logger.error("DDL 실행 로그 기록 실패:", {
        originalUserId: userId,
        originalDdlType: ddlType,
        originalTableName: tableName,
        originalSuccess: success,
        logError: logError,
      });

      // 로그 기록 실패를 파일 로그로라도 남김
      console.error("CRITICAL: DDL 로그 기록 실패", {
        userId,
        ddlType,
        tableName,
        success,
        logError,
        timestamp: new Date().toISOString(),
      });
    }
  }

  /**
   * DDL 실행 시작 로그
   */
  static async logDDLStart(
    userId: string,
    companyCode: string,
    ddlType: "CREATE_TABLE" | "ADD_COLUMN" | "DROP_TABLE" | "DROP_COLUMN",
    tableName: string,
    requestData: any
  ): Promise<void> {
    logger.info("DDL 실행 시작", {
      userId,
      companyCode,
      ddlType,
      tableName,
      requestData,
      timestamp: new Date().toISOString(),
    });
  }

  /**
   * 최근 DDL 실행 로그 조회
   */
  static async getRecentDDLLogs(
    limit: number = 50,
    userId?: string,
    ddlType?: string
  ): Promise<any[]> {
    try {
      let whereClause = "WHERE 1=1";
      const params: any[] = [];

      if (userId) {
        whereClause += " AND user_id = $" + (params.length + 1);
        params.push(userId);
      }

      if (ddlType) {
        whereClause += " AND ddl_type = $" + (params.length + 1);
        params.push(ddlType);
      }

      const sql = `
        SELECT 
          id,
          user_id,
          company_code,
          ddl_type,
          table_name,
          success,
          error_message,
          executed_at,
          CASE 
            WHEN LENGTH(ddl_query) > 100 THEN SUBSTRING(ddl_query, 1, 100) || '...'
            ELSE ddl_query 
          END as ddl_query_preview
        FROM ddl_execution_log 
        ${whereClause}
        ORDER BY executed_at DESC 
        LIMIT $${params.length + 1}
      `;

      params.push(limit);

      const logs = await query<any>(sql, params);
      return logs;
    } catch (error) {
      logger.error("DDL 로그 조회 실패:", error);
      return [];
    }
  }

  /**
   * DDL 실행 통계 조회
   */
  static async getDDLStatistics(
    fromDate?: Date,
    toDate?: Date
  ): Promise<{
    totalExecutions: number;
    successfulExecutions: number;
    failedExecutions: number;
    byDDLType: Record<string, number>;
    byUser: Record<string, number>;
    recentFailures: any[];
  }> {
    try {
      let dateFilter = "";
      const params: any[] = [];

      if (fromDate) {
        dateFilter += " AND executed_at >= $" + (params.length + 1);
        params.push(fromDate);
      }

      if (toDate) {
        dateFilter += " AND executed_at <= $" + (params.length + 1);
        params.push(toDate);
      }

      // 전체 통계
      const totalStats = await query<any>(
        `SELECT 
          COUNT(*) as total_executions,
          SUM(CASE WHEN success = true THEN 1 ELSE 0 END) as successful_executions,
          SUM(CASE WHEN success = false THEN 1 ELSE 0 END) as failed_executions
        FROM ddl_execution_log 
        WHERE 1=1 ${dateFilter}`,
        params
      );

      // DDL 타입별 통계
      const ddlTypeStats = await query<any>(
        `SELECT ddl_type, COUNT(*) as count
        FROM ddl_execution_log 
        WHERE 1=1 ${dateFilter}
        GROUP BY ddl_type
        ORDER BY count DESC`,
        params
      );

      // 사용자별 통계
      const userStats = await query<any>(
        `SELECT user_id, COUNT(*) as count
        FROM ddl_execution_log 
        WHERE 1=1 ${dateFilter}
        GROUP BY user_id
        ORDER BY count DESC
        LIMIT 10`,
        params
      );

      // 최근 실패 로그
      const recentFailures = await query<any>(
        `SELECT 
          user_id,
          ddl_type,
          table_name,
          error_message,
          executed_at
        FROM ddl_execution_log 
        WHERE success = false ${dateFilter}
        ORDER BY executed_at DESC
        LIMIT 10`,
        params
      );

      const stats = totalStats[0];

      return {
        totalExecutions: parseInt(stats.total_executions) || 0,
        successfulExecutions: parseInt(stats.successful_executions) || 0,
        failedExecutions: parseInt(stats.failed_executions) || 0,
        byDDLType: ddlTypeStats.reduce((acc, row) => {
          acc[row.ddl_type] = parseInt(row.count);
          return acc;
        }, {}),
        byUser: userStats.reduce((acc, row) => {
          acc[row.user_id] = parseInt(row.count);
          return acc;
        }, {}),
        recentFailures,
      };
    } catch (error) {
      logger.error("DDL 통계 조회 실패:", error);
      return {
        totalExecutions: 0,
        successfulExecutions: 0,
        failedExecutions: 0,
        byDDLType: {},
        byUser: {},
        recentFailures: [],
      };
    }
  }

  /**
   * 특정 테이블의 DDL 히스토리 조회
   */
  static async getTableDDLHistory(tableName: string): Promise<any[]> {
    try {
      const history = await query<any>(
        `SELECT 
          id,
          user_id,
          ddl_type,
          ddl_query,
          success,
          error_message,
          executed_at
        FROM ddl_execution_log 
        WHERE table_name = $1
        ORDER BY executed_at DESC
        LIMIT 20`,
        [tableName]
      );

      return history;
    } catch (error) {
      logger.error(`테이블 '${tableName}' DDL 히스토리 조회 실패:`, error);
      return [];
    }
  }

  /**
   * DDL 로그 정리 (오래된 로그 삭제)
   */
  static async cleanupOldLogs(retentionDays: number = 90): Promise<number> {
    try {
      const cutoffDate = new Date();
      cutoffDate.setDate(cutoffDate.getDate() - retentionDays);

      const result = await query(
        `DELETE FROM ddl_execution_log 
         WHERE executed_at < $1`,
        [cutoffDate]
      );

      const deletedCount = result.length;

      logger.info(`DDL 로그 정리 완료: ${deletedCount}개 레코드 삭제`, {
        retentionDays,
        cutoffDate: cutoffDate.toISOString(),
      });

      return deletedCount;
    } catch (error) {
      logger.error("DDL 로그 정리 실패:", error);
      return 0;
    }
  }

  /**
   * 긴급 상황 로그 (시스템 테이블 접근 시도 등)
   */
  static async logSecurityAlert(
    userId: string,
    companyCode: string,
    alertType:
      | "SYSTEM_TABLE_ACCESS"
      | "INVALID_PERMISSION"
      | "SUSPICIOUS_ACTIVITY",
    details: string,
    requestData?: any
  ): Promise<void> {
    try {
      // 보안 알림은 별도의 고급 로깅
      logger.error("DDL 보안 알림", {
        alertType,
        userId,
        companyCode,
        details,
        requestData,
        severity: "CRITICAL",
        timestamp: new Date().toISOString(),
      });

      // 필요시 외부 알림 시스템 연동 (이메일, 슬랙 등)
      // await sendSecurityAlert(alertType, userId, details);
    } catch (error) {
      logger.error("보안 알림 기록 실패:", error);
    }
  }
}
