import {
  CartaPorte,
  TipoCfdiCartaPorte,
  UbicacionCartaPorte,
  MercanciaCartaPorte,
  FiguraTransporte,
  AutotransporteCartaPorte,
  TipoUbicacionCartaPorte,
} from '../entities';

/**
 * Namespaces CFDI 4.0 y Carta Porte 3.1
 */
const CFDI_NAMESPACE = 'http://www.sat.gob.mx/cfd/4';
const CARTA_PORTE_NAMESPACE = 'http://www.sat.gob.mx/CartaPorte31';
const XSI_NAMESPACE = 'http://www.w3.org/2001/XMLSchema-instance';

const CFDI_SCHEMA_LOCATION = 'http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd';
const CARTA_PORTE_SCHEMA_LOCATION = 'http://www.sat.gob.mx/CartaPorte31 http://www.sat.gob.mx/sitio_internet/cfd/CartaPorte/CartaPorte31.xsd';

/**
 * Servicio para generar XML de Carta Porte CFDI 3.1
 * Cumple con especificacion SAT vigente desde 2024-07-17
 */
export class CartaPorteXmlService {
  /**
   * Genera XML del CFDI con complemento Carta Porte 3.1
   * NOTA: Este XML es para sellado previo al timbrado con PAC
   */
  generatePreTimbradoXml(cartaPorte: CartaPorte): string {
    const xmlParts: string[] = [];

    // Declaracion XML
    xmlParts.push('<?xml version="1.0" encoding="UTF-8"?>');

    // Elemento raiz: cfdi:Comprobante
    xmlParts.push(this.buildComprobanteElement(cartaPorte));

    return xmlParts.join('\n');
  }

  /**
   * Genera solo el complemento Carta Porte 3.1 (sin CFDI wrapper)
   */
  generateCartaPorteComplemento(cartaPorte: CartaPorte): string {
    return this.buildCartaPorteElement(cartaPorte);
  }

  /**
   * Construye el elemento cfdi:Comprobante
   */
  private buildComprobanteElement(cp: CartaPorte): string {
    const attrs: string[] = [];

    // Namespaces
    attrs.push(`xmlns:cfdi="${CFDI_NAMESPACE}"`);
    attrs.push(`xmlns:cartaporte31="${CARTA_PORTE_NAMESPACE}"`);
    attrs.push(`xmlns:xsi="${XSI_NAMESPACE}"`);
    attrs.push(`xsi:schemaLocation="${CFDI_SCHEMA_LOCATION} ${CARTA_PORTE_SCHEMA_LOCATION}"`);

    // Atributos del comprobante
    attrs.push('Version="4.0"');
    if (cp.serie) attrs.push(`Serie="${this.escapeXml(cp.serie)}"`);
    if (cp.folio) attrs.push(`Folio="${this.escapeXml(cp.folio)}"`);
    attrs.push(`Fecha="${this.formatDateTime(new Date())}"`);

    // Forma de pago (99 = Por definir, usado en traslados)
    attrs.push('FormaPago="99"');

    // Sello y Certificado (placeholders, el PAC los llena)
    attrs.push('Sello=""');
    attrs.push('NoCertificado=""');
    attrs.push('Certificado=""');

    // Subtotal y Total
    const subtotal = cp.subtotal ?? 0;
    const total = cp.total ?? 0;
    attrs.push(`SubTotal="${subtotal.toFixed(2)}"`);
    attrs.push(`Total="${total.toFixed(2)}"`);

    // Moneda
    attrs.push(`Moneda="${cp.moneda || 'MXN'}"`);

    // Tipo de comprobante
    const tipoComprobante = cp.tipoCfdi === TipoCfdiCartaPorte.INGRESO ? 'I' : 'T';
    attrs.push(`TipoDeComprobante="${tipoComprobante}"`);

    // Exportacion (01 = No aplica)
    attrs.push('Exportacion="01"');

    // Metodo de pago (PUE = Pago en una sola exhibicion)
    if (cp.tipoCfdi === TipoCfdiCartaPorte.INGRESO) {
      attrs.push('MetodoPago="PUE"');
    }

    // Lugar de expedicion (CP del emisor)
    attrs.push(`LugarExpedicion="${cp.emisorDomicilioFiscalCp || '00000'}"`);

    // Construir XML
    let xml = `<cfdi:Comprobante ${attrs.join(' ')}>\n`;

    // Emisor
    xml += this.buildEmisorElement(cp);

    // Receptor
    xml += this.buildReceptorElement(cp);

    // Conceptos
    xml += this.buildConceptosElement(cp);

    // Complemento (Carta Porte 3.1)
    xml += '  <cfdi:Complemento>\n';
    xml += this.buildCartaPorteElement(cp);
    xml += '  </cfdi:Complemento>\n';

    xml += '</cfdi:Comprobante>';

    return xml;
  }

  /**
   * Construye el elemento cfdi:Emisor
   */
  private buildEmisorElement(cp: CartaPorte): string {
    const attrs: string[] = [];
    attrs.push(`Rfc="${this.escapeXml(cp.emisorRfc)}"`);
    attrs.push(`Nombre="${this.escapeXml(cp.emisorNombre)}"`);
    attrs.push(`RegimenFiscal="${cp.emisorRegimenFiscal || '601'}"`);

    return `  <cfdi:Emisor ${attrs.join(' ')}/>\n`;
  }

  /**
   * Construye el elemento cfdi:Receptor
   */
  private buildReceptorElement(cp: CartaPorte): string {
    const attrs: string[] = [];
    attrs.push(`Rfc="${this.escapeXml(cp.receptorRfc)}"`);
    attrs.push(`Nombre="${this.escapeXml(cp.receptorNombre)}"`);

    // Uso CFDI (S01 = Sin efectos fiscales para traslados)
    const usoCfdi = cp.receptorUsoCfdi || (cp.tipoCfdi === TipoCfdiCartaPorte.TRASLADO ? 'S01' : 'G03');
    attrs.push(`UsoCFDI="${usoCfdi}"`);

    // Domicilio fiscal CP
    if (cp.receptorDomicilioFiscalCp) {
      attrs.push(`DomicilioFiscalReceptor="${cp.receptorDomicilioFiscalCp}"`);
    }

    // Regimen fiscal receptor
    attrs.push('RegimenFiscalReceptor="616"'); // Sin obligaciones fiscales (default)

    return `  <cfdi:Receptor ${attrs.join(' ')}/>\n`;
  }

  /**
   * Construye el elemento cfdi:Conceptos
   * Para Carta Porte: 1 concepto con clave SAT 78101800 (Servicio de transporte)
   */
  private buildConceptosElement(cp: CartaPorte): string {
    let xml = '  <cfdi:Conceptos>\n';

    const cantidad = 1;
    const valorUnitario = cp.subtotal ?? 0;
    const importe = cp.total ?? 0;

    // Clave de producto/servicio SAT para transporte de carga
    const claveProdServ = '78101800';
    const claveUnidad = 'E48'; // Unidad de servicio
    const descripcion = 'Servicio de transporte de carga';

    xml += '    <cfdi:Concepto ';
    xml += `ClaveProdServ="${claveProdServ}" `;
    xml += `Cantidad="${cantidad}" `;
    xml += `ClaveUnidad="${claveUnidad}" `;
    xml += `Unidad="Servicio" `;
    xml += `Descripcion="${descripcion}" `;
    xml += `ValorUnitario="${valorUnitario.toFixed(2)}" `;
    xml += `Importe="${importe.toFixed(2)}" `;
    xml += `ObjetoImp="01"/>\n`; // 01 = No objeto de impuesto

    xml += '  </cfdi:Conceptos>\n';

    return xml;
  }

  /**
   * Construye el complemento cartaporte31:CartaPorte
   */
  private buildCartaPorteElement(cp: CartaPorte): string {
    const attrs: string[] = [];
    attrs.push('Version="3.1"');

    // Transporte internacional
    const transpInt = cp.transporteInternacional ? 'Sí' : 'No';
    attrs.push(`TranspInternac="${transpInt}"`);

    if (cp.transporteInternacional) {
      if (cp.entradaSalidaMerc) attrs.push(`EntradaSalidaMerc="${cp.entradaSalidaMerc}"`);
      if (cp.paisOrigenDestino) attrs.push(`PaisOrigenDestino="${cp.paisOrigenDestino}"`);
    }

    // Totales de mercancias
    if (cp.pesoBrutoTotal) attrs.push(`TotalDistRec="${cp.pesoBrutoTotal.toFixed(3)}"`);

    let xml = `    <cartaporte31:CartaPorte ${attrs.join(' ')}>\n`;

    // Ubicaciones
    xml += this.buildUbicacionesElement(cp.ubicaciones || []);

    // Mercancias
    xml += this.buildMercanciasElement(cp);

    // Figuras de transporte
    xml += this.buildFigurasTransporteElement(cp.figuras || []);

    xml += '    </cartaporte31:CartaPorte>\n';

    return xml;
  }

  /**
   * Construye el elemento cartaporte31:Ubicaciones
   */
  private buildUbicacionesElement(ubicaciones: UbicacionCartaPorte[]): string {
    if (ubicaciones.length === 0) return '';

    let xml = '      <cartaporte31:Ubicaciones>\n';

    // Ordenar por secuencia
    const sorted = [...ubicaciones].sort((a, b) => a.numeroSecuencia - b.numeroSecuencia);

    for (const ub of sorted) {
      const tipoUbicacion = ub.tipoUbicacion === TipoUbicacionCartaPorte.ORIGEN ? 'Origen' : 'Destino';
      const attrs: string[] = [];

      attrs.push(`TipoUbicacion="${tipoUbicacion}"`);
      if (ub.idUbicacion) attrs.push(`IDUbicacion="${this.escapeXml(ub.idUbicacion)}"`);
      if (ub.rfcRemitenteDestinatario) attrs.push(`RFCRemitenteDestinatario="${ub.rfcRemitenteDestinatario}"`);
      if (ub.nombreRemitenteDestinatario) attrs.push(`NombreRemitenteDestinatario="${this.escapeXml(ub.nombreRemitenteDestinatario)}"`);

      if (ub.fechaHoraSalidaLlegada) {
        const fechaStr = this.formatDateTime(new Date(ub.fechaHoraSalidaLlegada));
        attrs.push(`FechaHoraSalidaLlegada="${fechaStr}"`);
      }

      if (ub.distanciaRecorrida !== undefined && ub.distanciaRecorrida > 0) {
        attrs.push(`DistanciaRecorrida="${ub.distanciaRecorrida.toFixed(2)}"`);
      }

      xml += `        <cartaporte31:Ubicacion ${attrs.join(' ')}>\n`;

      // Domicilio
      xml += this.buildDomicilioElement(ub);

      xml += '        </cartaporte31:Ubicacion>\n';
    }

    xml += '      </cartaporte31:Ubicaciones>\n';

    return xml;
  }

  /**
   * Construye el elemento cartaporte31:Domicilio
   */
  private buildDomicilioElement(ub: UbicacionCartaPorte): string {
    const attrs: string[] = [];

    if (ub.calle) attrs.push(`Calle="${this.escapeXml(ub.calle)}"`);
    if (ub.numExterior) attrs.push(`NumeroExterior="${this.escapeXml(ub.numExterior)}"`);
    if (ub.numInterior) attrs.push(`NumeroInterior="${this.escapeXml(ub.numInterior)}"`);
    if (ub.colonia) attrs.push(`Colonia="${ub.colonia}"`);
    if (ub.localidad) attrs.push(`Localidad="${ub.localidad}"`);
    if (ub.referencia) attrs.push(`Referencia="${this.escapeXml(ub.referencia)}"`);
    if (ub.municipio) attrs.push(`Municipio="${ub.municipio}"`);
    if (ub.estado) attrs.push(`Estado="${ub.estado}"`);
    attrs.push('Pais="MEX"');
    attrs.push(`CodigoPostal="${ub.codigoPostal}"`);

    return `          <cartaporte31:Domicilio ${attrs.join(' ')}/>\n`;
  }

  /**
   * Construye el elemento cartaporte31:Mercancias
   */
  private buildMercanciasElement(cp: CartaPorte): string {
    const mercancias = cp.mercancias || [];
    if (mercancias.length === 0) return '';

    // Calcular totales
    const pesoTotal = mercancias.reduce((sum, m) => sum + (m.pesoEnKg || 0), 0);
    const numMercancias = mercancias.length;

    const attrs: string[] = [];
    attrs.push(`PesoBrutoTotal="${pesoTotal.toFixed(3)}"`);
    attrs.push(`UnidadPeso="${cp.unidadPeso || 'KGM'}"`);
    attrs.push(`NumTotalMercancias="${numMercancias}"`);

    let xml = `      <cartaporte31:Mercancias ${attrs.join(' ')}>\n`;

    // Cada mercancia
    for (const merc of mercancias) {
      xml += this.buildMercanciaElement(merc);
    }

    // Autotransporte
    xml += this.buildAutotransporteElement(cp);

    xml += '      </cartaporte31:Mercancias>\n';

    return xml;
  }

  /**
   * Construye el elemento cartaporte31:Mercancia
   */
  private buildMercanciaElement(merc: MercanciaCartaPorte): string {
    const attrs: string[] = [];

    attrs.push(`BienesTransp="${merc.bienesTransp}"`);
    attrs.push(`Descripcion="${this.escapeXml(merc.descripcion)}"`);
    attrs.push(`Cantidad="${merc.cantidad.toFixed(4)}"`);
    attrs.push(`ClaveUnidad="${merc.claveUnidad}"`);
    if (merc.unidad) attrs.push(`Unidad="${this.escapeXml(merc.unidad)}"`);

    if (merc.dimensionesLargo && merc.dimensionesAncho && merc.dimensionesAlto) {
      const dims = `${merc.dimensionesLargo}/${merc.dimensionesAncho}/${merc.dimensionesAlto}cm`;
      attrs.push(`Dimensiones="${dims}"`);
    }

    if (merc.materialPeligroso) {
      attrs.push('MaterialPeligroso="Sí"');
      if (merc.cveMaterialPeligroso) attrs.push(`CveMaterialPeligroso="${merc.cveMaterialPeligroso}"`);
      if (merc.embalaje) attrs.push(`Embalaje="${merc.embalaje}"`);
      if (merc.descripEmbalaje) attrs.push(`DescripEmbalaje="${this.escapeXml(merc.descripEmbalaje)}"`);
    } else {
      attrs.push('MaterialPeligroso="No"');
    }

    attrs.push(`PesoEnKg="${merc.pesoEnKg.toFixed(3)}"`);

    if (merc.valorMercancia) {
      attrs.push(`ValorMercancia="${merc.valorMercancia.toFixed(2)}"`);
      attrs.push(`Moneda="${merc.moneda || 'MXN'}"`);
    }

    if (merc.fraccionArancelaria) attrs.push(`FraccionArancelaria="${merc.fraccionArancelaria}"`);
    if (merc.uuidComercioExt) attrs.push(`UUIDComercioExt="${merc.uuidComercioExt}"`);

    return `        <cartaporte31:Mercancia ${attrs.join(' ')}/>\n`;
  }

  /**
   * Construye el elemento cartaporte31:Autotransporte
   */
  private buildAutotransporteElement(cp: CartaPorte): string {
    const autos = cp.autotransporte || [];
    if (autos.length === 0) return '';

    const auto = autos[0]; // Usualmente solo hay uno

    const attrs: string[] = [];
    attrs.push(`PermSCT="${auto.permSCT}"`);
    attrs.push(`NumPermisoSCT="${auto.numPermisoSCT}"`);

    let xml = `        <cartaporte31:Autotransporte ${attrs.join(' ')}>\n`;

    // Identificacion vehicular
    const idVehAttrs: string[] = [];
    idVehAttrs.push(`ConfigVehicular="${auto.configVehicular}"`);
    idVehAttrs.push(`PlacaVM="${auto.placaVM}"`);
    if (auto.anioModeloVM) idVehAttrs.push(`AnioModeloVM="${auto.anioModeloVM}"`);

    xml += `          <cartaporte31:IdentificacionVehicular ${idVehAttrs.join(' ')}/>\n`;

    // Seguros
    if (cp.aseguraRespCivil) {
      xml += `          <cartaporte31:Seguros `;
      xml += `AseguraRespCivil="${this.escapeXml(cp.aseguraRespCivil)}" `;
      xml += `PolizaRespCivil="${this.escapeXml(cp.polizaRespCivil || '')}"`;
      if (cp.aseguraMedAmbiente) {
        xml += ` AseguraMedAmbiente="${this.escapeXml(cp.aseguraMedAmbiente)}"`;
        xml += ` PolizaMedAmbiente="${this.escapeXml(cp.polizaMedAmbiente || '')}"`;
      }
      if (cp.aseguraCarga) {
        xml += ` AseguraCarga="${this.escapeXml(cp.aseguraCarga)}"`;
        xml += ` PolizaCarga="${this.escapeXml(cp.polizaCarga || '')}"`;
        if (cp.primaSeguro) xml += ` PrimaSeguro="${cp.primaSeguro.toFixed(2)}"`;
      }
      xml += `/>\n`;
    }

    // Remolques
    if (auto.remolques && auto.remolques.length > 0) {
      xml += '          <cartaporte31:Remolques>\n';
      for (const rem of auto.remolques) {
        xml += `            <cartaporte31:Remolque SubTipoRem="${rem.subTipoRem}" Placa="${rem.placa}"/>\n`;
      }
      xml += '          </cartaporte31:Remolques>\n';
    }

    xml += '        </cartaporte31:Autotransporte>\n';

    return xml;
  }

  /**
   * Construye el elemento cartaporte31:FiguraTransporte
   */
  private buildFigurasTransporteElement(figuras: FiguraTransporte[]): string {
    if (figuras.length === 0) return '';

    let xml = '      <cartaporte31:FiguraTransporte>\n';

    for (const fig of figuras) {
      const attrs: string[] = [];
      attrs.push(`TipoFigura="${fig.tipoFigura}"`);
      if (fig.rfcFigura) attrs.push(`RFCFigura="${fig.rfcFigura}"`);
      attrs.push(`NombreFigura="${this.escapeXml(fig.nombreFigura)}"`);
      if (fig.numLicencia) attrs.push(`NumLicencia="${fig.numLicencia}"`);
      if (fig.numRegIdTribFigura) attrs.push(`NumRegIdTribFigura="${fig.numRegIdTribFigura}"`);
      if (fig.residenciaFiscalFigura) attrs.push(`ResidenciaFiscalFigura="${fig.residenciaFiscalFigura}"`);

      xml += `        <cartaporte31:TiposFigura ${attrs.join(' ')}`;

      // Partes de transporte (para propietario/arrendador)
      if (fig.partesTransporte && fig.partesTransporte.length > 0) {
        xml += '>\n';
        for (const parte of fig.partesTransporte) {
          xml += `          <cartaporte31:PartesTransporte ParteTransporte="${this.escapeXml(parte.parteTransporte)}"/>\n`;
        }
        xml += '        </cartaporte31:TiposFigura>\n';
      } else {
        xml += '/>\n';
      }
    }

    xml += '      </cartaporte31:FiguraTransporte>\n';

    return xml;
  }

  /**
   * Escapa caracteres especiales XML
   */
  private escapeXml(str: string): string {
    return str
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&apos;');
  }

  /**
   * Formatea fecha en formato ISO 8601 para SAT
   */
  private formatDateTime(date: Date): string {
    return date.toISOString().replace(/\.\d{3}Z$/, '');
  }
}
