<?php

use App\Livewire\Admin\EditInvoiceSeries;
use App\Livewire\Admin\Finance;
use App\Models\Invoice;
use App\Models\InvoiceSeries;
use App\Services\Billing\TaxTreatment;
use App\Support\CompanyProfile;
use App\Support\Settings;
use Livewire\Livewire;

/**
 * The details that end up on a legal document, and the parts of an invoice
 * series that stop being editable once one has been issued.
 */
it('refuses to consider itself ready without the fields an invoice legally needs', function () {
    // An invoice without a name, an address or a VAT number is not a valid
    // invoice in Austria. Issuing one is worse than refusing to.
    expect(CompanyProfile::missingForInvoicing())
        ->toContain('name')->toContain('address')->toContain('vat_id');

    CompanyProfile::put([
        'name' => 'CluPilot Cloud e.U.',
        'address' => 'Musterstraße 1',
        'postcode' => '1010',
        'city' => 'Wien',
        'vat_id' => 'ATU12345678',
    ]);

    expect(CompanyProfile::missingForInvoicing())->toBe([]);
});

it('stores the company details from the finance tab', function () {
    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(Finance::class)
        ->set('company.name', 'CluPilot Cloud e.U.')
        ->set('company.address', 'Musterstraße 1')
        ->set('company.postcode', '1010')
        ->set('company.city', 'Wien')
        ->set('company.country', 'Österreich')
        ->set('company.vat_id', 'ATU12345678')
        ->set('company.payment_days', 14)
        ->set('taxRate', 20)
        ->call('saveCompany')
        ->assertHasNoErrors();

    expect(CompanyProfile::get('vat_id'))->toBe('ATU12345678')
        ->and(CompanyProfile::taxRate())->toBe(20.0);
});

it('drops a field that is not part of the profile', function () {
    // This is fed from a form. A forged key must not be able to write an
    // arbitrary setting into the same table the site-visibility switch uses.
    CompanyProfile::put(['site.public' => false, 'name' => 'Echt']);

    expect(Settings::get('site.public', true))->toBeTrue()
        ->and(CompanyProfile::get('name'))->toBe('Echt');
});

it('lets a series be renamed and its prefix corrected while nothing has been issued', function () {
    $series = InvoiceSeries::query()->where('kind', 'invoice')->firstOrFail();

    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(EditInvoiceSeries::class, ['uuid' => $series->uuid])
        ->set('name', 'Ausgangsrechnung')
        ->set('prefix', 'ar')
        ->call('save')
        ->assertHasNoErrors();

    expect($series->refresh()->prefix)->toBe('AR')
        ->and($series->name)->toBe('Ausgangsrechnung');
});

it('freezes the prefix once a document carries it', function () {
    // The prefix is part of every number already issued. Changing it renames
    // nothing and leaves a series whose past says RE and whose future says AR.
    $series = InvoiceSeries::query()->where('kind', 'invoice')->firstOrFail();
    issueOne($series);

    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(EditInvoiceSeries::class, ['uuid' => $series->uuid])
        ->set('prefix', 'AR')
        ->set('name', 'Ausgangsrechnung')
        ->call('save')
        ->assertHasNoErrors();

    expect($series->refresh()->prefix)->toBe('RE')
        // The rest of the form still saves.
        ->and($series->name)->toBe('Ausgangsrechnung');
});

it('will not let the counter be wound back onto a number already issued', function () {
    // Gapless AND ascending. A lower number is one that is already printed on
    // a document somebody holds.
    $series = InvoiceSeries::query()->where('kind', 'invoice')->firstOrFail();
    $series->update(['next_number' => 25]);

    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(EditInvoiceSeries::class, ['uuid' => $series->uuid])
        ->set('nextNumber', 7)
        ->call('save')
        ->assertHasErrors(['nextNumber']);

    expect($series->refresh()->next_number)->toBe(25);
});

it('does let the counter be moved forward', function () {
    // Skipping numbers is lawful — reusing them is not. An operator migrating
    // from another system needs exactly this.
    $series = InvoiceSeries::query()->where('kind', 'invoice')->firstOrFail();

    Livewire::actingAs(operator('Owner'), 'operator')
        ->test(EditInvoiceSeries::class, ['uuid' => $series->uuid])
        ->set('nextNumber', 500)
        ->call('save')
        ->assertHasNoErrors();

    expect($series->refresh()->next_number)->toBe(500);
});

/** A minimal issued document, enough to make a series non-empty. */
function issueOne(InvoiceSeries $series): Invoice
{
    return Invoice::create([
        'invoice_series_id' => $series->id,
        'number' => $series->prefix.'-2026-0001',
        'number_year' => 2026,
        'number_sequence' => 1,
        'issued_on' => now()->toDateString(),
        'snapshot' => ['issuer' => [], 'lines' => []],
        'net_cents' => 1900,
        'tax_cents' => 380,
        'gross_cents' => 2280,
        'currency' => 'EUR',
    ]);
}

it('decides VAT in one place, so the invoice cannot disagree with the checkout', function () {
    // Two sources for one number is how a document ends up contradicting the
    // page that produced it. The Finance tab is the source; config is only the
    // fallback until a rate has been saved.
    config(['provisioning.tax.rate_percent' => 20]);

    expect(CompanyProfile::taxRate())->toBe(20.0)
        ->and(TaxTreatment::for(null)->rate)->toBe(0.2);

    Settings::set('company.tax_rate', 10.0);

    expect(CompanyProfile::taxRate())->toBe(10.0)
        ->and(TaxTreatment::for(null)->rate)->toBe(0.1);
});
