<?php

use App\Livewire\Billing;
use App\Livewire\ConfirmRemoveOrder;
use App\Models\Customer;
use App\Models\Instance;
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Carbon;
use Illuminate\Support\Number;

function cartCustomer(): array
{
    $customer = Customer::factory()->create();
    $user = User::factory()->create(['email' => $customer->email]);

    // Some of these tests call Billing::purchase(), which since Task 5
    // refuses outright without a Stripe key for the active mode.
    withStripeSecret();

    return [$customer, $user];
}

it('says what was ordered instead of counting it', function () {
    [$customer, $user] = cartCustomer();

    Order::factory()->for($customer)->create(['type' => 'traffic', 'plan' => 'team', 'amount_cents' => 500, 'status' => 'pending']);
    Order::factory()->for($customer)->create(['type' => 'upgrade', 'plan' => 'business', 'amount_cents' => 39900, 'status' => 'pending']);

    Livewire\Livewire::actingAs($user)->test(Billing::class)
        ->assertSee(__('billing.cart.title'))
        ->assertSee(__('billing.cart.traffic', ['gb' => 1000]))
        ->assertSee(__('billing.cart.upgrade', ['plan' => 'Business']))
        // …and what it all comes to.
        ->assertSee('404,00');
});

it('leaves paid orders out of the cart', function () {
    [$customer, $user] = cartCustomer();
    Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 1000, 'status' => 'paid']);

    Livewire\Livewire::actingAs($user)->test(Billing::class)->assertDontSee(__('billing.cart.title'));
});

it('removes an entry the customer changed their mind about', function () {
    [$customer, $user] = cartCustomer();
    $order = Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 1000, 'status' => 'pending']);

    Livewire\Livewire::actingAs($user)
        ->test(ConfirmRemoveOrder::class, ['uuid' => $order->uuid])
        ->assertSee(__('billing.cart.storage', ['gb' => 20]))
        ->call('remove');

    expect(Order::query()->count())->toBe(0);
});

it('will not let one customer remove another customer\'s order', function () {
    [, $user] = cartCustomer();
    $stranger = Customer::factory()->create();
    $order = Order::factory()->for($stranger)->create(['status' => 'pending']);

    // Modals are reachable without the page's guards, so this is the real check.
    Livewire\Livewire::actingAs($user)
        ->test(ConfirmRemoveOrder::class, ['uuid' => $order->uuid])
        ->assertNotFound();

    expect(Order::query()->whereKey($order->id)->exists())->toBeTrue();
});

it('refuses to remove an order that is already paid', function () {
    [$customer, $user] = cartCustomer();
    $order = Order::factory()->for($customer)->create(['status' => 'paid']);

    Livewire\Livewire::actingAs($user)
        ->test(ConfirmRemoveOrder::class, ['uuid' => $order->uuid])
        ->assertNotFound();

    expect(Order::query()->whereKey($order->id)->exists())->toBeTrue();
});

it('keeps at most one plan change in the cart', function () {
    [$customer, $user] = cartCustomer();
    Instance::factory()->for($customer)->create(['plan' => 'start', 'status' => 'active']);

    $component = Livewire\Livewire::actingAs($user)->test(Billing::class);
    $component->call('purchase', 'upgrade', 'team');
    $component->call('purchase', 'upgrade', 'business');

    // Two contradicting plan changes cannot both be paid for — the second
    // replaces the first instead of stacking. Business statt Enterprise als
    // zweiter Schritt: Enterprise wird seit der neuen Leiter angefragt und
    // nicht mehr verkauft, ein Aufstieg dorthin ist also gar kein Kauf mehr.
    $upgrades = Order::query()->where('type', 'upgrade')->where('status', 'pending')->get();
    expect($upgrades)->toHaveCount(1)
        ->and($upgrades->first()->plan)->toBe('business');
});

it('still lets add-ons stack', function () {
    [$customer, $user] = cartCustomer();
    Instance::factory()->for($customer)->create(['plan' => 'team', 'status' => 'active']);

    $component = Livewire\Livewire::actingAs($user)->test(Billing::class);
    $component->call('purchase', 'storage');
    $component->call('purchase', 'storage');
    $component->call('purchase', 'traffic');

    // Buying 200 GB as two packs is a perfectly sensible thing to want.
    expect(Order::query()->where('type', 'storage')->count())->toBe(2)
        ->and(Order::query()->where('type', 'traffic')->count())->toBe(1);
});

it('says net, gross and how often for every price', function () {
    [$customer, $user] = cartCustomer();
    config()->set('provisioning.tax.rate_percent', 20);

    Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 1000, 'status' => 'pending']);
    Order::factory()->for($customer)->create(['type' => 'traffic', 'amount_cents' => 500, 'status' => 'pending']);

    Livewire\Livewire::actingAs($user)->test(Billing::class)
        ->assertSee(__('billing.cart.net'))
        ->assertSee(__('billing.cart.per_month'))     // storage recurs
        ->assertSee(__('billing.cart.once'))          // traffic does not
        ->assertSee('12,00')                          // 15,00 net → 18,00 gross
        ->assertSee('18,00')
        ->assertSee(__('billing.cart.total_gross'));
});

it('separates what recurs from a one-off top-up', function () {
    [$customer, $user] = cartCustomer();
    config()->set('provisioning.tax.rate_percent', 20);

    Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 1000, 'status' => 'pending']);
    Order::factory()->for($customer)->create(['type' => 'traffic', 'amount_cents' => 500, 'status' => 'pending']);

    // 10,00 net recurring → 12,00 gross per month, not the 18,00 total.
    // Formatted through the same helper the view uses, or the non-breaking
    // space in the currency makes this a test about whitespace.
    $expected = __('billing.cart.recurring_note', [
        'amount' => Number::currency(12.00, in: 'EUR', locale: 'de'),
    ]);

    Livewire\Livewire::actingAs($user)->test(Billing::class)->assertSee($expected);
});

it('keeps the recurring figure consistent with the total to the cent', function () {
    [$customer, $user] = cartCustomer();
    config()->set('provisioning.tax.rate_percent', 20);

    // Cent-level prices: rounding after aggregating instead of per order makes
    // the two figures disagree, and a customer who spots that stops trusting
    // every other number on the page.
    Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 3, 'status' => 'pending']);
    Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 3, 'status' => 'pending']);

    $expected = Number::currency(0.08, in: 'EUR', locale: 'de');

    Livewire\Livewire::actingAs($user)->test(Billing::class)
        ->assertSee($expected)                                                     // total gross
        ->assertSee(__('billing.cart.recurring_note', ['amount' => $expected]));   // and the note
});

it('does not charge Austrian VAT to an EU business with a VAT ID', function () {
    config()->set('provisioning.tax.rate_percent', 20);
    config()->set('provisioning.tax.seller_country', 'AT');

    [$customer, $user] = cartCustomer();
    $customer->update(['vat_id' => 'DE 811 907 980', 'vat_id_verified_at' => now(), 'vat_id_verified_value' => 'DE811907980']);
    Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 1000, 'status' => 'pending']);

    // Reverse charge: we invoice net, the customer accounts for the VAT.
    Livewire\Livewire::actingAs($user)->test(Billing::class)
        ->assertSee(__('billing.cart.reverse_charge'))
        ->assertDontSee(__('billing.cart.vat', ['percent' => '20']));

    expect(Order::query()->first()->grossCents())->toBe(1000);
});

it('charges domestic VAT to a domestic business and to private customers', function () {
    config()->set('provisioning.tax.rate_percent', 20);
    config()->set('provisioning.tax.seller_country', 'AT');

    [$customer, $user] = cartCustomer();
    Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 1000, 'status' => 'pending']);

    // No VAT ID at all → domestic rate.
    expect(Order::query()->first()->grossCents())->toBe(1200);

    // An Austrian VAT ID is a domestic business sale, taxed normally.
    $customer->update(['vat_id' => 'ATU12345678', 'vat_id_verified_at' => now(), 'vat_id_verified_value' => 'ATU12345678']);
    expect(Order::query()->first()->fresh()->grossCents())->toBe(1200);

    Livewire\Livewire::actingAs($user)->test(Billing::class)
        ->assertSee(__('billing.cart.vat', ['percent' => '20']));
});

it('accepts a verified value stored in display form', function () {
    config()->set('provisioning.tax.rate_percent', 20);
    config()->set('provisioning.tax.seller_country', 'AT');

    [$customer] = cartCustomer();
    Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 1000, 'status' => 'pending']);

    // A verifier may hand back the number the way it prints it; spaces are
    // cosmetic and must not invalidate a genuine confirmation.
    $customer->update([
        'vat_id' => 'DE811907980',
        'vat_id_verified_at' => now(),
        'vat_id_verified_value' => 'DE 811 907 980',
    ]);

    expect($customer->fresh()->hasVerifiedVatId())->toBeTrue()
        ->and($customer->fresh()->vat_id_verified_at)->toBeInstanceOf(Carbon::class)
        ->and(Order::query()->first()->fresh()->grossCents())->toBe(1000);
});

it('will not let a typed-in VAT ID zero the tax', function () {
    config()->set('provisioning.tax.rate_percent', 20);
    config()->set('provisioning.tax.seller_country', 'AT');

    [$customer] = cartCustomer();
    Order::factory()->for($customer)->create(['type' => 'storage', 'amount_cents' => 1000, 'status' => 'pending']);

    // Plausible-looking but unverified: still domestic VAT.
    $customer->update(['vat_id' => 'DE811907980']);
    expect(Order::query()->first()->fresh()->grossCents())->toBe(1200);

    // Nonsense, even if someone marked it verified: not an EU member state.
    $customer->update(['vat_id' => 'XX123', 'vat_id_verified_at' => now(), 'vat_id_verified_value' => 'XX123']);
    expect(Order::query()->first()->fresh()->grossCents())->toBe(1200);

    // Verified and from a member state that is not ours: reverse charge.
    $customer->update(['vat_id' => 'DE811907980', 'vat_id_verified_at' => now(), 'vat_id_verified_value' => 'DE811907980']);
    expect(Order::query()->first()->fresh()->grossCents())->toBe(1000);

    // Swapping the number keeps the old confirmation in the row — it must not
    // vouch for a value nobody checked.
    $customer->update(['vat_id' => 'FR12345678901']);
    expect(Order::query()->first()->fresh()->grossCents())->toBe(1200);
});
