<?php

use App\Actions\GrantAddon;
use App\Actions\GrantSubscription;
use App\Actions\OpenSubscription;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\InvoiceSeries;
use App\Models\Operator;
use App\Models\Order;
use App\Models\Subscription;
use App\Services\Billing\IssueInvoice;
use App\Support\CompanyProfile;
use Illuminate\Support\Facades\Queue;

/**
 * The other half of the owner's second decision: a full gift produces no
 * invoice at all, but a discounted grant is invoiced normally, at what the
 * customer actually pays. This is the one place IssueInvoice had to change —
 * a single reject() of free-grant orders — and this file is what proves it.
 */
beforeEach(function () {
    CompanyProfile::put([
        'name' => 'CluPilot Cloud e.U.',
        'address' => 'Dreherstraße 66/1/8',
        'postcode' => '1110',
        'city' => 'Wien',
        'vat_id' => 'ATU00000000',
    ]);
});

it('skips a fully granted subscription: no invoice, no number consumed', function () {
    Queue::fake();

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

    app(GrantSubscription::class)(
        customer: $customer, grantedBy: $owner, plan: 'team',
        term: Subscription::TERM_MONTHLY, datacenter: 'fsn', priceCents: 0,
    );

    $order = Order::query()->where('customer_id', $customer->id)->sole();

    expect(fn () => app(IssueInvoice::class)->forOrders($customer, collect([$order])))
        ->toThrow(RuntimeException::class);

    expect(Invoice::query()->count())->toBe(0)
        ->and(InvoiceSeries::query()->where('kind', 'invoice')->value('next_number'))->toBe(1);
});

it('invoices a discounted grant normally, at what the customer actually pays', function () {
    Queue::fake();

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

    app(GrantSubscription::class)(
        customer: $customer, grantedBy: $owner, plan: 'team',
        term: Subscription::TERM_MONTHLY, datacenter: 'fsn', priceCents: 5000,
    );

    $order = Order::query()->where('customer_id', $customer->id)->sole();
    $invoice = app(IssueInvoice::class)->forOrders($customer, collect([$order]));

    expect($invoice->net_cents)->toBe(5000)
        ->and($invoice->number)->toStartWith('RE-');
});

it('skips a granted, free module the same way', function () {
    $order = Order::factory()->create(['plan' => 'team', 'datacenter' => 'fsn', 'status' => 'paid']);
    $subscription = app(OpenSubscription::class)($order);
    $owner = Operator::factory()->create();
    $customer = $subscription->customer;

    app(GrantAddon::class)($subscription, $owner, 'priority_support', 0);

    $addonOrder = Order::query()->where('type', 'addon')->sole();

    expect(fn () => app(IssueInvoice::class)->forOrders($customer, collect([$addonOrder])))
        ->toThrow(RuntimeException::class);

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

it('does not mistake an ordinary fully-discounted Stripe checkout for a grant', function () {
    // The exact case OpenSubscription's own docblock warns about: a real
    // Stripe checkout can also land at amount_cents = 0 (a 100% coupon). That
    // subscription was never granted (granted_at stays null) and is still an
    // owed invoice — isFreeGrant() must key off provenance, not off the
    // amount alone.
    $order = Order::factory()->create([
        'plan' => 'team', 'datacenter' => 'fsn', 'amount_cents' => 0,
        'currency' => 'EUR', 'status' => 'paid', 'stripe_event_id' => 'evt_full_coupon',
    ]);
    $subscription = app(OpenSubscription::class)($order, Subscription::TERM_MONTHLY, ['price_cents' => 0]);

    expect($subscription->isGranted())->toBeFalse();

    $invoice = app(IssueInvoice::class)->forOrders($order->customer, collect([$order]));

    expect($invoice->net_cents)->toBe(0)
        ->and($invoice->number)->toStartWith('RE-');
});
