import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { ComprasService, CuotaPersist } from 'src/compras/compras.service';
import { PrismaService } from 'src/prisma/prisma.service';
import { GenerarCxPDto } from '../dto/importaciones.dto';

type Tx = Prisma.TransactionClient;

@Injectable()
export class ImportacionesCxPService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly compras: ComprasService,
  ) {}

  async generarCxP(
    embarqueId: string,
    componenteId: string,
    dto: GenerarCxPDto,
    empresaId: string,
    userId?: string,
  ) {
    return this.prisma.$transaction(async (tx) => {
      const componente = await tx.imp_componentes_costo.findFirst({
        where: { id: componenteId, embarque_id: embarqueId, empresa_id: empresaId, activo: true },
      });
      if (!componente) throw new NotFoundException('Componente de costo no encontrado');
      if (componente.cuenta_pagar_id) {
        throw new BadRequestException('El componente ya tiene una CxP asociada');
      }

      const proveedorId = dto.proveedor_id || componente.proveedor_id;
      if (!proveedorId) {
        throw new BadRequestException('Debe especificar proveedor (en el componente o en el body)');
      }

      const proveedor = await tx.proveedores.findFirst({
        where: { id: proveedorId, empresa_id: empresaId, activo: true },
      });
      if (!proveedor) throw new BadRequestException('Proveedor inválido para la empresa');

      const totalGs = Number(componente.importe_gs ?? 0);
      if (totalGs <= 0) {
        throw new BadRequestException('El componente no tiene importe en Gs. para generar CxP');
      }

      const fechaEmision = new Date(dto.fecha_emision);
      if (Number.isNaN(fechaEmision.getTime())) {
        throw new BadRequestException('fecha_emision inválida');
      }

      const cuotasInput = (dto.cuotas ?? []).map((c) => ({
        numero_cuota: c.numero_cuota,
        fecha_vencimiento: c.fecha_vencimiento,
        monto: c.monto,
        observaciones: c.observaciones ?? null,
      }));

      const isCredito = (dto.plazo_credito_dias ?? 0) > 0 || cuotasInput.length > 0;
      const cuotas: CuotaPersist[] = this.compras.buildCuotasPersist({
        isCredito,
        creditoRequiereCuotas: false,
        cuotas: cuotasInput as any,
        totalCompra: totalGs,
        fechaEmision,
        plazoCreditoDias: this.compras.resolvePlazoCreditoDias(dto.plazo_credito_dias),
      });

      const monedaIdGs = await this.resolveMonedaGs(tx, empresaId);

      const creadas = await this.compras.persistCuentasPagar(tx, {
        empresaId,
        compraId: null,
        proveedorId,
        monedaId: monedaIdGs,
        fechaEmision,
        cuotas,
        embarqueId,
        origenTipo: 'importacion',
        origenId: componenteId,
      });

      // Vincular la primera cuota al componente para mostrar referencia rápida en UI.
      const primeraCuotaId = creadas[0]?.id ?? null;
      await tx.imp_componentes_costo.update({
        where: { id: componenteId },
        data: { cuenta_pagar_id: primeraCuotaId, updated_at: new Date() },
      });

      // Persistir número de factura informado (no se setea al cargar el componente).
      if (dto.numero_factura?.trim()) {
        await tx.imp_componentes_costo.update({
          where: { id: componenteId },
          data: { numero_factura: dto.numero_factura.trim() },
        });
      }

      await this.registerBitacora(tx, {
        embarqueId,
        empresaId,
        evento: 'CXP_GENERADA',
        detalle: {
          componente_id: componenteId,
          proveedor_id: proveedorId,
          total_gs: totalGs,
          cuotas: creadas.length,
          numero_factura: dto.numero_factura?.trim() || null,
        },
        usuarioId: userId,
      });

      return {
        message: 'CxP generada',
        cuotas: creadas.length,
        cuenta_pagar_principal_id: primeraCuotaId,
      };
    });
  }

  private async resolveMonedaGs(tx: Tx, empresaId: string): Promise<string | null> {
    // Para CxP de importación, asumimos que el importe_gs ya está convertido a Gs.
    // Buscamos la moneda PYG/GS disponible para la empresa para etiquetar la fila.
    const moneda = await tx.moneda.findFirst({
      where: {
        OR: [
          { codigo: { in: ['GS', 'PYG'] } },
          { descripcion: { contains: 'guaran', mode: 'insensitive' } },
        ],
        empresa_monedas: { some: { empresa_id: empresaId } },
      },
      select: { id: true },
    });
    return moneda?.id ?? null;
  }

  private async registerBitacora(
    tx: Tx,
    params: {
      embarqueId: string;
      empresaId: string;
      evento: string;
      detalle?: Record<string, unknown> | null;
      usuarioId?: string | null;
    },
  ) {
    await tx.imp_embarques_bitacora.create({
      data: {
        embarque_id: params.embarqueId,
        empresa_id: params.empresaId,
        evento: params.evento,
        detalle: (params.detalle ?? Prisma.JsonNull) as Prisma.InputJsonValue,
        usuario_id: params.usuarioId ?? null,
      },
    });
  }
}
