<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

/**
 * Booked modules, each at the price it was booked at.
 *
 * The owner's rule, and it is easy to miss: a customer's total is their
 * subscription plus their modules, and ALL of it is frozen. Price protection
 * that covered only the plan would let a module quietly double for someone who
 * booked it two years ago.
 *
 * A module the customer has NOT booked is a different question entirely — that
 * is a sale we have not made yet, and it is shown and sold at today's price.
 * So the catalogue is consulted only for what is not in this table.
 *
 * This table is live booking state, not the archive: it says what a contract is
 * paying for now. The evidence lives in `subscription_records`, which keeps each
 * booking and cancellation as its own event and survives erasure of the customer
 * it belonged to. So these rows go with their subscription — an orphaned booking
 * pointing at no contract would answer nothing that the register does not answer
 * better.
 */
return new class extends Migration
{
    public function up(): void
    {
        Schema::create('subscription_addons', function (Blueprint $table) {
            $table->id();
            $table->uuid()->unique();
            $table->foreignId('subscription_id')->constrained()->cascadeOnDelete();
            $table->foreignId('order_id')->nullable()->constrained()->nullOnDelete();

            // Which module, by the key the catalogue and the lang files use.
            $table->string('addon_key');

            // Frozen at booking. Net, per month, per unit.
            $table->unsignedInteger('price_cents');
            $table->char('currency', 3);

            // Storage is sold in packs, so a customer can hold several of one
            // module. One row per booking keeps each pack's own price.
            $table->unsignedSmallInteger('quantity')->default(1);

            $table->timestamp('booked_at');
            // Cancelling sets this rather than removing the row: what a
            // customer was paying, and until when, is part of the answer.
            $table->timestamp('cancelled_at')->nullable();

            $table->timestamps();

            $table->index(['subscription_id', 'addon_key']);
            $table->index(['subscription_id', 'cancelled_at']);

            // One order books one module, once. Two retries arriving together
            // both see no row and both insert otherwise — and the customer's
            // recurring bill quietly doubles. A booking made without an order
            // (an operator granting a module) is not constrained: NULLs do not
            // collide, which is the behaviour wanted here.
            $table->unique(['order_id', 'addon_key']);
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('subscription_addons');
    }
};
