import { ForbiddenException, Injectable, NotFoundException, NotImplementedException } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import type {
  CuentaDetalle,
  CuentaPendiente,
  CuentasFilter,
  Cuota,
  IFuenteCartera,
  ModoOperativo,
  OrigenCartera,
  Recibo,
  ReciboCobro,
  RegistrarCobroInput,
} from './i-fuente-cartera.interface';

/**
 * Fuente que lee de la tabla cartera_importada_documentos.
 * Usada por tenants que cargan su cartera vía Excel (clientes sin ERP).
 *
 * No tiene granularidad de cuotas (el Excel solo trae "saldo pendiente" del
 * documento completo). Eso se puede agregar más adelante con un sheet adicional.
 */
@Injectable()
export class CarteraImportadaFuente implements IFuenteCartera {
  constructor(private readonly prisma: PrismaService) {}

  getOrigen(): OrigenCartera {
    return 'cartera-importada';
  }

  private modo: ModoOperativo = 'operacional';

  setModo(modo: ModoOperativo): void {
    this.modo = modo;
  }

  getModo(): ModoOperativo {
    return this.modo;
  }

  async listarCuentasPendientes(filtros: CuentasFilter): Promise<CuentaPendiente[]> {
    const page = filtros.page && filtros.page > 0 ? filtros.page : 1;
    const pageSize = filtros.page_size && filtros.page_size > 0 ? filtros.page_size : 50;
    const skip = (page - 1) * pageSize;

    const where: Prisma.cartera_importada_documentosWhereInput = {
      empresa_id: filtros.empresa_id,
      active: true,
      saldo_pendiente: { gt: 0 },
    };
    if (filtros.cliente_id) {
      where.cliente_externo_id = filtros.cliente_id;
    }
    if (filtros.vencido_desde) {
      where.fecha_vencimiento = { lte: filtros.vencido_desde };
    }

    const rows = await this.prisma.cartera_importada_documentos.findMany({
      where,
      orderBy: [{ fecha_vencimiento: 'asc' }, { fecha_emision: 'asc' }],
      take: pageSize,
      skip,
    });

    const ahora = Date.now();
    return rows.map((r) => ({
      id: r.id,
      cliente_id: r.cliente_externo_id,
      cliente_nombre: r.cliente_nombre,
      documento_numero: r.documento_numero,
      monto_total: Number(r.monto_total),
      saldo_pendiente: Number(r.saldo_pendiente),
      fecha_emision: r.fecha_emision,
      fecha_vencimiento: r.fecha_vencimiento,
      dias_mora: r.fecha_vencimiento
        ? Math.max(0, Math.floor((ahora - r.fecha_vencimiento.getTime()) / 86_400_000))
        : 0,
      moneda_codigo: r.moneda_codigo,
    }));
  }

  async obtenerCuenta(id: string): Promise<CuentaDetalle> {
    const doc = await this.prisma.cartera_importada_documentos.findUnique({ where: { id } });
    if (!doc) throw new NotFoundException(`Documento ${id} no encontrado`);
    return {
      id: doc.id,
      cliente_id: doc.cliente_externo_id,
      cliente_nombre: doc.cliente_nombre,
      documento_numero: doc.documento_numero,
      monto_total: Number(doc.monto_total),
      saldo_pendiente: Number(doc.saldo_pendiente),
      fecha_emision: doc.fecha_emision,
      fecha_vencimiento: doc.fecha_vencimiento,
      dias_mora: doc.fecha_vencimiento
        ? Math.max(0, Math.floor((Date.now() - doc.fecha_vencimiento.getTime()) / 86_400_000))
        : 0,
      moneda_codigo: doc.moneda_codigo,
      cuotas: [
        {
          id: doc.id,
          numero: 1,
          monto: Number(doc.monto_total),
          saldo: Number(doc.saldo_pendiente),
          fecha_vencimiento: doc.fecha_vencimiento ?? doc.fecha_emision,
          estado: Number(doc.saldo_pendiente) > 0 ? 'pendiente' : 'pagada',
        },
      ],
    };
  }

  async listarCuotas(cuentaId: string): Promise<Cuota[]> {
    const det = await this.obtenerCuenta(cuentaId);
    return det.cuotas;
  }

  async obtenerSaldoVencido(_clienteId: string): Promise<number> {
    throw new NotImplementedException('Próximo sprint');
  }

  async obtenerHistorialCobros(_clienteId: string): Promise<Recibo[]> {
    throw new NotImplementedException('Próximo sprint');
  }

  async registrarCobro(_input: RegistrarCobroInput): Promise<ReciboCobro> {
    if (this.modo === 'co-pilot') {
      throw new ForbiddenException(
        'Modo co-pilot: el cobro se registra en el sistema del cliente.',
      );
    }
    throw new NotImplementedException('Próximo sprint — registrar cobro sobre cartera importada');
  }
}
