import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable, tap } from 'rxjs';
import { AuditService } from './audit.service';
import { AUDIT_ENTITY_KEY } from './auditable.decorator';

@Injectable()
export class AuditInterceptor implements NestInterceptor {
  constructor(
    private readonly auditService: AuditService,
    private readonly reflector: Reflector,
  ) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const entityType = this.reflector.get<string>(AUDIT_ENTITY_KEY, context.getHandler());
    if (!entityType) return next.handle();

    const req = context.switchToHttp().getRequest();
    const method = req.method as string;

    // Solo auditar mutaciones
    const mutationMethods = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
    if (!mutationMethods.has(method)) return next.handle();

    const action = this.resolveAction(
      method,
      String(req.route?.path || req.originalUrl || req.url || ''),
    );
    const user = req.user;
    const ip = (req.ip as string) || (req.headers?.['x-forwarded-for'] as string) || '';
    const userAgent = (req.headers?.['user-agent'] as string) || '';
    const requestEntityId = (req.params?.id as string) || (req.params?.suscripcionId as string) || undefined;
    const terminalId = (req.headers?.['x-terminal-id'] as string) || undefined;
    const cajaId = (req.headers?.['x-caja-id'] as string) || undefined;
    const sesionCajaId = (req.headers?.['x-sesion-caja-id'] as string) || undefined;

    return next.handle().pipe(
      tap((response) => {
        // Fire-and-forget: no bloqueamos la respuesta
        const resData = response && typeof response === 'object' ? response : undefined;
        const entityId = this.resolveEntityId(requestEntityId, response);
        void this.auditService.log({
          empresa_id: user?.empresa_id,
          user_id: user?.id,
          action,
          entity_type: entityType,
          entity_id: entityId,
          new_value: resData as Record<string, unknown> | undefined,
          ip_address: ip,
          user_agent: userAgent,
          descripcion: `${action} ${entityType}${entityId ? ` (${entityId})` : ''}`,
          terminal_id: terminalId,
          caja_id: cajaId,
          sesion_caja_id: sesionCajaId,
        });
      }),
    );
  }

  private resolveEntityId(requestEntityId: unknown, response: unknown): string | undefined {
    const normalizeId = (value: unknown): string | undefined => {
      if (typeof value !== 'string' && typeof value !== 'number') return undefined;
      const normalized = String(value).trim();
      return normalized || undefined;
    };

    const fromRequest = normalizeId(requestEntityId);
    if (fromRequest) return fromRequest;
    if (!response || typeof response !== 'object' || Array.isArray(response)) return undefined;

    const record = response as Record<string, unknown>;
    const nestedData =
      record.data && typeof record.data === 'object' && !Array.isArray(record.data)
        ? (record.data as Record<string, unknown>)
        : undefined;
    const candidates = [
      record.id,
      record.entity_id,
      record.compra_id,
      record.gasto_id,
      record.orden_compra_id,
      record.recepcion_id,
      nestedData?.id,
      nestedData?.entity_id,
    ];

    for (const candidate of candidates) {
      const resolved = normalizeId(candidate);
      if (resolved) return resolved;
    }
    return undefined;
  }

  private resolveAction(method: string, route = ''): string {
    switch (method) {
      case 'POST':
        return 'CREATE';
      case 'PUT':
        return 'UPDATE';
      case 'PATCH':
        return /(?:anular|aprobar|cancelar|activar|desactivar|estado|status)/i.test(route)
          ? 'CHANGE_STATUS'
          : 'UPDATE';
      case 'DELETE':
        return 'DELETE';
      default:
        return method;
    }
  }
}
