<?php 

namespace App\Services\ElectronicBilling\Builders;

use App\Data\ElectronicDocument;
use DOMDocument;
use DOMElement;

abstract class AbstractXmlBuilder 
{
    protected ElectronicDocument $data;
    protected DOMDocument $doc;
    protected DOMElement $root;

    abstract protected function getRootNodeName(): string;
    abstract protected function getNamespaceUrl(): string;
    abstract protected function getSchemaUrl(): string;

    public function build(ElectronicDocument $data): string
    {
        $this->data = $data;

        $this->doc = new DOMDocument('1.0', 'utf-8');
        $this->doc->preserveWhiteSpace = false;
        $this->doc->formatOutput = true;

        $this->createRoot();

        foreach ($this->getBuildSteps() as $step) 
        { 
            $this->$step();
        }

        return $this->doc->saveXML();
    }

    
    protected function getBuildSteps(): array 
    {
        return [];
    }

    protected function shouldBuildReceptor(): bool 
    {
        return true;
    }

    protected function createRoot(): void
    {
        $ns = $this->getNamespaceUrl();
        $this->root = $this->doc->createElementNS($ns, $this->getRootNodeName());
        
        $this->root->setAttribute('xmlns', $ns);
        $this->root->setAttribute('xmlns:xsd', $this->getSchemaUrl());
        $this->root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');

        $this->doc->appendChild($this->root);
    }

    protected function buildHeader(): void
    {
        $this->addText('Clave', $this->data->clave);
        $this->addText('ProveedorSistemas', $this->data->proveedor_sistema);
        $this->addText('CodigoActividadEmisor', $this->data->cod_act_economica_emisor);
        $this->addText('CodigoActividadReceptor', $this->data->cod_act_economica_receptor);
        $this->addText('NumeroConsecutivo', $this->data->numero_consecutivo);
        $this->addText('FechaEmision', $this->data->fecha_emision);
    }

    protected function buildIssuer(): void
    {
        $node = $this->addChild($this->root, 'Emisor');
        $this->addPersonDetails($node, $this->data->emisor);
    }

    protected function buildReceiver(): void
    {
        if ($this->shouldBuildReceptor()) 
        {
            $node = $this->addChild($this->root, 'Receptor');
            $this->addPersonDetails($node, $this->data->receptor);
        }
    }

    protected function addPersonDetails(DOMElement $parent, $personData): void
    {
        $this->addText('Nombre', $personData->nombre, $parent);
        
        if($personData->tipo_identificacion && $personData->numero_identificacion) {
            $nodeId = $this->addChild($parent, 'Identificacion');
            $this->addText('Tipo', $personData->tipo_identificacion, $nodeId);
            $this->addText('Numero', $personData->numero_identificacion, $nodeId);
        }

        if($personData->nombre_comercial) {
            $this->addText('NombreComercial', $personData->nombre_comercial, $parent);
        }

        if ($personData->ubicacion)
        {
            $loc = $this->addChild($parent, 'Ubicacion');
            $this->addText('Provincia', $personData->ubicacion['provincia'], $loc);
            $this->addText('Canton', $personData->ubicacion['canton'], $loc);
            $this->addText('Distrito', $personData->ubicacion['distrito'], $loc);
            $this->addText('Barrio', $personData->ubicacion['barrio'] ?? "SIN DETALLE",$loc);
            $this->addText('OtrasSenas', $personData->ubicacion['sennas'] ?? "SIN DETALLE", $loc);
        }

        $this->addText('CorreoElectronico', $personData->correo, $parent);
    }

    protected function buildConditionOfSale(): void
    {
        $this->addText('CondicionVenta', $this->data->condicion_venta);
        $this->addText('CondicionVentaOtros', $this->data->condicion_venta_otros);
        $this->addText('PlazoCredito', $this->data->plazo_credito);
    }

    protected function buildDetail(): void
    {
        $container = $this->addChild($this->root, 'DetalleServicio');

        foreach ($this->data->lineas as $linea) 
        {
            $row = $this->addChild($container, 'LineaDetalle');

            $this->addText('NumeroLinea', (string)$linea->numero_linea, $row);
            $this->addText('CodigoCABYS', $linea->codigo_cabys, $row);
            $this->addDecimal('Cantidad', $linea->cantidad, $row, 3);
            $this->addText('UnidadMedida', $linea->unidad_medida, $row);
            $this->addText('Detalle', $linea->detalle, $row);
            $this->addDecimal('PrecioUnitario', $linea->precio_unitario,  $row);
            $this->addDecimal('MontoTotal', $linea->monto_total,  $row);

            foreach ($linea->descuentos as $desc) 
            {
                $descNode = $this->addChild($row, 'Descuento');

                $this->addText('MontoDescuento', $desc->monto_descuento, $descNode);
                $this->addText('CodigoDescuento', $desc->codigo_descuento, $descNode);
            }

            $this->addDecimal('SubTotal', $linea->sub_total,  $row);
            $this->addDecimal('BaseImponible', $linea->base_imponible,  $row);

            foreach ($linea->impuestos as $imp) {
                $impNode = $this->addChild($row, 'Impuesto');
                $this->addText('Codigo', $imp->codigo, $impNode);
                $this->addText('CodigoTarifaIVA', $imp->codigo_tarifa_iva, $impNode);
                $this->addDecimal('Tarifa', $imp->tarifa,  $impNode, 2);
                $this->addDecimal('Monto', $imp->monto,  $impNode);
            }

            $this->addDecimal('ImpuestoAsumidoEmisorFabrica', $linea->imp_asum_emisor,  $row);
            $this->addDecimal('ImpuestoNeto', $linea->impuesto_neto,  $row);
            $this->addDecimal('MontoTotalLinea', $linea->monto_total_linea, $row);
        }
    }

    protected function buildSummary(): void
    {
        $node = $this->addChild($this->root, 'ResumenFactura');

        $resumen = $this->data->resumen;

        $codTipMonNode = $this->addChild($node, 'CodigoTipoMoneda');
        $this->addText('CodigoMoneda', $this->data->moneda, $codTipMonNode);
        $this->addText('TipoCambio', $this->data->tipo_cambio, $codTipMonNode);
      
        $this->addDecimal('TotalServGravados', $resumen['total_serv_gravados'], $node);
        $this->addDecimal('TotalServExentos', $resumen['total_serv_exentos'], $node);
        $this->addDecimal('TotalServExonerado', $resumen['total_serv_exonerados'], $node);
        $this->addDecimal('TotalServNoSujeto', $resumen['total_serv_no_sujetos'], $node);
        $this->addDecimal('TotalMercanciasGravadas', $resumen['total_merc_gravadas'], $node);
        $this->addDecimal('TotalMercanciasExentas', $resumen['total_merc_exentas'], $node);
        $this->addDecimal('TotalMercExonerada', $resumen['total_merc_exoneradas'], $node);
        $this->addDecimal('TotalMercNoSujeta', $resumen['total_merc_no_sujetas'], $node);
        $this->addDecimal('TotalGravado', $resumen['total_gravado'], $node);
        $this->addDecimal('TotalExento', $resumen['total_exento'], $node);
        $this->addDecimal('TotalExonerado', $resumen['total_exonerado'], $node);
        $this->addDecimal('TotalNoSujeto', $resumen['total_no_sujeto'], $node);
        $this->addDecimal('TotalVenta', $resumen['total_venta'], $node);
        $this->addDecimal('TotalDescuentos', $resumen['total_descuentos'], $node);
        $this->addDecimal('TotalVentaNeta', $resumen['total_venta_neta'], $node);

        foreach ($this->data->desglose_impuestos as $impuesto) 
        {
            $desgloseImpuesto = $this->addChild($node, 'TotalDesgloseImpuesto');
            $this->addText('Codigo', $impuesto["codigo"], $desgloseImpuesto);
            $this->addText('CodigoTarifaIVA', $impuesto["codigo_tarifa_iva"], $desgloseImpuesto);
            $this->addDecimal('TotalMontoImpuesto', $impuesto["total_monto_impuesto"], $desgloseImpuesto);
        }

        $this->addDecimal('TotalImpuesto', $resumen['total_impuesto'], $node);
        $this->addDecimal('TotalImpAsumEmisorFabrica', $resumen['total_imp_asum_emi'], $node);
        $this->addDecimal('TotalIVADevuelto', $resumen['total_iva_devuelto'], $node);

        $medPagNode = $this->addChild($node, 'MedioPago');
        $this->addText('TipoMedioPago', $this->data->medio_pago[0]['medio_pago_tipo'], $medPagNode);
        $this->addText('MedioPagoOtros', null, $medPagNode);
        $this->addText('TotalMedioPago', null, $medPagNode);

        $this->addDecimal('TotalComprobante', $resumen['total_comprobante'], $node);
    }

    protected function buildOtros(): DOMElement
    {
        $container = $this->addChild($this->root, 'Otros');

        $this->addText("OtroTexto","--- Soluciones Arce M. arcemiranda@factucr.com ---", $container);

        return $container;
    }

    protected function addChild(DOMElement $parent, string $name): DOMElement
    {
        $el = $this->doc->createElement($name);
        $parent->appendChild($el);
        return $el;
    }

    protected function addText(string $name, ?string $value, ?DOMElement $parent = null): void
    {
        if ($value === null || $value === '') return;
        
        $el = $this->doc->createElement($name);
        $el->appendChild($this->doc->createTextNode($value));
        ($parent ?? $this->root)->appendChild($el);
    }

    protected function addDecimal(string $name, float $value, ?DOMElement $parent = null, int $decimals = 5): void {
        $this->addText($name, number_format($value, $decimals, '.', ''), $parent);
    }
}