// 배치 실행 로그 서비스
// 작성일: 2024-12-24

import { query, queryOne } from "../database/db";
import {
  BatchExecutionLog,
  CreateBatchExecutionLogRequest,
  UpdateBatchExecutionLogRequest,
  BatchExecutionLogFilter,
  BatchExecutionLogWithConfig,
} from "../types/batchExecutionLogTypes";
import { ApiResponse } from "../types/batchTypes";

export class BatchExecutionLogService {
  /**
   * 배치 실행 로그 목록 조회 (회사별)
   */
  static async getExecutionLogs(
    filter: BatchExecutionLogFilter = {},
    userCompanyCode?: string
  ): Promise<ApiResponse<BatchExecutionLogWithConfig[]>> {
    try {
      const {
        batch_config_id,
        execution_status,
        start_date,
        end_date,
        page = 1,
        limit = 50,
      } = filter;

      const skip = (page - 1) * limit;
      const take = limit;

      // WHERE 조건 구성
      const whereConditions: string[] = [];
      const params: any[] = [];
      let paramIndex = 1;

      // 회사별 필터링 (최고 관리자가 아닌 경우)
      if (userCompanyCode && userCompanyCode !== "*") {
        whereConditions.push(`bc.company_code = $${paramIndex++}`);
        params.push(userCompanyCode);
      }

      if (batch_config_id) {
        whereConditions.push(`bel.batch_config_id = $${paramIndex++}`);
        params.push(batch_config_id);
      }

      if (execution_status) {
        whereConditions.push(`bel.execution_status = $${paramIndex++}`);
        params.push(execution_status);
      }

      if (start_date) {
        whereConditions.push(`bel.start_time >= $${paramIndex++}`);
        params.push(start_date);
      }

      if (end_date) {
        whereConditions.push(`bel.start_time <= $${paramIndex++}`);
        params.push(end_date);
      }

      const whereClause =
        whereConditions.length > 0
          ? `WHERE ${whereConditions.join(" AND ")}`
          : "";

      // 로그 조회 (batch_config 정보 포함)
      const sql = `
        SELECT 
          bel.*,
          json_build_object(
            'id', bc.id,
            'batch_name', bc.batch_name,
            'description', bc.description,
            'cron_schedule', bc.cron_schedule,
            'is_active', bc.is_active
          ) as batch_config
        FROM batch_execution_logs bel
        LEFT JOIN batch_configs bc ON bel.batch_config_id = bc.id
        ${whereClause}
        ORDER BY bel.start_time DESC
        LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
      `;

      const countSql = `
        SELECT COUNT(*) as count
        FROM batch_execution_logs bel
        ${whereClause}
      `;

      params.push(take, skip);

      const [logs, countResult] = await Promise.all([
        query<any>(sql, params),
        query<{ count: number }>(countSql, params.slice(0, -2)),
      ]);

      const total = parseInt(countResult[0]?.count?.toString() || "0", 10);

      return {
        success: true,
        data: logs as BatchExecutionLogWithConfig[],
        pagination: {
          page,
          limit,
          total,
          totalPages: Math.ceil(total / limit),
        },
      };
    } catch (error) {
      console.error("배치 실행 로그 조회 실패:", error);
      return {
        success: false,
        message: "배치 실행 로그 조회 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "알 수 없는 오류",
      };
    }
  }

  /**
   * 배치 실행 로그 생성
   */
  static async createExecutionLog(
    data: CreateBatchExecutionLogRequest
  ): Promise<ApiResponse<BatchExecutionLog>> {
    try {
      const log = await queryOne<BatchExecutionLog>(
        `INSERT INTO batch_execution_logs (
          batch_config_id, company_code, execution_status, start_time, end_time,
          duration_ms, total_records, success_records, failed_records,
          error_message, error_details, server_name, process_id
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
        RETURNING *`,
        [
          data.batch_config_id,
          data.company_code,
          data.execution_status,
          data.start_time || new Date(),
          data.end_time,
          data.duration_ms,
          data.total_records || 0,
          data.success_records || 0,
          data.failed_records || 0,
          data.error_message,
          data.error_details,
          data.server_name || process.env.HOSTNAME || "unknown",
          data.process_id || process.pid?.toString(),
        ]
      );

      return {
        success: true,
        data: log as BatchExecutionLog,
        message: "배치 실행 로그가 생성되었습니다.",
      };
    } catch (error) {
      console.error("배치 실행 로그 생성 실패:", error);
      return {
        success: false,
        message: "배치 실행 로그 생성 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "알 수 없는 오류",
      };
    }
  }

  /**
   * 배치 실행 로그 업데이트
   */
  static async updateExecutionLog(
    id: number,
    data: UpdateBatchExecutionLogRequest
  ): Promise<ApiResponse<BatchExecutionLog>> {
    try {
      // 동적 UPDATE 쿼리 생성
      const updates: string[] = [];
      const params: any[] = [];
      let paramIndex = 1;

      if (data.execution_status !== undefined) {
        updates.push(`execution_status = $${paramIndex++}`);
        params.push(data.execution_status);
      }
      if (data.end_time !== undefined) {
        updates.push(`end_time = $${paramIndex++}`);
        params.push(data.end_time);
      }
      if (data.duration_ms !== undefined) {
        updates.push(`duration_ms = $${paramIndex++}`);
        params.push(data.duration_ms);
      }
      if (data.total_records !== undefined) {
        updates.push(`total_records = $${paramIndex++}`);
        params.push(data.total_records);
      }
      if (data.success_records !== undefined) {
        updates.push(`success_records = $${paramIndex++}`);
        params.push(data.success_records);
      }
      if (data.failed_records !== undefined) {
        updates.push(`failed_records = $${paramIndex++}`);
        params.push(data.failed_records);
      }
      if (data.error_message !== undefined) {
        updates.push(`error_message = $${paramIndex++}`);
        params.push(data.error_message);
      }
      if (data.error_details !== undefined) {
        updates.push(`error_details = $${paramIndex++}`);
        params.push(data.error_details);
      }

      params.push(id);

      const log = await queryOne<BatchExecutionLog>(
        `UPDATE batch_execution_logs
        SET ${updates.join(", ")}
        WHERE id = $${paramIndex}
        RETURNING *`,
        params
      );

      return {
        success: true,
        data: log as BatchExecutionLog,
        message: "배치 실행 로그가 업데이트되었습니다.",
      };
    } catch (error) {
      console.error("배치 실행 로그 업데이트 실패:", error);
      return {
        success: false,
        message: "배치 실행 로그 업데이트 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "알 수 없는 오류",
      };
    }
  }

  /**
   * 배치 실행 로그 삭제
   */
  static async deleteExecutionLog(id: number): Promise<ApiResponse<void>> {
    try {
      await query(`DELETE FROM batch_execution_logs WHERE id = $1`, [id]);

      return {
        success: true,
        message: "배치 실행 로그가 삭제되었습니다.",
      };
    } catch (error) {
      console.error("배치 실행 로그 삭제 실패:", error);
      return {
        success: false,
        message: "배치 실행 로그 삭제 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "알 수 없는 오류",
      };
    }
  }

  /**
   * 특정 배치의 최신 실행 로그 조회
   */
  static async getLatestExecutionLog(
    batchConfigId: number
  ): Promise<ApiResponse<BatchExecutionLog | null>> {
    try {
      const log = await queryOne<BatchExecutionLog>(
        `SELECT * FROM batch_execution_logs
        WHERE batch_config_id = $1
        ORDER BY start_time DESC
        LIMIT 1`,
        [batchConfigId]
      );

      return {
        success: true,
        data: log || null,
      };
    } catch (error) {
      console.error("최신 배치 실행 로그 조회 실패:", error);
      return {
        success: false,
        message: "최신 배치 실행 로그 조회 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "알 수 없는 오류",
      };
    }
  }

  /**
   * 배치 실행 통계 조회
   */
  static async getExecutionStats(
    batchConfigId?: number,
    startDate?: Date,
    endDate?: Date
  ): Promise<
    ApiResponse<{
      total_executions: number;
      success_count: number;
      failed_count: number;
      success_rate: number;
      average_duration_ms: number;
      total_records_processed: number;
    }>
  > {
    try {
      const whereConditions: string[] = [];
      const params: any[] = [];
      let paramIndex = 1;

      if (batchConfigId) {
        whereConditions.push(`batch_config_id = $${paramIndex++}`);
        params.push(batchConfigId);
      }

      if (startDate) {
        whereConditions.push(`start_time >= $${paramIndex++}`);
        params.push(startDate);
      }

      if (endDate) {
        whereConditions.push(`start_time <= $${paramIndex++}`);
        params.push(endDate);
      }

      const whereClause =
        whereConditions.length > 0
          ? `WHERE ${whereConditions.join(" AND ")}`
          : "";

      const logs = await query<{
        execution_status: string;
        duration_ms: number;
        total_records: number;
      }>(
        `SELECT execution_status, duration_ms, total_records
        FROM batch_execution_logs
        ${whereClause}`,
        params
      );

      const total_executions = logs.length;
      const success_count = logs.filter(
        (log: any) => log.execution_status === "SUCCESS"
      ).length;
      const failed_count = logs.filter(
        (log: any) => log.execution_status === "FAILED"
      ).length;
      const success_rate =
        total_executions > 0 ? (success_count / total_executions) * 100 : 0;

      const validDurations = logs
        .filter((log: any) => log.duration_ms !== null)
        .map((log: any) => log.duration_ms!);
      const average_duration_ms =
        validDurations.length > 0
          ? validDurations.reduce(
              (sum: number, duration: number) => sum + duration,
              0
            ) / validDurations.length
          : 0;

      const total_records_processed = logs
        .filter((log: any) => log.total_records !== null)
        .reduce((sum: number, log: any) => sum + (log.total_records || 0), 0);

      return {
        success: true,
        data: {
          total_executions,
          success_count,
          failed_count,
          success_rate,
          average_duration_ms,
          total_records_processed,
        },
      };
    } catch (error) {
      console.error("배치 실행 통계 조회 실패:", error);
      return {
        success: false,
        message: "배치 실행 통계 조회 중 오류가 발생했습니다.",
        error: error instanceof Error ? error.message : "알 수 없는 오류",
      };
    }
  }
}
