<?php

use App\Actions\BookAddon;
use App\Actions\GrantAddon;
use App\Actions\OpenSubscription;
use App\Livewire\Billing;
use App\Models\Customer;
use App\Models\Instance;
use App\Models\Operator;
use App\Models\Order;
use App\Models\User;
use App\Services\Billing\AddonCatalogue;
use App\Services\Billing\PlanCatalogue;

/**
 * Was ein Block kostet, und wer ihn nicht kaufen kann.
 *
 * Die Preisregel: ein Block muss teurer je GB sein als der Aufstieg in die
 * nächste Stufe. 20 GB für 15 € sind 0,75 €/GB gegen 0,73 (Start → Team) und
 * 0,67 (Team → Business). Bei 12 € wäre er mit 0,60 billiger als jeder
 * Aufstieg gewesen, und Stapeln hätte sich gelohnt.
 */
it('verkauft 20 GB für 15 Euro netto', function () {
    $catalogue = app(AddonCatalogue::class);

    expect($catalogue->packGb())->toBe(20)
        ->and($catalogue->packDiskGb())->toBe(22)
        ->and($catalogue->priceCents(AddonCatalogue::STORAGE))->toBe(1500)
        ->and($catalogue->maxQuantity(AddonCatalogue::STORAGE))->toBe(3);
});

it('bleibt teurer je Gigabyte als der Aufstieg', function () {
    // Gerechnet wird mit dem, was Katalog und Konfiguration WIRKLICH sagen —
    // mit festen Zahlen prüfte dieser Test nur, ob PHP dividieren kann. So
    // schlägt er an, wenn ein Preis später einmal so gesetzt wird, dass
    // Stapeln sich wieder lohnt.
    $catalogue = app(AddonCatalogue::class);
    $plans = app(PlanCatalogue::class)->sellable();

    $blockPerGb = $catalogue->priceCents(AddonCatalogue::STORAGE) / $catalogue->packGb();

    foreach ([['start', 'team'], ['team', 'business']] as [$von, $nach]) {
        $mehrGb = $plans[$nach]['quota_gb'] - $plans[$von]['quota_gb'];
        $mehrCent = $plans[$nach]['price_cents'] - $plans[$von]['price_cents'];

        expect($blockPerGb)->toBeGreaterThan($mehrCent / $mehrGb);
    }
});

it('bietet Enterprise keinen Zusatzspeicher an', function () {
    // Enterprise wird seit der neuen Leiter angefragt statt gekauft; der
    // Vertrag hier ist einer aus der Zeit davor, und die laufen weiter.
    $order = Order::factory()->create(['plan' => 'enterprise', 'datacenter' => 'fsn', 'status' => 'paid']);
    $subscription = asGrandfathered('enterprise', fn () => app(OpenSubscription::class)($order));

    expect(fn () => app(BookAddon::class)($subscription, AddonCatalogue::STORAGE, 1))
        ->toThrow(RuntimeException::class);
});

/**
 * Nachtrag: derselbe Ausschluss, wenn der Betreiber ein Modul verschenkt.
 *
 * GrantAddon bucht durch BookAddon (siehe dessen Kopfkommentar), also reicht
 * die Prüfung an einer Stelle — dieser Test belegt nur, dass der Weg
 * tatsächlich derselbe ist und nicht heimlich daran vorbeiführt.
 */
it('lässt auch den Betreiber keinen Zusatzspeicher an Enterprise verschenken', function () {
    $order = Order::factory()->create(['plan' => 'enterprise', 'datacenter' => 'fsn', 'status' => 'paid']);
    $subscription = asGrandfathered('enterprise', fn () => app(OpenSubscription::class)($order));
    $operator = Operator::factory()->create();

    expect(fn () => app(GrantAddon::class)($subscription, $operator, AddonCatalogue::STORAGE, 0, 1))
        ->toThrow(RuntimeException::class);
});

/**
 * Nachtrag: dieselbe Sperre, bevor Geld fließt, nicht erst danach.
 *
 * `storageLimitNote` ist das, was den Knopf im Portal verschwinden lässt (statt
 * ihn anzubieten und dann an der Zahlung zu scheitern) — derselbe Grund, aus
 * dem die kaufmännische Grenze dort schon geprüft wurde. Ohne diese Prüfung
 * würde ein Enterprise-Kunde einen zahlbaren Auftrag in den Warenkorb legen
 * können, den BookAddon nach der Zahlung nur noch ablehnen könnte, während der
 * Auftrag selbst „paid" stehen bliebe.
 */
it('bietet Enterprise auch im Warenkorb keinen Zusatzspeicher an', function () {
    $customer = Customer::factory()->create();
    $user = User::factory()->create(['email' => $customer->email]);
    $order = asGrandfathered('enterprise', fn () => Order::factory()->withSubscription()->for($customer)->create(['plan' => 'enterprise']));
    Instance::factory()->for($customer)->create([
        'order_id' => $order->id, 'plan' => 'enterprise', 'status' => 'active',
    ]);

    withStripeSecret();

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

    expect(Order::query()->where('customer_id', $customer->id)->where('type', 'storage')->count())->toBe(0);
});
