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

/** Correlativos internos del CRM. No son documentos fiscales. */
export type TipoNumeracionCrm = 'OPP' | 'CTR' | 'TCK';

type ClientePrisma = PrismaService | Prisma.TransactionClient;

/**
 * Numeración interna del CRM: OPP-0000001, CTR-0000001, TCK-0000001.
 *
 * Nada que ver con `numeraciones_documento`, que es de SIFEN y lleva timbrado.
 * Esto es sólo un código legible para que el vendedor pueda decir "la OPP-124".
 *
 * **Se pide siempre dentro de la transacción que crea la entidad.** Si la
 * creación falla, el número se devuelve al pool en vez de dejar un hueco en la
 * secuencia. Por eso `siguiente()` acepta el cliente de transacción.
 */
@Injectable()
export class NumeracionCrmService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Devuelve el próximo código y avanza el contador.
   *
   * El `SELECT ... FOR UPDATE` serializa a los que piden número de la misma
   * empresa y tipo: sin eso, dos vendedores creando una oportunidad al mismo
   * tiempo se llevan el mismo OPP-. Es el mismo patrón que usa presupuestos.
   */
  async siguiente(empresa_id: string, tipo: TipoNumeracionCrm, tx?: Prisma.TransactionClient): Promise<string> {
    const correr = async (c: ClientePrisma) => {
      // La fila nace sola la primera vez. skipDuplicates evita que dos pedidos
      // simultáneos de una empresa nueva choquen en el índice único.
      await c.crm_numeracion.createMany({
        data: [{ empresa_id, tipo, prefijo: tipo }],
        skipDuplicates: true,
      });

      const filas = await c.$queryRaw<Array<{ id: string; prefijo: string; numero_actual: number; relleno: number }>>`
        SELECT id, prefijo, numero_actual, relleno
        FROM crm_numeracion
        WHERE empresa_id = ${empresa_id}::uuid AND tipo = ${tipo}
        FOR UPDATE`;

      const n = filas[0];
      const siguiente = n.numero_actual + 1;

      await c.crm_numeracion.update({
        where: { id: n.id },
        data: { numero_actual: siguiente, updated_at: new Date() },
      });

      return `${n.prefijo}-${String(siguiente).padStart(n.relleno, '0')}`;
    };

    // Con tx, el número vive y muere con la transacción del llamador.
    return tx ? correr(tx) : this.prisma.$transaction((t) => correr(t));
  }
}
