<?php

declare(strict_types=1);

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

return new class extends Migration
{
    public function up(): void
    {
        /*
         * Configurable fee rules. Section 26 of the spec forbids hardcoded
         * marketplace percentages, so every charge the platform makes resolves
         * through this table.
         *
         * Scoping columns are nullable and a NULL means "matches anything".
         * Rules are never edited in place once used -- they are superseded via
         * effective_from/effective_until so that a historical order can always
         * be reconciled against the rule that actually applied to it.
         *
         * Rules carry a ROLE, and this is the important part of the design.
         *
         * A `base` rule establishes the rate. Exactly one applies: the most
         * specific match wins, with `priority` breaking ties.
         *
         * An `adjustment` rule modifies whatever the base produced, and EVERY
         * matching adjustment applies. Adjustments exist because scope
         * dimensions have to compose rather than outrank each other. Volume
         * bands and a per-TLD tweak are independent intentions; under a
         * single-winner model the TLD rule silently cancelled the bands, which
         * meant an administrator could overcharge a seller by thousands while
         * believing they had granted a discount. Making the administrator state
         * whether a rule *replaces* or *adjusts* the rate removes that trap.
         */
        Schema::create('fees', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('code', 64)->unique();
            $table->text('description')->nullable();

            $table->enum('type', [
                'seller_commission',
                'buyer_fee',
                'transfer_fee',
                'listing_fee',
                'featured_fee',
                'payout_fee',
                'tax',
            ]);

            $table->enum('calculation', ['percentage', 'fixed', 'percentage_plus_fixed']);

            /*
             * `base` sets the rate; `adjustment` shifts it. See the note above.
             */
            $table->enum('role', ['base', 'adjustment'])->default('base');

            /*
             * Basis points: 250 = 2.50%. Null for purely fixed fees.
             *
             * Signed, so an adjustment can subtract: -100 means "one percentage
             * point off whatever the base rate was". A base rule is validated
             * non-negative by the application -- the schema cannot express that
             * distinction, and the role column is what makes the intent legible.
             */
            $table->smallInteger('rate_bps')->nullable();

            /*
             * Signed. Every monetary column in this application is a signed
             * BIGINT of minor units, because a fee has a mirror image: a credit,
             * a discount, a refund reversal. Storing amounts unsigned makes the
             * reversal unrepresentable and forces callers to encode direction
             * somewhere else, which is how ledgers stop reconciling.
             */
            $table->bigInteger('fixed_amount_minor')->nullable();
            $table->char('currency', 3)->default('USD');

            // Floor and cap applied after calculation. Signed for the same reason.
            $table->bigInteger('min_fee_minor')->nullable();
            $table->bigInteger('max_fee_minor')->nullable();

            // --- Scoping (NULL = wildcard) -------------------------------
            $table->foreignId('tld_id')->nullable()->constrained('tlds')->cascadeOnDelete();
            $table->foreignId('plan_id')->nullable()->constrained('plans')->cascadeOnDelete();
            $table->enum('listing_type', ['fixed_price', 'make_offer', 'auction'])->nullable();
            $table->enum('transaction_type', ['marketplace_sale', 'registration', 'renewal', 'transfer'])->nullable();
            $table->enum('seller_tier', ['standard', 'verified', 'pro', 'enterprise'])->nullable();

            /*
             * Price band the rule applies to. Unsigned on purpose: this bounds a
             * listing price, and a listing price cannot be negative.
             */
            $table->unsignedBigInteger('price_min_minor')->nullable();
            $table->unsignedBigInteger('price_max_minor')->nullable();

            $table->boolean('is_active')->default(true);
            $table->smallInteger('priority')->default(0);
            $table->timestamp('effective_from')->nullable();
            $table->timestamp('effective_until')->nullable();

            $table->json('metadata')->nullable();

            $table->timestamps();
            $table->softDeletes();

            // Resolution reads by type and role, then orders by priority.
            $table->index(['type', 'role', 'is_active', 'priority'], 'fees_resolution_index');
            $table->index(['effective_from', 'effective_until']);
        });
    }

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