import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import type { Request, Response } from 'express';
import { from, Observable, of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { IdempotencyStoreService } from './idempotency-store.service';

type RequestWithUser = Request & {
  user?: { id: string; empresa_id: string };
};

/**
 * Idempotencia opt-in vía header `Idempotency-Key`.
 *
 * - Sin header → comportamiento normal (no cachea).
 * - Con header y respuesta ya cacheada para `(method+path)` → devuelve la respuesta
 *   guardada con su status original, sin re-ejecutar el handler.
 * - Con header y sin cache → ejecuta el handler y persiste la respuesta exitosa.
 *
 * Solo se cachean respuestas exitosas: si el handler lanza, el error se propaga
 * y nada queda guardado, de modo que un reintento posterior sí se ejecuta.
 */
@Injectable()
export class IdempotencyInterceptor implements NestInterceptor {
  constructor(private readonly store: IdempotencyStoreService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const req = context.switchToHttp().getRequest<RequestWithUser>();
    const res = context.switchToHttp().getResponse<Response>();

    const rawKey = req.headers['idempotency-key'];
    const key = Array.isArray(rawKey) ? rawKey[0] : rawKey;
    if (!key) {
      return next.handle();
    }

    const endpoint = `${req.method} ${req.route?.path ?? req.path}`;
    const user = req.user;

    return from(this.store.find(key, endpoint)).pipe(
      switchMap((cached) => {
        if (cached) {
          res.status(cached.status_code);
          return of(cached.response);
        }
        return next.handle().pipe(
          tap((data) => {
            if (!user?.empresa_id || !user?.id) return;
            // Normaliza Decimal/BigInt a JSON plano antes de persistir.
            const response = JSON.parse(JSON.stringify(data ?? null));
            void this.store.save({
              key,
              empresa_id: user.empresa_id,
              usuario_id: user.id,
              endpoint,
              response,
              status_code: res.statusCode ?? 201,
            });
          }),
        );
      }),
    );
  }
}
