import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Prisma } from '@prisma/client';
import { PrismaService } from 'src/prisma/prisma.service';

export interface IdempotencyRecord {
  endpoint: string;
  response: unknown;
  status_code: number;
}

export interface SaveIdempotencyInput extends IdempotencyRecord {
  key: string;
  empresa_id: string;
  usuario_id: string;
}

/**
 * Persistencia de la idempotencia móvil sobre la tabla `mobile_idempotency`.
 * Una clave (`Idempotency-Key`) cachea la respuesta de un endpoint para que los
 * reintentos del cliente no dupliquen cobros, aperturas ni rendiciones.
 */
@Injectable()
export class IdempotencyStoreService {
  private readonly logger = new Logger(IdempotencyStoreService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly config: ConfigService,
  ) {}

  private getTtlDays(): number {
    const raw = Number(this.config.get('MOBILE_IDEMPOTENCY_TTL_DAYS'));
    return Number.isFinite(raw) && raw > 0 ? raw : 7;
  }

  /**
   * Busca una respuesta cacheada para `key`. Si la clave fue usada en otro
   * endpoint distinto, se trata como miss (clave ambigua → no se reutiliza).
   */
  async find(key: string, endpoint: string): Promise<IdempotencyRecord | null> {
    const row = await this.prisma.mobile_idempotency.findUnique({ where: { key } });
    if (!row || row.endpoint !== endpoint) return null;
    return {
      endpoint: row.endpoint,
      response: row.response,
      status_code: row.status_code,
    };
  }

  /** Persiste la respuesta. Ignora colisiones (reintento concurrente con misma key). */
  async save(input: SaveIdempotencyInput): Promise<void> {
    try {
      await this.prisma.mobile_idempotency.create({
        data: {
          key: input.key,
          empresa_id: input.empresa_id,
          usuario_id: input.usuario_id,
          endpoint: input.endpoint,
          response: (input.response ?? null) as Prisma.InputJsonValue,
          status_code: input.status_code,
        },
      });
    } catch (err) {
      if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2002') {
        // Otra request con la misma key ya persistió: idempotencia respetada.
        return;
      }
      this.logger.error(`No se pudo persistir idempotencia (${input.endpoint})`, err as Error);
    }
  }

  /** Limpia registros más viejos que el TTL configurado. Devuelve cuántos borró. */
  async cleanupExpired(): Promise<number> {
    const cutoff = new Date(Date.now() - this.getTtlDays() * 24 * 60 * 60 * 1000);
    const { count } = await this.prisma.mobile_idempotency.deleteMany({
      where: { created_at: { lt: cutoff } },
    });
    return count;
  }
}
