import { Request } from "express";
import { query, pool } from "../database/db";
import logger from "../utils/logger";

export function getClientIp(req: Request): string {
  const forwarded = req.headers["x-forwarded-for"];
  if (forwarded) {
    const first = Array.isArray(forwarded) ? forwarded[0] : forwarded.split(",")[0];
    return first.trim();
  }
  const realIp = req.headers["x-real-ip"];
  if (realIp) {
    return Array.isArray(realIp) ? realIp[0] : realIp;
  }
  return req.ip || req.socket?.remoteAddress || "unknown";
}

export type AuditAction =
  | "CREATE"
  | "UPDATE"
  | "DELETE"
  | "COPY"
  | "LOGIN"
  | "STATUS_CHANGE"
  | "BATCH_CREATE"
  | "BATCH_UPDATE"
  | "BATCH_DELETE";

export type AuditResourceType =
  | "MENU"
  | "SCREEN"
  | "SCREEN_LAYOUT"
  | "FLOW"
  | "FLOW_STEP"
  | "USER"
  | "ROLE"
  | "PERMISSION"
  | "COMPANY"
  | "CODE_CATEGORY"
  | "CODE"
  | "DATA"
  | "TABLE"
  | "NUMBERING_RULE"
  | "BATCH"
  | "NODE_FLOW";

export interface AuditLogParams {
  companyCode: string;
  userId: string;
  userName?: string;
  action: AuditAction;
  resourceType: AuditResourceType;
  resourceId?: string;
  resourceName?: string;
  tableName?: string;
  summary?: string;
  changes?: {
    before?: Record<string, any>;
    after?: Record<string, any>;
    fields?: string[];
  };
  ipAddress?: string;
  requestPath?: string;
}

export interface AuditLogEntry {
  id: number;
  company_code: string;
  company_name: string | null;
  user_id: string;
  user_name: string | null;
  action: string;
  resource_type: string;
  resource_id: string | null;
  resource_name: string | null;
  table_name: string | null;
  summary: string | null;
  changes: any;
  ip_address: string | null;
  request_path: string | null;
  created_at: string;
}

export interface AuditLogFilters {
  companyCode?: string;
  userId?: string;
  resourceType?: string;
  action?: string;
  tableName?: string;
  dateFrom?: string;
  dateTo?: string;
  search?: string;
  page?: number;
  limit?: number;
}

export interface AuditLogStats {
  dailyCounts: Array<{ date: string; count: number }>;
  resourceTypeCounts: Array<{ resource_type: string; count: number }>;
  actionCounts: Array<{ action: string; count: number }>;
  topUsers: Array<{ user_id: string; user_name: string; count: number }>;
}

class AuditLogService {
  /**
   * 감사 로그 1건 기록 (fire-and-forget)
   * 본 작업에 영향을 주지 않도록 에러를 내부에서 처리
   */
  async log(params: AuditLogParams): Promise<void> {
    try {
      logger.info(`[AuditLog] 기록 시도: ${params.resourceType} / ${params.action} / ${params.resourceName || params.resourceId || "N/A"}`);
      await query(
        `INSERT INTO system_audit_log 
          (company_code, user_id, user_name, action, resource_type, 
           resource_id, resource_name, table_name, summary, changes, 
           ip_address, request_path)
         VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
        [
          params.companyCode,
          params.userId,
          params.userName || null,
          params.action,
          params.resourceType,
          params.resourceId || null,
          params.resourceName || null,
          params.tableName || null,
          params.summary || null,
          params.changes ? JSON.stringify(params.changes) : null,
          params.ipAddress || null,
          params.requestPath || null,
        ]
      );
      logger.info(`[AuditLog] 기록 성공: ${params.resourceType} / ${params.action}`);
    } catch (error: any) {
      logger.error(`[AuditLog] 기록 실패: ${params.resourceType} / ${params.action} - ${error?.message}`, { error, params });
    }
  }

  /**
   * 감사 로그 다건 기록 (배치)
   */
  async logBatch(entries: AuditLogParams[]): Promise<void> {
    if (entries.length === 0) return;
    try {
      const values = entries
        .map(
          (_, i) =>
            `($${i * 12 + 1}, $${i * 12 + 2}, $${i * 12 + 3}, $${i * 12 + 4}, $${i * 12 + 5}, $${i * 12 + 6}, $${i * 12 + 7}, $${i * 12 + 8}, $${i * 12 + 9}, $${i * 12 + 10}, $${i * 12 + 11}, $${i * 12 + 12})`
        )
        .join(", ");

      const params = entries.flatMap((e) => [
        e.companyCode,
        e.userId,
        e.userName || null,
        e.action,
        e.resourceType,
        e.resourceId || null,
        e.resourceName || null,
        e.tableName || null,
        e.summary || null,
        e.changes ? JSON.stringify(e.changes) : null,
        e.ipAddress || null,
        e.requestPath || null,
      ]);

      await query(
        `INSERT INTO system_audit_log 
          (company_code, user_id, user_name, action, resource_type, 
           resource_id, resource_name, table_name, summary, changes, 
           ip_address, request_path)
         VALUES ${values}`,
        params
      );
    } catch (error) {
      logger.error("감사 로그 배치 기록 실패 (무시됨)", { error });
    }
  }

  /**
   * 감사 로그 조회 (페이징, 필터)
   */
  async queryLogs(
    filters: AuditLogFilters,
    isSuperAdmin: boolean = false
  ): Promise<{ data: AuditLogEntry[]; total: number }> {
    const conditions: string[] = [];
    const params: any[] = [];
    let paramIndex = 1;

    if (!isSuperAdmin && filters.companyCode) {
      conditions.push(`sal.company_code = $${paramIndex++}`);
      params.push(filters.companyCode);
    } else if (isSuperAdmin && filters.companyCode) {
      conditions.push(`sal.company_code = $${paramIndex++}`);
      params.push(filters.companyCode);
    }

    if (filters.userId) {
      conditions.push(`sal.user_id = $${paramIndex++}`);
      params.push(filters.userId);
    }
    if (filters.resourceType) {
      conditions.push(`sal.resource_type = $${paramIndex++}`);
      params.push(filters.resourceType);
    }
    if (filters.action) {
      conditions.push(`sal.action = $${paramIndex++}`);
      params.push(filters.action);
    }
    if (filters.tableName) {
      conditions.push(`sal.table_name = $${paramIndex++}`);
      params.push(filters.tableName);
    }
    if (filters.dateFrom) {
      conditions.push(`sal.created_at >= $${paramIndex++}::timestamptz`);
      params.push(filters.dateFrom);
    }
    if (filters.dateTo) {
      conditions.push(`sal.created_at <= $${paramIndex++}::timestamptz`);
      params.push(filters.dateTo);
    }
    if (filters.search) {
      conditions.push(
        `(sal.summary ILIKE $${paramIndex} OR sal.resource_name ILIKE $${paramIndex} OR sal.user_name ILIKE $${paramIndex})`
      );
      params.push(`%${filters.search}%`);
      paramIndex++;
    }

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

    const page = filters.page || 1;
    const limit = filters.limit || 50;
    const offset = (page - 1) * limit;

    const countResult = await query<{ count: string }>(
      `SELECT COUNT(*) as count FROM system_audit_log sal ${whereClause}`,
      params
    );
    const total = parseInt(countResult[0].count, 10);

    const data = await query<AuditLogEntry>(
      `SELECT sal.*, ci.company_name
       FROM system_audit_log sal
       LEFT JOIN company_mng ci ON sal.company_code = ci.company_code
       ${whereClause} 
       ORDER BY sal.created_at DESC 
       LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
      [...params, limit, offset]
    );

    const SECURITY_MASK = "(보안 항목 - 값 비공개)";
    const securedTables = ["table_type_columns"];

    if (!isSuperAdmin) {
      for (const entry of data) {
        if (entry.table_name && securedTables.includes(entry.table_name) && entry.changes) {
          const changes = typeof entry.changes === "string" ? JSON.parse(entry.changes) : entry.changes;
          if (changes.before) {
            for (const key of Object.keys(changes.before)) {
              changes.before[key] = SECURITY_MASK;
            }
          }
          if (changes.after) {
            for (const key of Object.keys(changes.after)) {
              changes.after[key] = SECURITY_MASK;
            }
          }
          entry.changes = changes;
        }
      }
    }

    return { data, total };
  }

  /**
   * 통계 조회
   */
  async getStats(
    companyCode?: string,
    days: number = 30
  ): Promise<AuditLogStats> {
    const companyFilter = companyCode
      ? "AND company_code = $1"
      : "";
    const params = companyCode ? [companyCode] : [];

    const dailyCounts = await query<{ date: string; count: number }>(
      `SELECT DATE(created_at) as date, COUNT(*)::int as count
       FROM system_audit_log
       WHERE created_at >= NOW() - INTERVAL '${days} days' ${companyFilter}
       GROUP BY DATE(created_at)
       ORDER BY date DESC`,
      params
    );

    const resourceTypeCounts = await query<{
      resource_type: string;
      count: number;
    }>(
      `SELECT resource_type, COUNT(*)::int as count
       FROM system_audit_log
       WHERE created_at >= NOW() - INTERVAL '${days} days' ${companyFilter}
       GROUP BY resource_type
       ORDER BY count DESC`,
      params
    );

    const actionCounts = await query<{ action: string; count: number }>(
      `SELECT action, COUNT(*)::int as count
       FROM system_audit_log
       WHERE created_at >= NOW() - INTERVAL '${days} days' ${companyFilter}
       GROUP BY action
       ORDER BY count DESC`,
      params
    );

    const topUsers = await query<{
      user_id: string;
      user_name: string;
      count: number;
    }>(
      `SELECT user_id, COALESCE(MAX(user_name), user_id) as user_name, COUNT(*)::int as count
       FROM system_audit_log
       WHERE created_at >= NOW() - INTERVAL '${days} days' ${companyFilter}
       GROUP BY user_id
       ORDER BY count DESC
       LIMIT 10`,
      params
    );

    return { dailyCounts, resourceTypeCounts, actionCounts, topUsers };
  }
}

export const auditLogService = new AuditLogService();
