/**
 * Change Log Service
 * ERP Construccion - Modulo Admin
 *
 * Consultas sobre el registro de cambios de auditoria (triggers).
 * @gap GAP-007
 */

import { Repository, DataSource, Between } from 'typeorm';
import { ChangeLog, OperationType } from '../entities/change-log.entity';

// DTOs
export interface ChangeLogFilters {
  tableSchema?: string;
  tableName?: string;
  recordId?: string;
  userId?: string;
  operation?: OperationType;
  fromDate?: Date;
  toDate?: Date;
}

interface PaginationOptions {
  page: number;
  limit: number;
}

interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

export interface RecordHistory {
  changeId: string;
  operation: OperationType;
  changedFields: string[] | null;
  oldData: Record<string, any> | null;
  newData: Record<string, any> | null;
  userId: string | null;
  executedAt: Date;
}

export class ChangeLogService {
  private repository: Repository<ChangeLog>;

  constructor(dataSource: DataSource) {
    this.repository = dataSource.getRepository(ChangeLog);
  }

  // ============================================
  // QUERIES
  // ============================================

  async findAll(
    tenantId: string,
    filters: ChangeLogFilters = {},
    pagination: PaginationOptions = { page: 1, limit: 50 }
  ): Promise<PaginatedResult<ChangeLog>> {
    const queryBuilder = this.repository.createQueryBuilder('log')
      .where('log.tenant_id = :tenantId', { tenantId });

    if (filters.tableSchema) {
      queryBuilder.andWhere('log.table_schema = :schema', { schema: filters.tableSchema });
    }
    if (filters.tableName) {
      queryBuilder.andWhere('log.table_name = :table', { table: filters.tableName });
    }
    if (filters.recordId) {
      queryBuilder.andWhere('log.record_id = :recordId', { recordId: filters.recordId });
    }
    if (filters.userId) {
      queryBuilder.andWhere('log.user_id = :userId', { userId: filters.userId });
    }
    if (filters.operation) {
      queryBuilder.andWhere('log.operation = :operation', { operation: filters.operation });
    }
    if (filters.fromDate) {
      queryBuilder.andWhere('log.executed_at >= :fromDate', { fromDate: filters.fromDate });
    }
    if (filters.toDate) {
      queryBuilder.andWhere('log.executed_at <= :toDate', { toDate: filters.toDate });
    }

    const skip = (pagination.page - 1) * pagination.limit;

    const [data, total] = await queryBuilder
      .orderBy('log.executed_at', 'DESC')
      .skip(skip)
      .take(pagination.limit)
      .getManyAndCount();

    return {
      data,
      total,
      page: pagination.page,
      limit: pagination.limit,
      totalPages: Math.ceil(total / pagination.limit),
    };
  }

  async findById(id: string): Promise<ChangeLog | null> {
    return this.repository.findOne({ where: { id } });
  }

  // ============================================
  // HISTORIAL DE REGISTRO
  // ============================================

  async getRecordHistory(
    tenantId: string,
    tableSchema: string,
    tableName: string,
    recordId: string,
    limit: number = 100
  ): Promise<RecordHistory[]> {
    const logs = await this.repository.find({
      where: {
        tenantId,
        tableSchema,
        tableName,
        recordId,
      },
      order: { executedAt: 'DESC' },
      take: limit,
    });

    return logs.map(log => ({
      changeId: log.id,
      operation: log.operation,
      changedFields: log.changedFields || null,
      oldData: log.oldData || null,
      newData: log.newData || null,
      userId: log.userId || null,
      executedAt: log.executedAt,
    }));
  }

  // ============================================
  // CAMBIOS RECIENTES
  // ============================================

  async getRecentChanges(
    tenantId: string,
    hours: number = 24,
    limit: number = 100
  ): Promise<ChangeLog[]> {
    const fromDate = new Date();
    fromDate.setHours(fromDate.getHours() - hours);

    return this.repository.find({
      where: {
        tenantId,
        executedAt: Between(fromDate, new Date()),
      },
      order: { executedAt: 'DESC' },
      take: limit,
    });
  }

  async getChangesByUser(
    tenantId: string,
    userId: string,
    pagination: PaginationOptions = { page: 1, limit: 50 }
  ): Promise<PaginatedResult<ChangeLog>> {
    return this.findAll(tenantId, { userId }, pagination);
  }

  async getChangesByTable(
    tenantId: string,
    tableSchema: string,
    tableName: string,
    pagination: PaginationOptions = { page: 1, limit: 50 }
  ): Promise<PaginatedResult<ChangeLog>> {
    return this.findAll(tenantId, { tableSchema, tableName }, pagination);
  }

  // ============================================
  // ESTADÍSTICAS
  // ============================================

  async getStatistics(
    tenantId: string,
    fromDate?: Date,
    toDate?: Date
  ): Promise<{
    totalChanges: number;
    inserts: number;
    updates: number;
    deletes: number;
    tableBreakdown: { schema: string; table: string; count: number }[];
  }> {
    const queryBuilder = this.repository.createQueryBuilder('log')
      .where('log.tenant_id = :tenantId', { tenantId });

    if (fromDate) {
      queryBuilder.andWhere('log.executed_at >= :fromDate', { fromDate });
    }
    if (toDate) {
      queryBuilder.andWhere('log.executed_at <= :toDate', { toDate });
    }

    // Counts by operation
    const [inserts, updates, deletes] = await Promise.all([
      queryBuilder.clone().andWhere('log.operation = :op', { op: 'INSERT' }).getCount(),
      queryBuilder.clone().andWhere('log.operation = :op', { op: 'UPDATE' }).getCount(),
      queryBuilder.clone().andWhere('log.operation = :op', { op: 'DELETE' }).getCount(),
    ]);

    const totalChanges = inserts + updates + deletes;

    // Table breakdown
    const tableStats = await this.repository.createQueryBuilder('log')
      .select('log.table_schema', 'schema')
      .addSelect('log.table_name', 'table')
      .addSelect('COUNT(*)', 'count')
      .where('log.tenant_id = :tenantId', { tenantId })
      .groupBy('log.table_schema')
      .addGroupBy('log.table_name')
      .orderBy('count', 'DESC')
      .limit(20)
      .getRawMany();

    return {
      totalChanges,
      inserts,
      updates,
      deletes,
      tableBreakdown: tableStats.map(s => ({
        schema: s.schema,
        table: s.table,
        count: parseInt(s.count),
      })),
    };
  }

  // ============================================
  // MANTENIMIENTO
  // ============================================

  async purgeOldRecords(tenantId: string, daysToKeep: number = 90): Promise<number> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);

    const result = await this.repository.delete({
      tenantId,
      executedAt: Between(new Date(0), cutoffDate),
    });

    return result.affected || 0;
  }
}
