<?php

namespace Daniv04\HaciendaPackage\Services\Validators;

use Daniv04\HaciendaPackage\Exceptions\InvalidReceiptException;

class CalculationValidatorService
{
    const TOLERANCE = 0.01;

    /**
     * Validate all mathematical calculations before persisting
     */
    public function validate(array $payload, string $receiptType): void
    {
        $this->validateDetailLines($payload);
        $this->validateTaxes($payload);
        $this->validateTotal($payload);
        $this->validateDiscounts($payload);
    }

    /**
     * Validate MontoTotalLinea = Cantidad × PrecioUnitario for each line
     */
    private function validateDetailLines(array $payload): void
    {
        $lines = $payload['DetalleServicio']['LineaDetalle'] ?? [];

        foreach ($lines as $line) {
            $cantidad = (float) ($line['Cantidad'] ?? 0);
            $precioUnitario = (float) ($line['PrecioUnitario'] ?? 0);
            $montoTotalLinea = (float) ($line['MontoTotalLinea'] ?? 0);

            $expected = $cantidad * $precioUnitario;

            if (abs($expected - $montoTotalLinea) > self::TOLERANCE) {
                throw new InvalidReceiptException(
                    "Línea {$line['NumeroLinea']}: MontoTotalLinea ({$montoTotalLinea}) " .
                    "≠ Cantidad ({$cantidad}) × PrecioUnitario ({$precioUnitario}) = {$expected}"
                );
            }

            // Validate subtotal calculations per line if there are discounts
            if (!empty($line['Descuento']['MontoDescuento'])) {
                $descuento = (float) $line['Descuento']['MontoDescuento'];
                $subTotal = $cantidad * $precioUnitario - $descuento;

                if ($subTotal < 0) {
                    throw new InvalidReceiptException(
                        "Línea {$line['NumeroLinea']}: Descuento ({$descuento}) " .
                        "es mayor que el subtotal ({$cantidad * $precioUnitario})"
                    );
                }
            }
        }
    }

    /**
     * Validate total impuesto matches sum of line taxes
     */
    private function validateTaxes(array $payload): void
    {
        $summary = $payload['ResumenFactura'] ?? [];
        $totalImpuestoSummary = (float) ($summary['TotalImpuesto'] ?? 0);

        $calculatedTax = 0.0;
        $lines = $payload['DetalleServicio']['LineaDetalle'] ?? [];

        foreach ($lines as $line) {
            $lineImpuestos = $line['Impuesto'] ?? [];
            foreach ($lineImpuestos as $impuesto) {
                $calculatedTax += (float) ($impuesto['Monto'] ?? 0);
            }
        }

        if (abs($calculatedTax - $totalImpuestoSummary) > self::TOLERANCE) {
            throw new InvalidReceiptException(
                "TotalImpuesto ({$totalImpuestoSummary}) ≠ suma de impuestos de líneas ({$calculatedTax})"
            );
        }
    }

    /**
     * Validate TotalComprobante = TotalGravado + TotalExento + TotalNoSujeto - TotalDescuentos + TotalImpuesto
     */
    private function validateTotal(array $payload): void
    {
        $summary = $payload['ResumenFactura'] ?? [];

        $totalGravado = (float) ($summary['TotalGravado'] ?? 0);
        $totalExento = (float) ($summary['TotalExento'] ?? 0);
        $totalNoSujeto = (float) ($summary['TotalNoSujeto'] ?? 0);
        $totalDescuentos = (float) ($summary['TotalDescuentos'] ?? 0);
        $totalImpuesto = (float) ($summary['TotalImpuesto'] ?? 0);
        $totalComprobante = (float) ($summary['TotalComprobante'] ?? 0);

        $expectedTotal = $totalGravado + $totalExento + $totalNoSujeto - $totalDescuentos + $totalImpuesto;

        if (abs($expectedTotal - $totalComprobante) > self::TOLERANCE) {
            throw new InvalidReceiptException(
                "TotalComprobante ({$totalComprobante}) ≠ " .
                "TotalGravado ({$totalGravado}) + TotalExento ({$totalExento}) + " .
                "TotalNoSujeto ({$totalNoSujeto}) - TotalDescuentos ({$totalDescuentos}) + " .
                "TotalImpuesto ({$totalImpuesto}) = {$expectedTotal}"
            );
        }
    }

    /**
     * Validate total discounts match sum of line discounts
     */
    private function validateDiscounts(array $payload): void
    {
        $summary = $payload['ResumenFactura'] ?? [];
        $totalDescuentosSummary = (float) ($summary['TotalDescuentos'] ?? 0);

        $calculatedDiscounts = 0.0;
        $lines = $payload['DetalleServicio']['LineaDetalle'] ?? [];

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

        if (abs($calculatedDiscounts - $totalDescuentosSummary) > self::TOLERANCE) {
            throw new InvalidReceiptException(
                "TotalDescuentos ({$totalDescuentosSummary}) ≠ suma de descuentos de líneas ({$calculatedDiscounts})"
            );
        }
    }
}
