<?php

namespace Daniv04\HaciendaPackage\Tests\Unit;

use Daniv04\HaciendaPackage\Services\Validators\InvoiceSummaryValidator;
use Daniv04\HaciendaPackage\Tests\TestCase;
use Illuminate\Support\MessageBag;

class InvoiceSummaryValidatorTest extends TestCase
{
    private function makeValidatorStub(): object
    {
        $errors = new MessageBag();

        return new class ($errors) {
            public function __construct(private MessageBag $bag) {}

            public function errors(): MessageBag
            {
                return $this->bag;
            }
        };
    }

    private function makeMerchandiseLine(array $overrides = []): array
    {
        return array_merge([
            'NumeroLinea' => 1,
            'CodigoCABYS' => '4111000000000', // mercancía (comienza con 4)
            'Cantidad' => 1,
            'UnidadMedida' => 'Unid',
            'Detalle' => 'Producto test',
            'PrecioUnitario' => 1000.0,
            'MontoTotal' => 1000.0,
            'SubTotal' => 1000.0,
            'BaseImponible' => 1000.0,
            'ImpuestoNeto' => 130.0,
            'MontoTotalLinea' => 1130.0,
            'Impuesto' => [[
                'Codigo' => '01',
                'CodigoTarifaIVA' => '08',
                'Tarifa' => 13.0,
                'Monto' => 130.0,
            ]],
        ], $overrides);
    }

    private function makeSummaryPayload(array $lines, array $resumen): array
    {
        return [
            'DetalleServicio' => ['LineaDetalle' => $lines],
            'ResumenFactura' => array_merge([
                'CodigoTipoMoneda' => ['CodigoMoneda' => 'CRC', 'TipoCambio' => 1.0],
                'TotalServGravados' => 0,
                'TotalServExentos' => 0,
                'TotalServExonerado' => 0,
                'TotalServNoSujeto' => 0,
                'TotalMercanciasGravadas' => 0,
                'TotalMercanciasExentas' => 0,
                'TotalMercExonerada' => 0,
                'TotalMercNoSujeta' => 0,
                'TotalGravado' => 0,
                'TotalExento' => 0,
                'TotalExonerado' => 0,
                'TotalNoSujeto' => 0,
                'TotalVenta' => 0,
                'TotalDescuentos' => 0,
                'TotalVentaNeta' => 0,
                'TotalImpuesto' => 0,
                'TotalOtrosCargos' => 0,
                'TotalIVADevuelto' => 0,
                'TotalComprobante' => 0,
                'TotalDesgloseImpuesto' => [],
                'MedioPago' => [['TipoMedioPago' => '01', 'TotalMedioPago' => 0]],
            ], $resumen),
        ];
    }

    // ── TotalMerGravados usa MontoTotal (bruto), no SubTotal ──────────────────

    public function test_total_mer_gravados_acepta_monto_total_bruto(): void
    {
        // bruto=1000, descuento=100, SubTotal=900, IVA(13% sobre 900)=117, total=1017
        $line = $this->makeMerchandiseLine([
            'MontoTotal' => 1000.0,
            'SubTotal' => 900.0,
            'BaseImponible' => 900.0,
            'ImpuestoNeto' => 117.0,
            'MontoTotalLinea' => 1017.0,
            'Descuento' => [['MontoDescuento' => 100.0, 'CodigoDescuento' => '01']],
            'Impuesto' => [[
                'Codigo' => '01',
                'CodigoTarifaIVA' => '08',
                'Tarifa' => 13.0,
                'Monto' => 117.0,
            ]],
        ]);

        $payload = $this->makeSummaryPayload([$line], [
            'TotalMercanciasGravadas' => 1000.0,
                        'TotalGravado' => 1000.0,
            'TotalVenta' => 1000.0,
            'TotalDescuentos' => 100.0,
            'TotalVentaNeta' => 900.0,
            'TotalImpuesto' => 117.0,
            'TotalComprobante' => 1017.0,
            'TotalDesgloseImpuesto' => [['Codigo' => '01', 'CodigoTarifaIVA' => '08', 'TotalMontoImpuesto' => 117.0]],
            'MedioPago' => [['TipoMedioPago' => '01', 'TotalMedioPago' => 1017.0]],
        ]);

        $stub = $this->makeValidatorStub();
        (new InvoiceSummaryValidator($payload))->validate($stub);

        $this->assertEmpty($stub->errors()->get('ResumenFactura.TotalMercanciasGravadas'));
        $this->assertEmpty($stub->errors()->get('ResumenFactura.TotalVentaNeta'));
        $this->assertEmpty($stub->errors()->get('ResumenFactura.TotalComprobante'));
    }

    public function test_total_mer_gravados_rechaza_valor_neto_cuando_hay_descuento(): void
    {
        // Enviar SubTotal (900) en lugar de MontoTotal (1000) debe producir error
        $line = $this->makeMerchandiseLine([
            'MontoTotal' => 1000.0,
            'SubTotal' => 900.0,
            'Descuento' => [['MontoDescuento' => 100.0, 'CodigoDescuento' => '01']],
            'Impuesto' => [[
                'Codigo' => '01',
                'CodigoTarifaIVA' => '08',
                'Tarifa' => 13.0,
                'Monto' => 117.0,
            ]],
        ]);

        $payload = $this->makeSummaryPayload([$line], [
            'TotalMercanciasGravadas' => 900.0, // incorrecto: neto en lugar de bruto
                        'TotalGravado' => 900.0,
            'TotalVenta' => 900.0,
            'TotalDescuentos' => 100.0,
            'TotalVentaNeta' => 800.0,
            'TotalImpuesto' => 117.0,
            'TotalComprobante' => 917.0,
            'TotalDesgloseImpuesto' => [['Codigo' => '01', 'CodigoTarifaIVA' => '08', 'TotalMontoImpuesto' => 117.0]],
            'MedioPago' => [['TipoMedioPago' => '01', 'TotalMedioPago' => 917.0]],
        ]);

        $stub = $this->makeValidatorStub();
        (new InvoiceSummaryValidator($payload))->validate($stub);

        $this->assertNotEmpty($stub->errors()->get('ResumenFactura.TotalMercanciasGravadas'));
    }

    public function test_total_mer_gravados_sin_descuento_acepta_monto_total(): void
    {
        // Sin descuento: MontoTotal = SubTotal, no hay diferencia
        $line = $this->makeMerchandiseLine();

        $payload = $this->makeSummaryPayload([$line], [
            'TotalMercanciasGravadas' => 1000.0,
                        'TotalGravado' => 1000.0,
            'TotalVenta' => 1000.0,
            'TotalDescuentos' => 0.0,
            'TotalVentaNeta' => 1000.0,
            'TotalImpuesto' => 130.0,
            'TotalComprobante' => 1130.0,
            'TotalDesgloseImpuesto' => [['Codigo' => '01', 'CodigoTarifaIVA' => '08', 'TotalMontoImpuesto' => 130.0]],
            'MedioPago' => [['TipoMedioPago' => '01', 'TotalMedioPago' => 1130.0]],
        ]);

        $stub = $this->makeValidatorStub();
        (new InvoiceSummaryValidator($payload))->validate($stub);

        $this->assertEmpty($stub->errors()->get('ResumenFactura.TotalMercanciasGravadas'));
        $this->assertEmpty($stub->errors()->get('ResumenFactura.TotalVentaNeta'));
    }

    // ── TotalVentaNeta = TotalVenta − TotalDescuentos ─────────────────────────

    public function test_total_venta_neta_correcto(): void
    {
        $line = $this->makeMerchandiseLine([
            'MontoTotal' => 1000.0,
            'SubTotal' => 900.0,
            'Descuento' => [['MontoDescuento' => 100.0, 'CodigoDescuento' => '01']],
            'Impuesto' => [[
                'Codigo' => '01', 'CodigoTarifaIVA' => '08',
                'Tarifa' => 13.0, 'Monto' => 117.0,
            ]],
        ]);

        $payload = $this->makeSummaryPayload([$line], [
            'TotalMercanciasGravadas' => 1000.0,
                        'TotalGravado' => 1000.0,
            'TotalVenta' => 1000.0,
            'TotalDescuentos' => 100.0,
            'TotalVentaNeta' => 900.0, // 1000 - 100 ✓
            'TotalImpuesto' => 117.0,
            'TotalComprobante' => 1017.0,
            'TotalDesgloseImpuesto' => [['Codigo' => '01', 'CodigoTarifaIVA' => '08', 'TotalMontoImpuesto' => 117.0]],
            'MedioPago' => [['TipoMedioPago' => '01', 'TotalMedioPago' => 1017.0]],
        ]);

        $stub = $this->makeValidatorStub();
        (new InvoiceSummaryValidator($payload))->validate($stub);

        $this->assertEmpty($stub->errors()->get('ResumenFactura.TotalVentaNeta'));
    }

    public function test_total_venta_neta_incorrecto_genera_error(): void
    {
        $line = $this->makeMerchandiseLine([
            'MontoTotal' => 1000.0,
            'SubTotal' => 900.0,
            'Descuento' => [['MontoDescuento' => 100.0, 'CodigoDescuento' => '01']],
            'Impuesto' => [[
                'Codigo' => '01', 'CodigoTarifaIVA' => '08',
                'Tarifa' => 13.0, 'Monto' => 117.0,
            ]],
        ]);

        $payload = $this->makeSummaryPayload([$line], [
            'TotalMercanciasGravadas' => 1000.0,
                        'TotalGravado' => 1000.0,
            'TotalVenta' => 1000.0,
            'TotalDescuentos' => 100.0,
            'TotalVentaNeta' => 800.0, // doble deducción — incorrecto
            'TotalImpuesto' => 117.0,
            'TotalComprobante' => 917.0,
            'TotalDesgloseImpuesto' => [['Codigo' => '01', 'CodigoTarifaIVA' => '08', 'TotalMontoImpuesto' => 117.0]],
            'MedioPago' => [['TipoMedioPago' => '01', 'TotalMedioPago' => 917.0]],
        ]);

        $stub = $this->makeValidatorStub();
        (new InvoiceSummaryValidator($payload))->validate($stub);

        $this->assertNotEmpty($stub->errors()->get('ResumenFactura.TotalVentaNeta'));
    }

    public function test_total_venta_neta_sin_descuento_es_igual_a_total_venta(): void
    {
        $line = $this->makeMerchandiseLine();

        $payload = $this->makeSummaryPayload([$line], [
            'TotalMercanciasGravadas' => 1000.0,
                        'TotalGravado' => 1000.0,
            'TotalVenta' => 1000.0,
            'TotalDescuentos' => 0.0,
            'TotalVentaNeta' => 1000.0,
            'TotalImpuesto' => 130.0,
            'TotalComprobante' => 1130.0,
            'TotalDesgloseImpuesto' => [['Codigo' => '01', 'CodigoTarifaIVA' => '08', 'TotalMontoImpuesto' => 130.0]],
            'MedioPago' => [['TipoMedioPago' => '01', 'TotalMedioPago' => 1130.0]],
        ]);

        $stub = $this->makeValidatorStub();
        (new InvoiceSummaryValidator($payload))->validate($stub);

        $this->assertEmpty($stub->errors()->get('ResumenFactura.TotalVentaNeta'));
    }
}
