import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { CobPromesaEvidencia, CobPromesaPagador, CobResultadoGestion, CobTipoGestion } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import { CarteraConfigService } from '../config/cartera-config.service';
import { RegistrarGestionDto } from '../dto/registrar-gestion.dto';
import { WebhookService } from '../webhooks/webhook.service';

interface ResultadoRegistroErp {
  gestion: { id: string; fecha: Date };
  promesa: { id: string; fecha_prometida: Date; monto_prometido: number | null } | null;
}

/**
 * Servicio de gestiones unificado para el módulo Cartera Inteligente.
 *
 *  - Fuente erp-novasis     → escribe en `cob_gestion` (queda visible en Mesa de Gestión)
 *  - Fuente cartera-importada → escribe en `cob_gestion_cartera`
 *
 * Cuando `resultado === 'PROMESA_PAGO'` y la fuente es erp-novasis, además
 * crea una promesa formal en `promesas_pago` dentro de una transacción.
 */
@Injectable()
export class GestionesCarteraService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly configService: CarteraConfigService,
    private readonly webhooks: WebhookService,
  ) {}

  async registrar(empresa_id: string, usuario_id: string, cuenta_id: string, dto: RegistrarGestionDto) {
    const config = await this.configService.getOrCreate(empresa_id);

    if (config.fuente_default === 'cartera-importada') {
      const gestion = await this.registrarEnCarteraImportada(empresa_id, usuario_id, cuenta_id, dto);
      this.emitirEvento('gestion.registrada', empresa_id, {
        gestion_id: gestion.id,
        cuenta_id,
        fuente: config.fuente_default,
        tipo_gestion: dto.tipo_gestion,
        resultado: dto.resultado,
        observacion: dto.observacion ?? null,
        proxima_accion: dto.proxima_accion ?? null,
        proxima_fecha: dto.proxima_fecha ?? null,
        usuario_id,
        fecha: gestion.fecha,
      });
      return gestion;
    }

    const resultado = await this.registrarEnErpNovasis(empresa_id, usuario_id, cuenta_id, dto);
    this.emitirEvento('gestion.registrada', empresa_id, {
      gestion_id: resultado.gestion.id,
      cuenta_id,
      fuente: config.fuente_default,
      tipo_gestion: dto.tipo_gestion,
      resultado: dto.resultado,
      observacion: dto.observacion ?? null,
      proxima_accion: dto.proxima_accion ?? null,
      proxima_fecha: dto.proxima_fecha ?? null,
      usuario_id,
      fecha: resultado.gestion.fecha,
      promesa_id: resultado.promesa?.id ?? null,
    });
    if (resultado.promesa) {
      this.emitirEvento('promesa.creada', empresa_id, {
        promesa_id: resultado.promesa.id,
        cliente_id: null,
        factura_cab_id: cuenta_id,
        fecha_prometida: resultado.promesa.fecha_prometida,
        monto_prometido: resultado.promesa.monto_prometido,
        origen: 'cartera-inteligente',
        gestion_id: resultado.gestion.id,
        usuario_id,
      });
    }
    return { ...resultado.gestion, promesa: resultado.promesa };
  }

  async historial(empresa_id: string, cuenta_id: string) {
    const config = await this.configService.getOrCreate(empresa_id);

    if (config.fuente_default === 'cartera-importada') {
      const rows = await this.prisma.cob_gestion_cartera.findMany({
        where: { empresa_id, cartera_importada_documento_id: cuenta_id, anulada: false },
        orderBy: { fecha: 'desc' },
        take: 100,
      });
      return rows.map((r) => ({
        id: r.id,
        fecha: r.fecha,
        tipo_gestion: r.tipo_gestion,
        resultado: r.resultado,
        observacion: r.observacion,
        proxima_accion: r.proxima_accion,
        proxima_fecha: r.proxima_fecha,
        usuario_id: r.usuario_id,
      }));
    }

    const rows = await this.prisma.cob_gestion.findMany({
      where: { empresa_id, factura_cab_id: cuenta_id, anulada: false },
      orderBy: { fecha: 'desc' },
      take: 100,
    });
    return rows.map((r) => ({
      id: r.id,
      fecha: r.fecha,
      tipo_gestion: r.tipo_gestion,
      resultado: r.resultado,
      observacion: r.observacion,
      proxima_accion: r.proxima_accion,
      proxima_fecha: r.proxima_fecha,
      usuario_id: r.usuario_id,
      promesa_creada_id: r.promesa_creada_id,
    }));
  }

  // ─── Helpers ─────────────────────────────────────────────────────────────

  private emitirEvento(
    evento: 'gestion.registrada' | 'promesa.creada',
    empresa_id: string,
    payload: unknown,
  ): void {
    void this.webhooks.publicar(empresa_id, evento, payload).catch(() => {
      /* swallowed: WebhookService ya loguea */
    });
  }

  private async registrarEnErpNovasis(
    empresa_id: string,
    usuario_id: string,
    cuenta_id: string,
    dto: RegistrarGestionDto,
  ): Promise<ResultadoRegistroErp> {
    const factura = await this.prisma.factura_cab.findFirst({
      where: { id: cuenta_id, empresa_id },
      select: { id: true, cliente_id: true },
    });
    if (!factura) throw new NotFoundException('Factura no encontrada para esta empresa');

    const debeCrearPromesa = dto.resultado === 'PROMESA_PAGO' && !!dto.promesa;

    if (!debeCrearPromesa) {
      const gestion = await this.prisma.cob_gestion.create({
        data: {
          empresa_id,
          cliente_id: factura.cliente_id,
          factura_cab_id: factura.id,
          usuario_id,
          tipo_gestion: dto.tipo_gestion as CobTipoGestion,
          resultado: dto.resultado as CobResultadoGestion,
          observacion: dto.observacion ?? null,
          proxima_accion: dto.proxima_accion ?? null,
          proxima_fecha: dto.proxima_fecha ?? null,
        },
      });
      return { gestion: { id: gestion.id, fecha: gestion.fecha }, promesa: null };
    }

    // Transacción: crea gestión + promesa formal y vincula promesa_creada_id
    const promesaDto = dto.promesa!;
    if (Number.isNaN(promesaDto.fecha_prometida.getTime())) {
      throw new BadRequestException('Fecha prometida inválida');
    }
    const fechaRecordatorio = new Date(promesaDto.fecha_prometida);
    fechaRecordatorio.setDate(fechaRecordatorio.getDate() - 1);

    return this.prisma.$transaction(async (tx) => {
      const promesa = await tx.promesas_pago.create({
        data: {
          empresa_id,
          factura_cab_id: factura.id,
          cliente_id: factura.cliente_id,
          fecha_prometida: promesaDto.fecha_prometida,
          monto_prometido: promesaDto.monto_prometido ?? null,
          estado: 'pendiente',
          notas: promesaDto.notas ?? dto.observacion ?? null,
          fecha_recordatorio: fechaRecordatorio,
          creado_por: usuario_id,
          tipo_evidencia: (promesaDto.tipo_evidencia ?? 'VERBAL') as CobPromesaEvidencia,
          pagador_tipo: (promesaDto.pagador_tipo ?? 'CLIENTE') as CobPromesaPagador,
        },
      });

      const gestion = await tx.cob_gestion.create({
        data: {
          empresa_id,
          cliente_id: factura.cliente_id,
          factura_cab_id: factura.id,
          usuario_id,
          tipo_gestion: dto.tipo_gestion as CobTipoGestion,
          resultado: dto.resultado as CobResultadoGestion,
          observacion: dto.observacion ?? null,
          proxima_accion: dto.proxima_accion ?? null,
          proxima_fecha: dto.proxima_fecha ?? promesa.fecha_prometida,
          promesa_creada_id: promesa.id,
        },
      });

      return {
        gestion: { id: gestion.id, fecha: gestion.fecha },
        promesa: {
          id: promesa.id,
          fecha_prometida: promesa.fecha_prometida,
          monto_prometido: promesa.monto_prometido !== null ? Number(promesa.monto_prometido) : null,
        },
      };
    });
  }

  private async registrarEnCarteraImportada(
    empresa_id: string,
    usuario_id: string,
    cuenta_id: string,
    dto: RegistrarGestionDto,
  ) {
    const doc = await this.prisma.cartera_importada_documentos.findFirst({
      where: { id: cuenta_id, empresa_id },
      select: { id: true, cliente_externo_id: true, active: true },
    });
    if (!doc) throw new NotFoundException('Documento de cartera no encontrado');
    if (!doc.active) throw new BadRequestException('Documento inactivo');

    return this.prisma.cob_gestion_cartera.create({
      data: {
        empresa_id,
        cartera_importada_documento_id: doc.id,
        cliente_externo_id: doc.cliente_externo_id,
        usuario_id,
        tipo_gestion: dto.tipo_gestion,
        resultado: dto.resultado,
        observacion: dto.observacion ?? null,
        proxima_accion: dto.proxima_accion ?? null,
        proxima_fecha: dto.proxima_fecha ?? null,
      },
    });
  }
}
