<?php

use App\Models\Customer;
use App\Models\Invoice;
use App\Models\InvoiceSeries;
use App\Models\Order;
use App\Services\Billing\InvoiceRenderer;
use App\Services\Billing\IssueInvoice;
use App\Support\CompanyProfile;
use App\Support\Settings;

/**
 * Issuing a document that cannot be changed afterwards.
 */
beforeEach(function () {
    CompanyProfile::put([
        'name' => 'CluPilot Cloud e.U.',
        'address' => 'Dreherstraße 66/1/8',
        'postcode' => '1110',
        'city' => 'Wien',
        'vat_id' => 'ATU00000000',
    ]);
});

function paidOrders(Customer $customer, int ...$amounts)
{
    return collect($amounts)->map(fn (int $cents) => Order::factory()->create([
        'customer_id' => $customer->id,
        'amount_cents' => $cents,
        'currency' => 'EUR',
        'status' => 'paid',
    ]));
}

it('issues one invoice for one purchase, with every item on its own line', function () {
    // A customer who buys a plan and two add-ons in one go has bought once.
    // Three invoices for one purchase is three times the paperwork for the
    // same money — and the add-ons were asked for as separate lines, not
    // separate documents.
    $customer = Customer::factory()->create();
    $invoice = app(IssueInvoice::class)->forOrders($customer, paidOrders($customer, 1900, 500, 900));

    expect($invoice->snapshot['lines'])->toHaveCount(3)
        ->and($invoice->net_cents)->toBe(3300)
        ->and($invoice->tax_cents)->toBe(660)
        ->and($invoice->gross_cents)->toBe(3960)
        ->and($invoice->number)->toStartWith('RE-');
});

it('refuses to issue before the company details are complete', function () {
    // An invoice without a registered name, an address or a VAT number is not
    // a valid invoice here — and issuing one consumes a number that can never
    // be handed out again.
    Settings::forget('company.vat_id');

    $customer = Customer::factory()->create();

    expect(fn () => app(IssueInvoice::class)->forOrders($customer, paidOrders($customer, 1900)))
        ->toThrow(RuntimeException::class);

    expect(Invoice::query()->count())->toBe(0)
        // And the number was not taken either.
        ->and(InvoiceSeries::query()->where('kind', 'invoice')->value('next_number'))->toBe(1);
});

it('refuses to issue a document with nothing on it', function () {
    expect(fn () => app(IssueInvoice::class)->forOrders(Customer::factory()->create(), collect()))
        ->toThrow(RuntimeException::class);
});

it('freezes what it says, so later settings cannot rewrite it', function () {
    // The whole reason no PDF is stored. Change the address in 2028 and an
    // invoice from 2026 still shows the address it was issued under.
    $customer = Customer::factory()->create(['name' => 'Muster GmbH']);
    $invoice = app(IssueInvoice::class)->forOrders($customer, paidOrders($customer, 1900));

    CompanyProfile::put(['name' => 'CluPilot Cloud GmbH', 'address' => 'Anderswo 9']);
    $customer->update(['name' => 'Muster GmbH & Co KG']);

    $frozen = $invoice->refresh()->snapshot;

    expect($frozen['issuer']['name'])->toBe('CluPilot Cloud e.U.')
        ->and($frozen['issuer']['address'])->toBe('Dreherstraße 66/1/8')
        ->and($frozen['customer']['name'])->toBe('Muster GmbH');

    // And the rendered document agrees with the snapshot, not with today.
    expect(app(InvoiceRenderer::class)->forInvoice($invoice))->toBeString()->not->toBeEmpty();
});

it('takes the number and writes the invoice together, or neither', function () {
    // A number taken and then lost to a failure is a gap in a series that must
    // not have one.
    $customer = Customer::factory()->create();

    app(IssueInvoice::class)->forOrders($customer, paidOrders($customer, 1900));
    app(IssueInvoice::class)->forOrders($customer, paidOrders($customer, 2900));

    expect(Invoice::query()->pluck('number')->all())->toBe(['RE-2026-0001', 'RE-2026-0002'])
        ->and(InvoiceSeries::query()->where('kind', 'invoice')->value('next_number'))->toBe(3);
})->skip(fn () => now()->format('Y') !== '2026', 'The expected numbers carry the current year.');

it('carries the customer’s billing address as it was written', function () {
    // One free-text field today. Guessing which line is the postcode would put
    // it where the street belongs, on a document nobody can correct afterwards.
    $customer = Customer::factory()->create([
        'billing_address' => "Beispielgasse 12/3\n1030 Wien\nÖsterreich",
    ]);

    $invoice = app(IssueInvoice::class)->forOrders($customer, paidOrders($customer, 1900));

    expect($invoice->snapshot['customer']['address_lines'])
        ->toBe(['Beispielgasse 12/3', '1030 Wien', 'Österreich']);
});

it('prints the reverse-charge note exactly when no VAT is charged', function () {
    // A zero-rated invoice without the note says nothing about why, which is
    // the one thing an auditor looks for.
    $customer = Customer::factory()->create([
        'vat_id' => 'DE123456789',
        'vat_id_verified_at' => now(),
        'vat_id_verified_value' => 'DE123456789',
    ]);

    $invoice = app(IssueInvoice::class)->forOrders($customer, paidOrders($customer, 1900));

    expect($invoice->tax_cents)->toBe(0)
        ->and($invoice->snapshot['meta']['closing'])->toBe(__('invoice.reverse_charge'));
});
