/**
 * @ai-summary Audit log data access layer
 * @ai-context Provides parameterized SQL queries for audit log operations
 */

import { Pool } from 'pg';
import {
  AuditLogEntry,
  CreateAuditLogInput,
  AuditLogFilters,
  AuditLogPagination,
  AuditLogSearchResult,
} from '../domain/audit-log.types';
import { logger } from '../../../core/logging/logger';

// Maximum records for CSV export to prevent memory exhaustion
const MAX_EXPORT_RECORDS = 5000;

export class AuditLogRepository {
  constructor(private pool: Pool) {}

  /**
   * Escape LIKE special characters to prevent pattern injection
   */
  private escapeLikePattern(pattern: string): string {
    return pattern.replace(/[%_\\]/g, (match) => `\\${match}`);
  }

  /**
   * Build WHERE clause from filters (shared logic for search and export)
   */
  private buildWhereClause(filters: AuditLogFilters): {
    whereClause: string;
    params: unknown[];
    nextParamIndex: number;
  } {
    const conditions: string[] = [];
    const params: unknown[] = [];
    let paramIndex = 1;

    if (filters.search) {
      conditions.push(`al.action ILIKE $${paramIndex}`);
      params.push(`%${this.escapeLikePattern(filters.search)}%`);
      paramIndex++;
    }

    if (filters.category) {
      conditions.push(`al.category = $${paramIndex}`);
      params.push(filters.category);
      paramIndex++;
    }

    if (filters.severity) {
      conditions.push(`al.severity = $${paramIndex}`);
      params.push(filters.severity);
      paramIndex++;
    }

    if (filters.userId) {
      conditions.push(`al.user_id = $${paramIndex}`);
      params.push(filters.userId);
      paramIndex++;
    }

    if (filters.startDate) {
      conditions.push(`al.created_at >= $${paramIndex}`);
      params.push(filters.startDate);
      paramIndex++;
    }

    if (filters.endDate) {
      conditions.push(`al.created_at <= $${paramIndex}`);
      params.push(filters.endDate);
      paramIndex++;
    }

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

    return { whereClause, params, nextParamIndex: paramIndex };
  }

  /**
   * Create a new audit log entry
   */
  async create(input: CreateAuditLogInput): Promise<AuditLogEntry> {
    const query = `
      INSERT INTO audit_logs (category, severity, user_id, action, resource_type, resource_id, details)
      VALUES ($1, $2, $3, $4, $5, $6, $7)
      RETURNING id, category, severity, user_id, action, resource_type, resource_id, details, created_at,
                NULL::text as user_email
    `;

    try {
      const result = await this.pool.query(query, [
        input.category,
        input.severity,
        input.userId || null,
        input.action,
        input.resourceType || null,
        input.resourceId || null,
        input.details ? JSON.stringify(input.details) : null,
      ]);

      return this.mapRow(result.rows[0]);
    } catch (error) {
      logger.error('Error creating audit log', { error, input });
      throw error;
    }
  }

  /**
   * Search audit logs with filters and pagination
   */
  async search(
    filters: AuditLogFilters,
    pagination: AuditLogPagination
  ): Promise<AuditLogSearchResult> {
    const { whereClause, params, nextParamIndex } = this.buildWhereClause(filters);

    // Count query
    const countQuery = `SELECT COUNT(*) as total FROM audit_logs al ${whereClause}`;

    // Data query with pagination - LEFT JOIN to get user email
    const dataQuery = `
      SELECT al.id, al.category, al.severity, al.user_id, al.action,
             al.resource_type, al.resource_id, al.details, al.created_at,
             up.email as user_email
      FROM audit_logs al
      LEFT JOIN user_profiles up ON al.user_id = up.auth0_sub
      ${whereClause}
      ORDER BY al.created_at DESC
      LIMIT $${nextParamIndex} OFFSET $${nextParamIndex + 1}
    `;

    try {
      const [countResult, dataResult] = await Promise.all([
        this.pool.query(countQuery, params),
        this.pool.query(dataQuery, [...params, pagination.limit, pagination.offset]),
      ]);

      const total = parseInt(countResult.rows[0].total, 10);
      const logs = dataResult.rows.map((row) => this.mapRow(row));

      return {
        logs,
        total,
        limit: pagination.limit,
        offset: pagination.offset,
      };
    } catch (error) {
      logger.error('Error searching audit logs', { error, filters, pagination });
      throw error;
    }
  }

  /**
   * Get all logs matching filters for CSV export (limited to prevent memory exhaustion)
   */
  async getForExport(filters: AuditLogFilters): Promise<{ logs: AuditLogEntry[]; truncated: boolean }> {
    const { whereClause, params } = this.buildWhereClause(filters);

    // First, count total matching records
    const countQuery = `SELECT COUNT(*) as total FROM audit_logs al ${whereClause}`;
    const countResult = await this.pool.query(countQuery, params);
    const totalCount = parseInt(countResult.rows[0].total, 10);
    const truncated = totalCount > MAX_EXPORT_RECORDS;

    const query = `
      SELECT al.id, al.category, al.severity, al.user_id, al.action,
             al.resource_type, al.resource_id, al.details, al.created_at,
             up.email as user_email
      FROM audit_logs al
      LEFT JOIN user_profiles up ON al.user_id = up.auth0_sub
      ${whereClause}
      ORDER BY al.created_at DESC
      LIMIT ${MAX_EXPORT_RECORDS}
    `;

    try {
      const result = await this.pool.query(query, params);
      const logs = result.rows.map((row) => this.mapRow(row));

      if (truncated) {
        logger.warn('Audit log export truncated', {
          totalCount,
          exportedCount: logs.length,
          limit: MAX_EXPORT_RECORDS,
        });
      }

      return { logs, truncated };
    } catch (error) {
      logger.error('Error exporting audit logs', { error, filters });
      throw error;
    }
  }

  /**
   * Delete logs older than specified days (retention cleanup)
   */
  async cleanup(olderThanDays: number): Promise<number> {
    const query = `
      DELETE FROM audit_logs
      WHERE created_at < NOW() - INTERVAL '1 day' * $1
    `;

    try {
      const result = await this.pool.query(query, [olderThanDays]);
      const deletedCount = result.rowCount || 0;

      logger.info('Audit log cleanup completed', {
        olderThanDays,
        deletedCount,
      });

      return deletedCount;
    } catch (error) {
      logger.error('Error cleaning up audit logs', { error, olderThanDays });
      throw error;
    }
  }

  /**
   * Map database row to AuditLogEntry (snake_case to camelCase)
   */
  private mapRow(row: Record<string, unknown>): AuditLogEntry {
    return {
      id: row.id as string,
      category: row.category as AuditLogEntry['category'],
      severity: row.severity as AuditLogEntry['severity'],
      userId: row.user_id as string | null,
      userEmail: (row.user_email as string | null) || null,
      action: row.action as string,
      resourceType: row.resource_type as string | null,
      resourceId: row.resource_id as string | null,
      details: row.details as Record<string, unknown> | null,
      createdAt: new Date(row.created_at as string),
    };
  }
}
