import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';

/**
 * Reglas de tesorería — qué cuenta de tesorería usa una operación automática
 * cuando el usuario no eligió ninguna. Se busca primero la regla del medio de
 * pago y, si no hay, la default (medio_pago_id NULL).
 *
 * Solo estos tipos tienen efecto (los lee el flujo indicado):
 *   COBRO_CLIENTE   — recibos de cobranza (recibos.service)
 *   PAGO_PROVEEDOR  — órdenes de pago (pagos-proveedor.service)
 *   PAGO_GASTO      — gastos de contado (gastos-tesoreria.service)
 *
 * La pantalla ofrecía además COBRO_DEPOSITADO, CHEQUE_DEBITADO, COBRO_EFECTIVO
 * y TRANSFERENCIA_ENTRADA/SALIDA, que ningún flujo lee: se guardaban sin hacer
 * nada. `modo` y `tipo_tesoreria` tampoco los usa nadie; se conservan las
 * columnas para no romper datos viejos.
 */
export const TIPOS_REGLA_TESORERIA = ['COBRO_CLIENTE', 'PAGO_PROVEEDOR', 'PAGO_GASTO'] as const;

@Injectable()
export class TesReglasService {
  constructor(private readonly prisma: PrismaService) {}

  private readonly cuentaSelect = { select: { id: true, nombre: true, moneda: true, tipo: true } };
  private readonly medioSelect = { select: { id: true, descripcion: true } };

  async findAll(empresaId: string) {
    return this.prisma.tes_reglas.findMany({
      where: { empresa_id: empresaId },
      include: { cuenta: this.cuentaSelect, medio_pago: this.medioSelect },
      orderBy: [{ tipo_operacion: 'asc' }, { medio_pago_id: 'asc' }],
    });
  }

  /** Regla por tipo de operación y, opcionalmente, medio de pago (null = regla default). */
  async getRegla(empresaId: string, tipoOperacion: string, medioPagoId: string | null = null) {
    return this.prisma.tes_reglas.findFirst({
      where: { empresa_id: empresaId, tipo_operacion: tipoOperacion, medio_pago_id: medioPagoId },
      include: { cuenta: this.cuentaSelect, medio_pago: this.medioSelect },
    });
  }

  async upsert(
    empresaId: string,
    data: {
      tipo_operacion: string;
      medio_pago_id?: string | null;
      tipo_tesoreria?: string | null;
      cuenta_id?: string | null;
      modo?: string;
      activo?: boolean;
    },
  ) {
    if (!(TIPOS_REGLA_TESORERIA as readonly string[]).includes(data.tipo_operacion)) {
      throw new BadRequestException(
        `El tipo de regla "${data.tipo_operacion}" no tiene efecto. Usá Cobro de clientes, Pago a proveedor o Pago de gasto.`,
      );
    }
    const medioPagoId = data.medio_pago_id ?? null;
    const existente = await this.prisma.tes_reglas.findFirst({
      where: { empresa_id: empresaId, tipo_operacion: data.tipo_operacion, medio_pago_id: medioPagoId },
      select: { id: true },
    });
    if (existente) {
      return this.prisma.tes_reglas.update({
        where: { id: existente.id },
        data: {
          ...(data.cuenta_id !== undefined ? { cuenta_id: data.cuenta_id } : {}),
          ...(data.tipo_tesoreria !== undefined ? { tipo_tesoreria: data.tipo_tesoreria } : {}),
          ...(data.modo ? { modo: data.modo } : {}),
          ...(data.activo !== undefined ? { activo: data.activo } : {}),
          updated_at: new Date(),
        },
        include: { cuenta: this.cuentaSelect, medio_pago: this.medioSelect },
      });
    }
    return this.prisma.tes_reglas.create({
      data: {
        empresa_id: empresaId,
        tipo_operacion: data.tipo_operacion,
        medio_pago_id: medioPagoId,
        tipo_tesoreria: data.tipo_tesoreria ?? null,
        cuenta_id: data.cuenta_id ?? null,
        modo: data.modo ?? 'CONFIRMACION',
        activo: data.activo ?? true,
      },
      include: { cuenta: this.cuentaSelect, medio_pago: this.medioSelect },
    });
  }

  async remove(empresaId: string, id: string) {
    const regla = await this.prisma.tes_reglas.findFirst({ where: { id, empresa_id: empresaId } });
    if (!regla) throw new NotFoundException('Regla no encontrada');
    await this.prisma.tes_reglas.delete({ where: { id } });
    return { ok: true };
  }

  /** Devuelve el cuenta_id sugerido por la regla (default), o null si no hay regla activa */
  async getCuentaSugerida(empresaId: string, tipoOperacion: string): Promise<string | null> {
    const regla = await this.prisma.tes_reglas.findFirst({
      where: { empresa_id: empresaId, tipo_operacion: tipoOperacion, medio_pago_id: null },
      select: { cuenta_id: true, activo: true },
    });
    return regla?.activo && regla.cuenta_id ? regla.cuenta_id : null;
  }
}
