<?php

use App\Actions\VerifyCustomerVatId;
use App\Models\Customer;
use App\Services\Billing\TaxTreatment;
use App\Services\Tax\FakeVatIdVerifier;
use App\Services\Tax\VatIdVerifier;

/**
 * Checking a VAT number against the EU register.
 *
 * The hole this closes: `vat_id_verified_at` was declared on the model and read
 * by TaxTreatment, and set by no code anywhere. So reverse charge could never
 * trigger, and every EU business customer was invoiced Austrian VAT they cannot
 * reclaim in their own country.
 *
 * The tests are mostly about the third answer. VIES is a gateway onto twenty-seven
 * national registers and any of them is regularly down, so "I could not ask" has
 * to be told apart from "the number is not registered" — one of them must change
 * nothing at all.
 */
function armRegister(): FakeVatIdVerifier
{
    $fake = new FakeVatIdVerifier;
    app()->instance(VatIdVerifier::class, $fake);

    return $fake;
}

function businessWithVatId(string $vatId): Customer
{
    return Customer::factory()->create([
        'customer_type' => Customer::TYPE_BUSINESS,
        'vat_id' => $vatId,
    ]);
}

it('records a number the register knows, and reverse charge then applies', function () {
    armRegister()->registered('NL123456789B01', 'Berger B.V.');
    $customer = businessWithVatId('NL123456789B01');

    app(VerifyCustomerVatId::class)($customer);

    expect($customer->fresh()->hasVerifiedVatId())->toBeTrue()
        // Proven through the tax rule, not through the column: the column is only
        // interesting because of what TaxTreatment does with it.
        ->and(TaxTreatment::for($customer->fresh())->reverseCharge)->toBeTrue()
        ->and(TaxTreatment::for($customer->fresh())->rate)->toBe(0.0);
});

it('refuses a number the register does not know, and keeps charging VAT', function () {
    armRegister()->notRegistered('NL999999999B99');
    $customer = businessWithVatId('NL999999999B99');

    $check = app(VerifyCustomerVatId::class)($customer);

    expect($check->isInvalid())->toBeTrue()
        ->and($customer->fresh()->hasVerifiedVatId())->toBeFalse()
        ->and(TaxTreatment::for($customer->fresh())->reverseCharge)->toBeFalse();
});

it('changes nothing at all when the register is unreachable', function () {
    // The one that matters. An outage must not clear a verification that has been
    // standing for months and quietly move that customer onto the domestic rate —
    // their next invoice would say something different because a national register
    // was asleep.
    $customer = businessWithVatId('NL123456789B01');
    armRegister()->registered('NL123456789B01');
    app(VerifyCustomerVatId::class)($customer);

    $stampedAt = $customer->fresh()->vat_id_verified_at;

    armRegister()->down('NL123456789B01');
    $check = app(VerifyCustomerVatId::class)($customer->fresh());

    expect($check->isUnavailable())->toBeTrue()
        ->and($customer->fresh()->hasVerifiedVatId())->toBeTrue()
        ->and($customer->fresh()->vat_id_verified_at->equalTo($stampedAt))->toBeTrue()
        ->and(TaxTreatment::for($customer->fresh())->reverseCharge)->toBeTrue();
});

it('withdraws reverse charge once the register stops knowing the number', function () {
    // A registration can be revoked. From that moment we are zero-rating against
    // a number that does not exist, which is our liability, not the customer's.
    $customer = businessWithVatId('NL123456789B01');
    armRegister()->registered('NL123456789B01');
    app(VerifyCustomerVatId::class)($customer);

    armRegister()->notRegistered('NL123456789B01');
    app(VerifyCustomerVatId::class)($customer->fresh());

    expect($customer->fresh()->hasVerifiedVatId())->toBeFalse()
        ->and(TaxTreatment::for($customer->fresh())->reverseCharge)->toBeFalse();
});

it('drops the verification when the number is removed', function () {
    $customer = businessWithVatId('NL123456789B01');
    armRegister()->registered('NL123456789B01');
    app(VerifyCustomerVatId::class)($customer);

    $customer->update(['vat_id' => null]);
    $check = app(VerifyCustomerVatId::class)($customer->fresh());

    expect($check)->toBeNull()
        ->and($customer->fresh()->vat_id_verified_at)->toBeNull()
        ->and($customer->fresh()->vat_id_verified_value)->toBeNull();
});

it('never asks the register about a number no member state could hold', function () {
    $fake = armRegister();
    $customer = businessWithVatId('not-a-number');

    $check = app(VerifyCustomerVatId::class)($customer);

    expect($check->isInvalid())->toBeTrue()
        // Not one request spent on something that cannot be a VAT number.
        ->and($fake->asked)->toBeEmpty();
});

it('still charges domestic VAT to a business in our own country', function () {
    // Reverse charge is a cross-border rule. An Austrian business pays Austrian
    // VAT and reclaims it as input tax like any other Austrian buyer.
    armRegister()->registered('ATU12345678');
    $customer = businessWithVatId('ATU12345678');

    app(VerifyCustomerVatId::class)($customer);

    expect($customer->fresh()->hasVerifiedVatId())->toBeTrue()
        ->and(TaxTreatment::for($customer->fresh())->reverseCharge)->toBeFalse()
        ->and(TaxTreatment::for($customer->fresh())->rate)->toBeGreaterThan(0.0);
});

it('still charges VAT to somebody who has recorded themselves as a private person', function () {
    armRegister()->registered('NL123456789B01');
    $customer = Customer::factory()->create([
        'customer_type' => Customer::TYPE_CONSUMER,
        'vat_id' => 'NL123456789B01',
    ]);

    app(VerifyCustomerVatId::class)($customer);

    // The number is real — it is just not theirs to zero the tax with.
    expect($customer->fresh()->hasVerifiedVatId())->toBeTrue()
        ->and(TaxTreatment::for($customer->fresh())->reverseCharge)->toBeFalse();
});

it('asks about every number on record and touches nothing in a dry run', function () {
    armRegister()->registered('NL123456789B01');
    $customer = businessWithVatId('NL123456789B01');

    $this->artisan('clupilot:verify-vat-ids --dry-run')->assertSuccessful();
    expect($customer->fresh()->hasVerifiedVatId())->toBeFalse();

    $this->artisan('clupilot:verify-vat-ids')->assertSuccessful();
    expect($customer->fresh()->hasVerifiedVatId())->toBeTrue();
});
