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

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

  private fmt(date: Date | string | null | undefined): string | null {
    if (!date) return null;
    return new Date(date).toISOString().slice(0, 19);
  }

  private fmtDate(date: Date | string | null | undefined): string | null {
    if (!date) return null;
    return new Date(date).toISOString().slice(0, 10);
  }

  private num(value: unknown): number | null {
    if (value === null || value === undefined) return null;
    const n = typeof value === 'number' ? value : Number(value);
    return Number.isFinite(n) ? n : null;
  }

  async buildPayload(remisionId: string, empresaId: string): Promise<Record<string, unknown>> {
    const rem = await this.prisma.nota_remision_cab.findFirst({
      where: { id: remisionId, empresa_id: empresaId },
      include: {
        clientes: {
          include: {
            personas: {
              include: {
                naturaleza_receptor: true,
                tipo_contribuyente: true,
                tipo_documento_identidad: true,
                ciudades: {
                  include: {
                    distritos: { include: { departamentos: true } },
                  },
                },
              },
            },
            tipo_operacion: true,
          },
        },
        motivo_emision_remision: true,
        responsable_emision: true,
        responsable_flete: true,
        tipo_transporte: true,
        modalidad_transporte: true,
        condicion_negociacion: true,
        transportistas: {
          include: {
            personas: {
              include: {
                naturaleza_receptor: true,
                tipo_documento_identidad: true,
                paises: true,
              },
            },
          },
        },
        choferes: {
          include: {
            personas: true,
          },
        },
        vehiculos: true,
        agentes_transporte: {
          include: { personas: true },
        },
        ciudades_nota_remision_cab_ciudad_salida_idTociudades: {
          include: { distritos: { include: { departamentos: true } } },
        },
        ciudades_nota_remision_cab_ciudad_entrega_idTociudades: {
          include: { distritos: { include: { departamentos: true } } },
        },
        nota_remision_det: {
          include: {
            productos: {
              include: { unidades_de_medida: true },
            },
          },
        },
        // Facturas electrónicas asociadas (1 remisión → N facturas).
        nota_remision_factura: {
          include: { factura_cab: { select: { cdc: true } } },
        },
      },
    });

    if (!rem) throw new BadRequestException('Nota de remisión no encontrada');

    const sucursal =
      rem.empresa_id && rem.dest
        ? await this.prisma.empresas_sucursales.findFirst({
            where: { empresa_id: rem.empresa_id, punto_establecimiento: rem.dest, active: true },
            select: { id: true },
          })
        : null;

    const persona = rem.clientes?.personas;
    const personaCiudad = persona?.ciudades;

    const ciudadSal = (rem as any).ciudades_nota_remision_cab_ciudad_salida_idTociudades;
    const ciudadEnt = (rem as any).ciudades_nota_remision_cab_ciudad_entrega_idTociudades;

    const transPersona = rem.transportistas?.personas;
    const choferPersona = rem.choferes?.personas;
    const agente = rem.agentes_transporte;
    const agentePersona = agente?.personas;
    const vehiculo = rem.vehiculos;
    const motivo = rem.motivo_emision_remision;
    const modalidad = rem.modalidad_transporte;

    const payload: Record<string, unknown> = {
      empresa_id: rem.empresa_id ?? undefined,
      sucursal_id: sucursal?.id ?? null,
      cdc: rem.cdc ?? null,
      iTiDE: '7',
      dInfoFisc: rem.dinfofisc ?? 'según el Art. 3 Inc. 7 de la Resolución general Nro. 41/2014',
      dInfoEmi: rem.dinfoemi ?? null,
      dInfAdic: rem.dinfadic ?? null,
      dEst: rem.dest,
      dPunExp: rem.dpunexp,
      dNumDoc: rem.dnumdoc,
      dSerieNum: null,
      dFeEmiDE: rem.dfeemide ? this.fmt(rem.dfeemide) : null,

      // Receptor (cliente destino de la mercancía)
      iNatRec: this.num(persona?.naturaleza_receptor?.codigo) ?? null,
      iTiOpe: this.num(rem.clientes?.tipo_operacion?.codigo),
      cPaisRec: 'PRY',
      iTiContRec: this.num(persona?.tipo_contribuyente?.codigo) ?? null,
      dRucRec: persona?.ruc ?? null,
      dDVRec: this.num(persona?.dv),
      iTipIDRec: persona?.tipo_documento_identidad?.codigo ?? null,
      dDTipIDRec: persona?.tipo_documento_identidad?.descripcion ?? null,
      dNumIDRec: persona?.nro_documento ?? null,
      dNomRec: persona?.razon_social ?? null,
      dNumCasRec: persona?.nro_casa != null ? (this.num(persona.nro_casa) ?? 0) : 0,
      // dDirRec es obligatorio: si el cliente no tiene dirección fiscal cargada,
      // se usa la dirección de entrega de la remisión (DATOS DE ENTREGA).
      dDirRec: persona?.direccion ?? rem.ddirlocent ?? null,
      cDepRec: this.num(
        personaCiudad?.distritos?.departamentos?.codigo ?? ciudadEnt?.distritos?.departamentos?.codigo,
      ),
      cDisRec: this.num(personaCiudad?.distritos?.codigo ?? ciudadEnt?.distritos?.codigo),
      cCiuRec: this.num(personaCiudad?.codigo ?? ciudadEnt?.codigo),
      dTelRec: persona?.telefono?.replace(/\s+/g, '') ?? null,
      dCelRec: persona?.celular?.replace(/\s+/g, '') ?? null,
      dEmailRec: persona?.email ?? null,

      // Campos específicos de Nota de Remisión
      iMotEmiNR: this.num(motivo?.codigo) ?? null,
      dDesMotEmiNR:
        this.num(motivo?.codigo) === 99 ? (rem.dinfadic ?? motivo?.descripcion ?? null) : (motivo?.descripcion ?? null),
      iRespEmiNR: this.num(rem.responsable_emision?.codigo) ?? null,
      dKmR: this.num(rem.dkmr) ?? 0,
      iTipTrans: this.num(rem.tipo_transporte?.codigo) ?? null,
      iModTrans: this.num(modalidad?.codigo) ?? null,
      dDesModTrans: modalidad?.descripcion ?? null,
      cCondNeg: rem.condicion_negociacion?.codigo ?? null,
      dNuDespImp: rem.dnudespimp ?? null,
      iRespFlete: this.num(rem.responsable_flete?.codigo) ?? this.num(rem.responsable_emision?.codigo) ?? null,

      // Fechas traslado
      dIniTras: this.fmtDate(rem.dinitras),
      dFinTras: this.fmtDate(rem.dfintras),

      // Salida
      dDirLocSal: rem.ddirlocsal ?? null,
      dComp1Sal: rem.dcomp1sal ?? null,
      dComp2Sal: rem.dcomp2sal ?? null,
      // Salida/Entrega: el middleware exige number en estos campos (a diferencia
      // del receptor, que los pide como string).
      dNumCasSal: this.num(rem.dnumcassal) ?? 0,
      cDepSal: this.num(ciudadSal?.distritos?.departamentos?.codigo) ?? null,
      cDisSal: this.num(ciudadSal?.distritos?.codigo) ?? null,
      cCiuSal: this.num(ciudadSal?.codigo) ?? null,
      dTelSal: rem.dtelsal ?? null,

      // Entrega
      dDirLocEnt: rem.ddirlocent ?? null,
      dNumCasEnt: this.num(rem.dnumcasent) ?? 0,
      dComp1Ent: rem.dcomp1ent ?? null,
      dComp2Ent: rem.dcomp2ent ?? null,
      cDepEnt: this.num(ciudadEnt?.distritos?.departamentos?.codigo) ?? null,
      cDisEnt: this.num(ciudadEnt?.distritos?.codigo) ?? null,
      cCiuEnt: this.num(ciudadEnt?.codigo) ?? null,
      dTiVehTras: modalidad?.descripcion ?? null,
      dTelEnt: rem.dtelent ?? null,

      // Vehículo
      // La app usa tipo_identificacion 1=Matrícula, 2=Identificación; SIFEN
      // dTipIdenVeh es al revés (1=Nro. identificación, 2=Matrícula), por eso
      // se invierte al mapear.
      dMarVeh: vehiculo?.marca ?? null,
      dTipIdenVeh: vehiculo?.tipo_identificacion === 1 ? 2 : vehiculo?.tipo_identificacion === 2 ? 1 : null,
      dNroIDVeh: vehiculo?.nro_identificacion ?? null,
      dAdicVeh: vehiculo?.informacion_adicional ?? null,
      dNroMatVeh: vehiculo?.nro_matricula ?? null,

      // Transportista
      iNatTrans: this.num(transPersona?.naturaleza_receptor?.codigo) ?? null,
      dNomTrans: transPersona?.razon_social ?? null,
      dRucTrans: transPersona?.ruc ?? null,
      dDVTrans: transPersona?.dv ? this.num(transPersona.dv) : null,
      iTipIDTrans: transPersona?.tipo_documento_identidad?.codigo ?? null,
      dNumIDTrans: transPersona?.nro_documento ?? null,
      cNacTrans: transPersona?.paises?.codigo ?? null,
      dDomFisc: transPersona?.direccion ?? null,

      // Chofer
      dNomChof: choferPersona?.razon_social ?? null,
      dNumIDChof: choferPersona?.nro_documento ?? null,
      dDirChof: choferPersona?.direccion ?? null,

      // Agente (opcional)
      dNombAg: agentePersona?.razon_social ?? null,
      dRucAg: agentePersona?.ruc ?? null,
      dDVAg: agentePersona?.dv ? this.num(agentePersona.dv) : null,
      dDirAge: agentePersona?.direccion ?? null,

      dOrdCompra: rem.dordcompra ?? null,
      dOrdVta: rem.dordvta ?? null,

      // Fecha futura emisión (solo si no hay documento asociado; se anula abajo
      // si terminan existiendo documentos asociados).
      dFecEm: this.fmtDate(rem.dfecem),

      link_documento: rem.enlace_qr ?? null,
    };

    // Detalles
    if (rem.nota_remision_det?.length > 0) {
      payload.Detalles = rem.nota_remision_det.map((det) => {
        const prod = det.productos;
        return {
          dCodInt: prod?.cod_producto ?? prod?.id?.replace(/-/g, '').slice(0, 5) ?? null,
          dDesProSer: det.ddesproser,
          cUniMed: prod?.unidades_de_medida?.codigo ?? det.cunimed,
          dDesUniMed: prod?.unidades_de_medida?.representacion ?? null,
          dCantProSer: this.num(det.dcantproser) ?? 0,
          cRelMerc: det.crelmerc ?? null,
          dCanQuiMer: det.dcanquimer ? this.num(det.dcanquimer) : null,
          dPorQuiMer: det.dporquimer ? this.num(det.dporquimer) : null,
          dNCM: null,
          dGtin: null,
          dGtinPq: null,
          dInfItem: det.dinfitem ?? null,
          cPaisOrig: det.cpaisorig ?? null,
        };
      });
    }

    // Documentos asociados (SIFEN admite N). Se combinan:
    //  1) Facturas electrónicas vinculadas vía nota_remision_factura (1→N).
    //  2) El documento asociado de cabecera (impreso, o electrónico legacy por
    //     cdc_asociado), evitando duplicar un CDC ya incluido.
    const documentosAsociados: Record<string, unknown>[] = [];
    const cdcsElectronicos = new Set<string>();

    for (const nrf of rem.nota_remision_factura ?? []) {
      const cdc = nrf.factura_cab?.cdc;
      if (cdc && !cdcsElectronicos.has(cdc)) {
        cdcsElectronicos.add(cdc);
        // Numérico, no '1': el middleware valida el tipo de dato y rechaza el
        // documento con "El tipo de dato esperado es number". Facturas y notas
        // de crédito ya lo mandaban así.
        documentosAsociados.push({ iTipDocAso: 1, dCdCDERef: cdc });
      }
    }

    if (rem.tipo_documento_asociado === '1') {
      // Electrónico cargado en cabecera (legacy): solo si no vino por la N:N.
      if (rem.cdc_asociado && !cdcsElectronicos.has(rem.cdc_asociado)) {
        cdcsElectronicos.add(rem.cdc_asociado);
        documentosAsociados.push({ iTipDocAso: 1, dCdCDERef: rem.cdc_asociado });
      }
    } else if (rem.tipo_documento_asociado === '2' || rem.tipo_documento_asociado === '3') {
      documentosAsociados.push({
        iTipDocAso: this.num(rem.tipo_documento_asociado),
        dNTimDI: rem.timbrado_asociado ?? null,
        dEstDocAso: rem.establecimiento_asociado ?? null,
        dPExpDocAso: rem.expedicion_asociado ?? null,
        dNumDocAso: rem.nro_doc_asociado ?? null,
        // El middleware exige iTipoDocAso como string.
        iTipoDocAso: this.num(rem.tipo_doc_impreso_asociado) ? this.num(rem.tipo_doc_impreso_asociado) : null,
        dFecEmiDI: this.fmtDate(rem.fecha_doc_asociado),
      });
    }

    if (documentosAsociados.length > 0) {
      payload.DocumentosAsociados = documentosAsociados;
      // SIFEN: la fecha futura de emisión y los documentos asociados no coexisten.
      payload.dFecEm = null;
    }

    return payload;
  }
}
