<?php

namespace App\Services\Billing;

use App\Models\Customer;
use App\Support\CompanyProfile;

/**
 * Which VAT applies to a customer, and why.
 *
 * Only the two clear-cut cases are decided here:
 *
 *  - a customer whose VERIFIED VAT ID belongs to another EU member state pays
 *    no VAT to us; the liability shifts to them (reverse charge), and the
 *    invoice has to say so. Unverified means domestic rate: a self-declared
 *    string must never be able to zero the tax;
 *  - everyone else is charged the seller's domestic rate.
 *
 * Deliberately NOT handled: cross-border sales to private individuals, which
 * are taxed at the customer's own country's rate under the OSS scheme. Doing
 * that needs a maintained rate table per member state and a tax adviser's sign
 * off, not a guess in a config file — so those customers fall back to the
 * domestic rate, which over-collects rather than under-collects.
 */
final readonly class TaxTreatment
{
    /** Reverse charge applies inside the EU only. */
    private const EU_MEMBER_STATES = [
        'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'EL', 'ES', 'FI', 'FR',
        'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO',
        'SE', 'SI', 'SK',
    ];

    private function __construct(
        public float $rate,
        public bool $reverseCharge,
    ) {}

    public static function for(?Customer $customer): self
    {
        // From the Finance page, not from config: an operator can change the
        // rate there and cannot change a config value, and two sources for one
        // number is how an invoice ends up disagreeing with the checkout that
        // produced it. CompanyProfile falls back to the config value until a
        // rate has been saved.
        $domestic = CompanyProfile::taxRate() / 100;

        // Unverified is the normal state, and it must cost the customer nothing
        // more than the domestic rate they would pay anyway — but it must not
        // zero the VAT either. Otherwise typing "XX123" is a discount.
        if ($customer === null || ! $customer->hasVerifiedVatId()) {
            return new self($domestic, false);
        }

        $vatId = $customer->normalisedVatId();
        $country = substr($vatId, 0, 2);
        $seller = strtoupper((string) config('provisioning.tax.seller_country', 'AT'));

        // Reverse charge is an intra-EU business rule: it needs a member state
        // that is not ours, and a number that at least looks like one.
        $eligible = in_array($country, self::EU_MEMBER_STATES, true)
            && $country !== $seller
            && preg_match('/^[A-Z]{2}[0-9A-Z]{8,12}$/', $vatId) === 1;

        return $eligible ? new self(0.0, true) : new self($domestic, false);
    }

    public function grossCents(int $netCents): int
    {
        return (int) round($netCents * (1 + $this->rate));
    }

    public function percentLabel(): string
    {
        return rtrim(rtrim(number_format($this->rate * 100, 1, ',', '.'), '0'), ',');
    }
}
