<?php

namespace Daniv04\HaciendaPackage\Services\Validators;

class InvoiceSummaryValidator
{
    public function __construct(
        private array $data
    ) {}

    public function validate($validator): void
    {
        $this->validateCodigoMoneda($validator);
        $this->validateTipoCambio($validator);
        $this->validateTotalServGravadosRequired($validator);
        $this->validateTotalServGravadosAmount($validator);
        $this->validateTotalServExentosRequired($validator);
        $this->validateTotalServExentosAmount($validator);
        $this->validateTotalServExoneradosRequired($validator);
        $this->validateTotalServExoneradosAmount($validator);
        $this->validateTotalServNoSujetoRequired($validator);
        $this->validateTotalServNoSujetoAmount($validator);
        $this->validateTotalMerGravadosRequired($validator);
        $this->validateTotalMerGravadosAmount($validator);
        $this->validateTotalMerExentasRequired($validator);
        $this->validateTotalMerExentasAmount($validator);
        $this->validateTotalMerExoneradasRequired($validator);
        $this->validateTotalMercExoneradaAmount($validator);
        $this->validateTotalMerNoSujetaRequired($validator);
        $this->validateTotalMerNoSujetaAmount($validator);
        $this->validateTotalGravado($validator);
        $this->validateTotalExento($validator);
        $this->validateTotalExonerado($validator);
        $this->validateTotalNoSujeto($validator);
        $this->validateTotalVenta($validator);
        $this->validateTotalDescuentos($validator);
        $this->validateTotalImpuestoRequired($validator);
        $this->validateTotalImpuestoAmount($validator);
        $this->validateTotalComprobante($validator);
        $this->validateTotalMedioPago($validator);
        $this->validateTotalDesgloseImpuesto($validator);
    }

    //----------------------------Fields CodigoMoneda y TipoCambio
    protected function validateCodigoMoneda($validator)
    {
        $codes = array_keys(config('catalogues.currency_codes', []));
        $tipoMoneda = $this->data['ResumenFactura']['CodigoTipoMoneda']['CodigoMoneda'] ?? null;

        if ($tipoMoneda && !in_array($tipoMoneda, $codes)) {
            $validator->errors()->add(
                "ResumenFactura.CodigoTipoMoneda.CodigoMoneda",
                "The currency code '{$tipoMoneda}' is not valid."
            );
        }
    }

    protected function validateTipoCambio($validator)
    {
        $tipoMoneda = $this->data['ResumenFactura']['CodigoTipoMoneda']['CodigoMoneda'] ?? null;
        $tipoCambio = $this->data['ResumenFactura']['CodigoTipoMoneda']['TipoCambio'] ?? null;

        if ($tipoMoneda === 'CRC' && round($tipoCambio, 5) !== 1.00000) {
            $validator->errors()->add(
                "ResumenFactura.CodigoTipoMoneda.TipoCambio",
                "The exchange rate must be 1.00000 when the currency code is CRC."
            );
        }
    }

    //---------------------------Field TotalServGravados
    protected function validateTotalServGravadosRequired($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];

        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'] ?? '';
            $taxes = $line['Impuesto'] ?? [];

            if ($this->isServiceCabys($cabys) && !isset($this->data['ResumenFactura']['TotalServGravados']) && $this->hasGravadoTax($taxes)) {
                $validator->errors()->add('TotalServGravados', 'The field TotalServGravados is required for services with CABYS code starting with 5, 6, 7, 8, or 9 and having a IVA tax.');
            }
        }
    }

    protected function validateTotalServGravadosAmount($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalServGravados = (float) ($this->data['ResumenFactura']['TotalServGravados'] ?? 0);


        $totalServicesAmount = 0.0;

        $totalTaxAmount = 0.0;
        $taxExemptionAmount = 0.0;

        foreach ($lines as $line) {
            $cabys = $line['CodigoCABYS'];

            if (!$this->isServiceCabys($cabys)) {
                continue;
            }

            $taxes = $line['Impuesto'] ?? [];
            $hasIVA = false;

            foreach ($taxes as $tax) {
                if ($this->isGravadoTax($tax)) {

                    $hasIVA = true;
                    $totalTaxAmount += (float) ($tax['Monto'] ?? 0);
                    if (isset($tax['Exoneracion']['MontoExoneracion'])) {
                        $taxExemptionAmount += (float) ($tax['Exoneracion']['MontoExoneracion'] ?? 0);
                    }
                }
            }

            if ($hasIVA) {

                $totalServicesAmount += (float) ($line['SubTotal'] ?? 0);
            }
        }

        $expectedAmount = $totalServicesAmount;

        if ($totalTaxAmount > 0 && $taxExemptionAmount > 0) {
            $taxExemptionPercentage = $taxExemptionAmount / $totalTaxAmount;

            $expectedAmount = $totalServicesAmount * (1 - $taxExemptionPercentage);
        }
        
        if (abs($totalServGravados - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalServGravados',
                "TotalServGravados should be the sum of gravado service lines. Expected: {$expectedAmount}, Given: {$totalServGravados}"
            );
        }
    }

    //---------------------------Field TotalServExentos
    protected function validateTotalServExentosRequired($validator)
    {

        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];

        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'];

            $taxes = $line['Impuesto'] ?? [];



            if ($this->isServiceCabys($cabys) && !isset($this->data['ResumenFactura']['TotalServExentos']) && $this->hasExentTax($taxes)) {
                $validator->errors()->add('TotalServExentos', 'The field TotalServExentos is required for services with CABYS code starting with 5, 6, 7, 8, or 9 and having an exent tax.');
            }
        }
    }

    protected function validateTotalServExentosAmount($validator)
    {


        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalServExentos = (float) ($this->data['ResumenFactura']['TotalServExentos'] ?? 0);

        $expectedAmount = 0.0;

        foreach ($lines as $line) {
            $cabys = $line['CodigoCABYS'];

            if (!$this->isServiceCabys($cabys)) {
                continue;
            }

            $taxes = $line['Impuesto'] ?? [];

            if ($this->hasExentTax($taxes)) {
                $expectedAmount += (float) ($line['SubTotal'] ?? 0);
            }
        }
        if (abs($totalServExentos - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalServExentos',
                "TotalServExentos should be the sum of exento service lines. Expected: {$expectedAmount}, Given: {$totalServExentos}"
            );
        }
    }


    //---------------------------TotalServExonerado
    protected function validateTotalServExoneradosRequired($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];

        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'];

            $taxes = $line['Impuesto'] ?? [];

            if (!isset($this->data['ResumenFactura']['TotalServExonerados']) && $this->isServiceCabys($cabys) && $this->hasExoneratedTax($taxes)) {
                $validator->errors()->add('TotalServExonerados', 'The field TotalServExonerados is required for services with CABYS code starting with 5, 6, 7, 8, or 9 and having both gravado and exonerated taxes.');
            }
        }
    }
    protected function validateTotalServExoneradosAmount($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalServExonerados = (float) ($this->data['ResumenFactura']['TotalServExonerados'] ?? 0);

        $totalTaxes = 0.0;
        $totalExonerate = 0.0;
        $totalServices = 0.0;

        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'];

            if (!$this->isServiceCabys($cabys)) {
                continue;
            }

            $taxes = $line['Impuesto'] ?? [];

            foreach ($taxes as $tax) {
                if ($this->isGravadoTax($tax) && isset($tax['Exoneracion']['MontoExoneracion']) && $tax['Exoneracion']['MontoExoneracion'] > 0) {
                    $totalServices += (float) ($line['SubTotal'] ?? 0);
                    $totalTaxes += (float) ($tax['Monto'] ?? 0);
                    $totalExonerate += (float) ($tax['Exoneracion']['MontoExoneracion'] ?? 0);
                }
            }
        }

        $expectedAmount = 0.0;
        if ($totalExonerate > 0 && $totalTaxes > 0) {
            $exoneratePercentage = $totalExonerate / $totalTaxes;
            $expectedAmount = $totalServices * $exoneratePercentage;
        }

        if ($totalExonerate > 0 && abs($totalServExonerados - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalServExonerados',
                "TotalServExonerados should be the sum of exonerated service lines. Expected: {$expectedAmount}, Given: {$totalServExonerados}"
            );
        }
    }

    //---------------------------TotalServNoSujeto
    protected function validateTotalServNoSujetoRequired($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];

        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'];

            $taxes = $line['Impuesto'] ?? [];

            if (!isset($this->data['ResumenFactura']['TotalServNoSujeto']) && $this->isServiceCabys($cabys) && !$this->hasGravadoTax($taxes) && !$this->hasExentTax($taxes) && !$this->hasExoneratedTax($taxes)) {
                $validator->errors()->add('TotalServNoSujeto', 'The field TotalServNoSujeto is required for services with CABYS code starting with 5, 6, 7, 8, or 9 and having not subject taxes.');
            }
        }
    }

    protected function validateTotalServNoSujetoAmount($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalServNoSujeto = (float) ($this->data['ResumenFactura']['TotalServNoSujeto'] ?? 0);

        $expectedAmount = 0.0;

        foreach ($lines as $line) {
            $cabys = $line['CodigoCABYS'];

            if (!$this->isServiceCabys($cabys)) {
                continue;
            }

            $taxes = $line['Impuesto'] ?? [];

            if ($this->hasNoSubjectTax($taxes)) {
                $expectedAmount += (float) ($line['SubTotal'] ?? 0);
            }
        }
        if (abs($totalServNoSujeto - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalServNoSujeto',
                "TotalServNoSujeto should be the sum of no sujeto service lines. Expected: {$expectedAmount}, Given: {$totalServNoSujeto}"
            );
        }
    }

    //---------------------------TotalMerGravados
    protected function validateTotalMerGravadosRequired($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'];

            $taxes = $line['Impuesto'] ?? [];
            $ivaChargedFactory = $line['IVACobradoFabrica'] ?? [];

            if (!isset($ivaChargedFactory) && $this->isMerchandiseCabys($cabys) && !isset($this->data['ResumenFactura']['TotalMerGravados']) && $this->hasGravadoTax($taxes)) {
                $validator->errors()->add('TotalMerGravados', 'The field TotalMerGravados is required for merchandise with CABYS code starting with 0, 1, 2, 3, or 4 and having a IVA tax.');
            }
        }
    }

    protected function validateTotalMerGravadosAmount($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalMerGravados = (float) ($this->data['ResumenFactura']['TotalMerGravados'] ?? 0);

        $totalMerchandiseAmount = 0.0;
        $totalTaxAmount = 0.0;
        $totalExonerate = 0.0;

        foreach ($lines as $line) {
            $cabys = $line['CodigoCABYS'];

            if (!$this->isMerchandiseCabys($cabys)) {
                continue;
            }

            $taxes = $line['Impuesto'] ?? [];
            $hasIVA = false;

            foreach ($taxes as $tax) {
                if ($this->isGravadoTax($tax)) {
                    $hasIVA = true;
                    $totalTaxAmount += (float) ($tax['Monto'] ?? 0);
                    if (isset($tax['Exoneracion']['MontoExoneracion'])) {
                        $totalExonerate += (float) ($tax['Exoneracion']['MontoExoneracion'] ?? 0);
                    }
                }
            }

            if ($hasIVA) {
                $totalMerchandiseAmount += (float) ($line['SubTotal'] ?? 0);
            }
        }

        $expectedAmount = $totalMerchandiseAmount;

        if ($totalTaxAmount > 0 && $totalExonerate > 0) {
            $exoneratePercentage = $totalExonerate / $totalTaxAmount;
            $expectedAmount = $totalMerchandiseAmount * (1 - $exoneratePercentage);
        }

        if (abs($totalMerGravados - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalMerGravados',
                "TotalMerGravados should be the sum of gravado merchandise lines. Expected: {$expectedAmount}, Given: {$totalMerGravados}"
            );
        }
    }

    //---------------------------TotalMeranciasExentas
    protected function validateTotalMerExentasRequired($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'];

            $taxes = $line['Impuesto'] ?? [];

            if ($this->isMerchandiseCabys($cabys) && !isset($this->data['ResumenFactura']['TotalMercanciasExentas']) && $this->hasExentTax($taxes)) {
                $validator->errors()->add('TotalMercanciasExentas', 'The field TotalMercanciasExentas is required for merchandise with CABYS code starting with 0, 1, 2, 3, or 4 and having an exent tax.');
            }
        }
    }

    protected function validateTotalMerExentasAmount($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalMerExentas = (float) ($this->data['ResumenFactura']['TotalMercanciasExentas'] ?? 0);

        $expectedAmount = 0.0;

        foreach ($lines as $line) {
            $cabys = $line['CodigoCABYS'];

            if (!$this->isMerchandiseCabys($cabys)) {
                continue;
            }

            $taxes = $line['Impuesto'] ?? [];

            if ($this->hasExentTax($taxes)) {
                $expectedAmount += (float) ($line['SubTotal'] ?? 0);
            }
        }
        if (abs($totalMerExentas - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalMercanciasExentas',
                "TotalMercanciasExentas should be the sum of exento merchandise lines. Expected: {$expectedAmount}, Given: {$totalMerExentas}"
            );
        }
    }

    protected function validateTotalMerExoneradasRequired($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'];

            $taxes = $line['Impuesto'] ?? [];

            if ($this->isMerchandiseCabys($cabys) && !isset($this->data['ResumenFactura']['TotalMercExonerada']) && $this->hasExoneratedTax($taxes)) {
                $validator->errors()->add('TotalMercExonerada', 'The field TotalMercExonerada is required for merchandise with CABYS code starting with 0, 1, 2, 3, or 4 and having both gravado and exonerated taxes.');
            }
        }
    }

    protected function validateTotalMercExoneradaAmount($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalMercExonerada = (float) ($this->data['ResumenFactura']['TotalMercExonerada'] ?? 0);

        $totalTaxes = 0.0;
        $totalExonerate = 0.0;
        $totalMerc = 0.0;

        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'];

            if (!$this->isMerchandiseCabys($cabys)) {
                continue;
            }

            $taxes = $line['Impuesto'] ?? [];

            foreach ($taxes as $tax) {
                if ($this->isGravadoTax($tax) && isset($tax['Exoneracion']['MontoExoneracion']) && $tax['Exoneracion']['MontoExoneracion'] > 0) {
                    $totalMerc += (float) ($line['SubTotal'] ?? 0);
                    $totalTaxes += (float) ($tax['Monto'] ?? 0);
                    $totalExonerate += (float) ($tax['Exoneracion']['MontoExoneracion'] ?? 0);
                }
            }
        }

        $expectedAmount = 0.0;
        if ($totalExonerate > 0 && $totalTaxes > 0) {
            $exoneratePercentage = $totalExonerate / $totalTaxes;
            $expectedAmount = $totalMerc * $exoneratePercentage;
        }

        if ($totalExonerate > 0 && abs($totalMercExonerada - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalMercExonerada',
                "TotalMercExonerada should be the sum of exonerated merchandise lines. Expected: {$expectedAmount}, Given: {$totalMercExonerada}"
            );
        }
    }

    //---------------------------TotalMerNoSujeta

    protected function validateTotalMerNoSujetaRequired($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        foreach ($lines as $line) {

            $cabys = $line['CodigoCABYS'];

            $taxes = $line['Impuesto'] ?? [];

            if ($this->isMerchandiseCabys($cabys) && !isset($this->data['ResumenFactura']['TotalMerNoSujeta']) && $this->hasNoSubjectTax($taxes)) {
                $validator->errors()->add('TotalMerNoSujeta', 'The field TotalMerNoSujeta is required for merchandise with CABYS code starting with 0, 1, 2, 3, or 4 and having not subject taxes.');
            }
        }
    }

    protected function validateTotalMerNoSujetaAmount($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalMerNoSujeta = (float) ($this->data['ResumenFactura']['TotalMerNoSujeta'] ?? 0);

        $expectedAmount = 0.0;

        foreach ($lines as $line) {
            $cabys = $line['CodigoCABYS'];

            if (!$this->isMerchandiseCabys($cabys)) {
                continue;
            }

            $taxes = $line['Impuesto'] ?? [];

            if ($this->hasNoSubjectTax($taxes)) {
                $expectedAmount += (float) ($line['SubTotal'] ?? 0);
            }
        }
        if (abs($totalMerNoSujeta - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalMerNoSujeta',
                "TotalMerNoSujeta should be the sum of no sujeto merchandise lines. Expected: {$expectedAmount}, Given: {$totalMerNoSujeta}"
            );
        }
    }

    //---------------------------TotalGravado
    protected function validateTotalGravado($validator)
    {
        $totalGravado = (float) ($this->data['ResumenFactura']['TotalGravado'] ?? 0);
        $expectedAmount = 0.0;

        //dd($this->data['ResumenFactura']['TotalServGravados'], $this->data['ResumenFactura']['TotalMercanciasGravadas']);

        if (isset($this->data['ResumenFactura']['TotalServGravados']) && isset($this->data['ResumenFactura']['TotalMercanciasGravadas'])) {

            $expectedAmount = (float) ($this->data['ResumenFactura']['TotalServGravados']) + (float) ($this->data['ResumenFactura']['TotalMercanciasGravadas']);
        }


        if (abs($totalGravado - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalGravado',
                "TotalGravado should be the sum of TotalServGravados and TotalMercanciasGravadas. Expected: {$expectedAmount}, Given: {$totalGravado}"
            );
        }
    }

    //---------------------------TotalExento
    protected function validateTotalExento($validator)
    {

        $totalExento = (float) ($this->data['ResumenFactura']['TotalExento'] ?? 0);
        $expectedAmount = 0.0;

        if (isset($this->data['ResumenFactura']['TotalServExentos']) && isset($this->data['ResumenFactura']['TotalMercanciasExentas'])) {
            $expectedAmount = (float) ($this->data['ResumenFactura']['TotalServExentos'] ?? 0) + (float) ($this->data['ResumenFactura']['TotalMercanciasExentas'] ?? 0);
        }

        if (abs($totalExento - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalExento',
                "TotalExento should be the sum of TotalServExentos and TotalMercanciasExentas. Expected: {$expectedAmount}, Given: {$totalExento}"
            );
        }
    }

    //---------------------------TotalExonerado
    protected function validateTotalExonerado($validator)
    {
        $totalExonerado = (float) ($this->data['ResumenFactura']['TotalExonerado'] ?? 0);
        $expectedAmount = 0.0;

        if (isset($this->data['ResumenFactura']['TotalServExonerados']) && isset($this->data['ResumenFactura']['TotalMercExonerada'])) {
            $expectedAmount = (float) ($this->data['ResumenFactura']['TotalServExonerados'] ?? 0) + (float) ($this->data['ResumenFactura']['TotalMercExonerada'] ?? 0);
        }

        if (abs($totalExonerado - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalExonerado',
                "TotalExonerado should be the sum of TotalServExonerados and TotalMercExonerada. Expected: {$expectedAmount}, Given: {$totalExonerado}"
            );
        }
    }

    //---------------------------TotalNoSujeto
    protected function validateTotalNoSujeto($validator)
    {

        $totalNoSujeto = (float) ($this->data['ResumenFactura']['TotalNoSujeto'] ?? 0);
        $expectedAmount = 0.0;

        if (isset($this->data['ResumenFactura']['TotalServNoSujeto']) && isset($this->data['ResumenFactura']['TotalMercNoSujeta'])) {
            $expectedAmount = (float) ($this->data['ResumenFactura']['TotalServNoSujeto'] ?? 0) + (float) ($this->data['ResumenFactura']['TotalMercNoSujeta'] ?? 0);
        }

        if (abs($totalNoSujeto - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalNoSujeto',
                "TotalNoSujeto should be the sum of TotalServNoSujeto and TotalMercNoSujeta. Expected: {$expectedAmount}, Given: {$totalNoSujeto}"
            );
        }
    }

    //---------------------------TotalVenta
    protected function validateTotalVenta($validator)
    {
        $totalVenta = (float) ($this->data['ResumenFactura']['TotalVenta'] ?? 0);
        $expectedAmount = 0.0;

        if (isset($this->data['ResumenFactura']['TotalGravado']) && isset($this->data['ResumenFactura']['TotalExento']) && isset($this->data['ResumenFactura']['TotalExonerado']) && isset($this->data['ResumenFactura']['TotalNoSujeto'])) {
            $expectedAmount = (float) ($this->data['ResumenFactura']['TotalGravado'] ?? 0) + (float) ($this->data['ResumenFactura']['TotalExento'] ?? 0) + (float) ($this->data['ResumenFactura']['TotalExonerado'] ?? 0) + (float) ($this->data['ResumenFactura']['TotalNoSujeto'] ?? 0);
        }

        if (abs($totalVenta - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalVenta',
                "TotalVenta should be the sum of TotalGravado, TotalExento, TotalExonerado, and TotalNoSujeto. Expected: {$expectedAmount}, Given: {$totalVenta}"
            );
        }
    }

    //---------------------------TotalDescuentos
    protected function validateTotalDescuentos($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalDiscounts = (float) ($this->data['ResumenFactura']['TotalDescuentos'] ?? 0);
        $expectedAmount = 0.0;

        foreach ($lines as $line) {
            if ($line['Descuento'] ?? false) {
                $expectedAmount += (float) ($line['Descuento']['MontoDescuento'] ?? 0);
            }
        }

        if (abs($totalDiscounts - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalDescuentos',
                "TotalDescuentos should be the sum of all line discounts. Expected: {$expectedAmount}, Given: {$totalDiscounts}"
            );
        }
    }

    //---------------------------TotalVentaNeta
    protected function validateTotalVentaNeta($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalVentaNeta = (float) ($this->data['ResumenFactura']['TotalVentaNeta'] ?? 0);
        $expectedAmount = 0.0;

        if (isset($lines['ResumenFactura']['TotalVenta']) && isset($lines['ResumenFactura']['TotalDescuentos'])) {
            $expectedAmount = (float) ($lines['ResumenFactura']['TotalVenta'] ?? 0) - (float) ($lines['ResumenFactura']['TotalDescuentos'] ?? 0);
        }

        if (abs($totalVentaNeta - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalVentaNeta',
                "TotalVentaNeta should be TotalVenta minus TotalDescuentos. Expected: {$expectedAmount}, Given: {$totalVentaNeta}"
            );
        }
    }

    //---------------------------TotalImpuesto
    protected function validateTotalImpuestoRequired($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalTax = (float) ($this->data['ResumenFactura']['TotalImpuesto'] ?? 0);
        $expectedAmount = 0.0;
        $isRequired = false;

        foreach ($lines as $line) {
            $taxes = $line['Impuesto'] ?? [];
            foreach ($taxes as $tax) {
                if ($tax['Codigo'] ?? false) {
                    $isRequired = true;
                    break 2;
                }
            }
        }
        if ($isRequired && !isset($this->data['ResumenFactura']['TotalImpuesto'])) {
            $validator->errors()->add('ResumenFactura.TotalImpuesto', 'The field TotalImpuesto is required when at least one line has a tax.');
        }
    }

    protected function validateTotalImpuestoAmount($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];

        $totalTax = (float) ($this->data['ResumenFactura']['TotalImpuesto'] ?? 0);
        $expectedAmount = 0.0;

        foreach ($lines as $line) {
            if (isset($line['ImpuestoAsumidoEmisorFabrica']) && $line['ImpuestoAsumidoEmisorFabrica'] && $line['ImpuestoAsumidoEmisorFabrica'] > 0) {
                $expectedAmount += (float) ($line['ImpuestoAsumidoEmisorFabrica'] ?? 0);
                continue;
            }
            $taxes = $line['Impuesto'] ?? [];
            foreach ($taxes as $tax) {
                if (isset($tax['Exoneracion']) && isset($tax['Exoneracion']['MontoExoneracion']) && $tax['Exoneracion']['MontoExoneracion'] > 0) {
                    $expectedAmount += (float) $line['ImpuestoNeto'] - (float) ($tax['Exoneracion']['MontoExoneracion']);
                } elseif ($tax['Monto'] ?? false) {
                    $expectedAmount += (float) ($tax['Monto'] ?? 0);
                }
            }
        }

        if (abs($totalTax - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalImpuesto',
                "TotalImpuesto should be the sum of all tax amounts from the lines. Expected: {$expectedAmount}, Given: {$totalTax}"
            );
        }
    }

    //---------------------------TotalImpAsumEmisorFabrica
    protected function validateTotalImpAsumEmisorFabrica($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $totalImpAsumEmisorFabrica = (float) ($this->data['ResumenFactura']['TotalImpAsumEmisorFabrica'] ?? 0);
        $expectedAmount = 0.0;
        $hasTax = false;

        foreach ($lines as $line) {
            if ($line['ImpuestoAsumidoEmisorFabrica'] ?? false) {
                $expectedAmount += (float) ($line['ImpuestoAsumidoEmisorFabrica'] ?? 0);
                $hasTax = true;
            }
        }

        if (abs($totalImpAsumEmisorFabrica - $expectedAmount) > 0.001 && $hasTax) {
            $validator->errors()->add(
                'ResumenFactura.TotalImpAsumEmisorFabrica',
                "TotalImpAsumEmisorFabrica should be the sum of all ImpuestoAsumidoEmisorFabrica from the lines. Expected: {$expectedAmount}, Given: {$totalImpAsumEmisorFabrica}"
            );
        }
    }

    //--------------------------TotalIvaDevuelto

    protected function validateTotalIvaDevuelto($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];


        $totalIvaDevuelto = (float) ($this->data['ResumenFactura']['TotalIvaDevuelto'] ?? 0);
        $expectedAmount = 0.0;
        $hasTax = false;

        foreach ($lines as $line) {
            $cabys = $lines['CodigoCABYS'] ?? '';

            if ($this->isHealthService($cabys) && $this->isCreditCardPayment($this->data['MedioPago'] ?? [])) {
                $taxes = $line['Impuesto'] ?? [];
                foreach ($taxes as $tax) {
                    if ($this->isIVATax($tax)) {
                        $expectedAmount += (float) ($tax['Monto'] ?? 0);
                        $hasTax = true;
                    }
                }
            }
        }

        if (abs($totalIvaDevuelto - $expectedAmount) > 0.001 && $hasTax) {
            $validator->errors()->add(
                'ResumenFactura.TotalIvaDevuelto',
                "TotalIvaDevuelto should be the sum of all IVADevuelto from the lines. Expected: {$expectedAmount}, Given: {$totalIvaDevuelto}"
            );
        }
    }

    //---------------------------TotalOtrosCargos
    protected function validateTotalOtrosCargos($validator)
    {
        $otherCharges = $this->data['OtrosCargos'] ?? [];
        $totalOtherCharges = (float) ($this->data['ResumenFactura']['TotalOtrosCargos'] ?? 0);
        $expectedAmount = 0.0;

        foreach ($otherCharges as $charge) {
            if ($charge['MontoCargo'] ?? false) {
                $expectedAmount += (float) ($charge['MontoCargo'] ?? 0);
            }
        }

        if (abs($totalOtherCharges - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalOtrosCargos',
                "TotalOtrosCargos should be the sum of all OtrosCargos from the lines. Expected: {$expectedAmount}, Given: {$totalOtherCharges}"
            );
        }
    }

    //---------------------------TotalMedioPago
    protected function validateTotalMedioPagoRequired($validator)
    {
        $payment = $this->data['ResumenFactura']['MedioPago'] ?? [];
        if (!empty($payment) && count($payment) > 1) {
            foreach ($payment as $pay) {
                if (!isset($pay['TotalMedioPago'])) {
                    $validator->errors()->add('TotalMedioPago', 'The field TotalMedioPago is required when there are more than 1 payment method.');
                }
            }
        }
    }

    //---------------------------TotalComprobante

    protected function validateTotalComprobante($validator)
    {
        $summary = $this->data['ResumenFactura'] ?? [];
        $totalVoucher = (float) ($summary['TotalComprobante'] ?? 0);
        $totalNetSale = (float) ($summary['TotalVentaNeta'] ?? 0);
        $totalTax = (float) ($summary['TotalImpuesto'] ?? 0);
        $totalOtherCharges = (float) ($summary['TotalOtrosCargos'] ?? 0);
        $totalReturnedVAT = (float) ($summary['TotalIvaDevuelto'] ?? 0);
        $expectedAmount = 0.0;


        $expectedAmount = $totalNetSale + $totalTax + $totalOtherCharges - $totalReturnedVAT;

        if (abs($totalVoucher - $expectedAmount) > 0.001) {
            $validator->errors()->add(
                'ResumenFactura.TotalComprobante',
                "TotalComprobante should be TotalVentaNeta plus TotalImpuesto plus TotalOtrosCargos minus TotalIvaDevuelto. Expected: {$expectedAmount}, Given: {$totalVoucher}"
            );
        }
    }

    //---------------------------TotalMedioPago
    protected function validateTotalMedioPago($validator)
    {
        $mediosPago = $this->data['ResumenFactura']['MedioPago'] ?? [];


        if (is_array($mediosPago) && count($mediosPago) > 1) {
            foreach ($mediosPago as $index => $medio) {
                $totalMedioPago = $medio['TotalMedioPago'] ?? null;

                if (is_null($totalMedioPago) || $totalMedioPago === '') {
                    $validator->errors()->add(
                        "ResumenFactura.MedioPago.{$index}.TotalMedioPago",
                        'The field TotalMedioPago is required when more than one payment method is used.'
                    );
                }
            }
        }
    }

    protected function validateTotalDesgloseImpuesto($validator)
    {
        $lines = $this->data['DetalleServicio']['LineaDetalle'] ?? [];
        $hasTaxes = false;

        foreach ($lines as $line) {
            $taxes = $line['Impuesto'] ?? null;
            
            if (!empty($taxes) && is_array($taxes) && count($taxes) > 0) {
                $hasTaxes = true;
                break;
            }
        }
        if ($hasTaxes) {
            $totalDesgloseImpuesto = $this->data['ResumenFactura']['TotalDesgloseImpuesto'] ?? null;
            
            if (empty($totalDesgloseImpuesto) || !is_array($totalDesgloseImpuesto) || count($totalDesgloseImpuesto) === 0) {
                $validator->errors()->add(
                    'ResumenFactura.TotalDesgloseImpuesto',
                    'The field TotalDesgloseImpuesto is required when using detail lines with tax calculation.'
                );
            }
        }
    }




    //---------------------------Helpers

    private function isCreditCardPayment($payment): bool
    {
        return isset($payment['TipoMedioPago']) && $payment['TipoMedioPago'] === '02';
    }
    private function isHealthService($cabys): bool
    {
        return preg_match('/^93/', $cabys) === 1;
    }
    private function isServiceCabys(string $cabys): bool
    {
        return preg_match('/^[5-9]/', $cabys) === 1;
    }

    private function isMerchandiseCabys(string $cabys): bool
    {
        return preg_match('/^[0-4]/', $cabys) === 1;
    }

    private function isIVATax($tax): bool
    {
        return isset($tax['Codigo']) && in_array($tax['Codigo'], ['01', '07']);
    }

    private function isGravadoTax($tax): bool
    {
        return $this->isIVATax($tax) && in_array($tax['CodigoTarifaIVA'], ['02', '03', '04', '05', '06', '07', '08', '09']);
    }

    private function isExentTax($tax): bool
    {
        return $this->isIVATax($tax) && $tax['CodigoTarifaIVA'] === '10';
    }

    private function isNotSubjectTax($tax): bool
    {
        return $this->isIVATax($tax) && in_array($tax['CodigoTarifaIVA'], ['01','11']);
    }

    private function hasGravadoTax($taxes): bool
    {
        foreach ($taxes as $tax) {
            if ($this->isGravadoTax($tax)) {
                return true;
            }
        }
        return false;
    }

    private function hasExentTax($taxes): bool
    {

        foreach ($taxes as $tax) {

            if ($this->isExentTax($tax)) {
                return true;
            }
        }
        return false;
    }
    private function hasExoneratedTax($taxes): bool
    {
        foreach ($taxes as $tax) {
            if ($this->isGravadoTax($tax) && isset($tax['Exoneracion']['MontoExoneracion']) && $tax['Exoneracion']['MontoExoneracion'] > 0) {
                return true;
            }
        }
        return false;
    }

    private function hasNoSubjectTax($taxes): bool
    {
        foreach ($taxes as $tax) {
            if ($this->isNotSubjectTax($tax)) {
                return true;
            }
        }
        return false;
    }
}
