<?php

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

/**
 * Agent commission engine (§13, §13A) and settlement.
 *
 * Rules are data, not code: a scheme is defined here and evaluated when a
 * verified system event happens. Amounts are minor units with a currency.
 */
return new class extends Migration
{
    public function up(): void
    {
        Schema::create('commission_rules', function (Blueprint $table) {
            $table->id();
            $table->uuid('uuid')->unique();
            $table->string('code', 40);
            $table->string('name');

            $table->foreignId('company_id')->constrained()->cascadeOnUpdate()->restrictOnDelete();
            // A scheme may be limited to one branch (§13A requirement 8).
            $table->foreignId('branch_id')->nullable()->constrained()->nullOnDelete();

            $table->string('trigger_event', 40);
            // fixed | percent_of_collection | percent_of_invoice
            $table->string('calculation_basis', 40);
            $table->char('currency', 3);

            // A fixed amount in minor units, or a rate in basis points so the
            // percentage itself never becomes a float (2% = 200).
            $table->unsignedBigInteger('fixed_amount')->default(0);
            $table->unsignedInteger('rate_basis_points')->default(0);

            // Caps and floors (§13A requirements 5 and 6).
            $table->unsignedBigInteger('minimum_amount')->nullable();
            $table->unsignedBigInteger('maximum_amount')->nullable();

            // Narrowing conditions; null means "any".
            $table->char('destination_country', 2)->nullable();
            $table->foreignId('employer_id')->nullable()->constrained()->nullOnDelete();
            $table->foreignId('job_demand_id')->nullable()->constrained()->nullOnDelete();
            $table->foreignId('agent_id')->nullable()->constrained()->nullOnDelete();
            $table->string('agent_type', 20)->nullable();

            // Date-effective schemes (§13A requirement 7).
            $table->date('effective_from')->nullable();
            $table->date('effective_to')->nullable();

            // Lower runs first; the first match wins unless stacking is allowed.
            $table->unsignedInteger('priority')->default(100);
            $table->boolean('is_stackable')->default(false);
            $table->boolean('is_active')->default(true);

            $table->text('description')->nullable();
            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
            $table->timestamps();
            $table->softDeletes();

            $table->unique(['company_id', 'code']);
            $table->index(['company_id', 'trigger_event', 'is_active']);
        });

        Schema::create('commissions', function (Blueprint $table) {
            $table->id();
            $table->uuid('uuid')->unique();
            $table->string('commission_no', 40);

            $table->foreignId('company_id')->constrained()->cascadeOnUpdate()->restrictOnDelete();
            $table->foreignId('branch_id')->nullable()->constrained()->nullOnDelete();
            $table->foreignId('agent_id')->constrained()->cascadeOnUpdate()->restrictOnDelete();
            $table->foreignId('applicant_id')->nullable()->constrained()->nullOnDelete();
            $table->foreignId('job_demand_id')->nullable()->constrained()->nullOnDelete();
            $table->foreignId('employer_id')->nullable()->constrained()->nullOnDelete();
            $table->foreignId('commission_rule_id')->nullable()->constrained()->nullOnDelete();

            $table->string('trigger_event', 40);
            // What the event was: a payment, a visa record, an applicant …
            $table->string('trigger_reference_type')->nullable();
            $table->unsignedBigInteger('trigger_reference_id')->nullable();

            $table->string('calculation_basis', 40);
            $table->unsignedBigInteger('eligible_amount')->default(0);
            $table->unsignedInteger('rate_basis_points')->default(0);
            $table->unsignedBigInteger('commission_amount');
            $table->char('currency', 3);

            $table->string('status', 20)->default('accrued');

            $table->timestamp('accrued_at');
            $table->foreignId('approved_by')->nullable()->constrained('users')->nullOnDelete();
            $table->timestamp('approved_at')->nullable();
            $table->timestamp('payable_at')->nullable();

            $table->unsignedBigInteger('paid_amount')->default(0);
            $table->foreignId('agent_settlement_id')->nullable();

            // A clawback is a second linked record; the original is untouched.
            $table->foreignId('reverses_commission_id')->nullable()->constrained('commissions')->nullOnDelete();
            $table->text('reversal_reason')->nullable();
            $table->timestamp('reversed_at')->nullable();
            $table->foreignId('reversed_by')->nullable()->constrained('users')->nullOnDelete();

            // How the number was reached, kept for the agent statement (§13A.4).
            $table->json('calculation_breakdown')->nullable();
            $table->text('notes')->nullable();
            $table->timestamps();

            $table->unique(['company_id', 'commission_no']);
            // One commission per rule per event: the guard against paying twice
            // for the same thing (§13A requirement 2).
            $table->unique(
                ['commission_rule_id', 'trigger_reference_type', 'trigger_reference_id', 'agent_id'],
                'commissions_rule_event_unique',
            );
            $table->index(['agent_id', 'status']);
            $table->index(['company_id', 'status', 'accrued_at']);
        });

        Schema::create('agent_settlements', function (Blueprint $table) {
            $table->id();
            $table->uuid('uuid')->unique();
            $table->string('settlement_no', 40);

            $table->foreignId('company_id')->constrained()->cascadeOnUpdate()->restrictOnDelete();
            $table->foreignId('branch_id')->nullable()->constrained()->nullOnDelete();
            $table->foreignId('agent_id')->constrained()->cascadeOnUpdate()->restrictOnDelete();

            $table->string('status', 20)->default('draft');
            $table->char('currency', 3);

            $table->date('period_start');
            $table->date('period_end');

            $table->unsignedBigInteger('gross_commission')->default(0);
            $table->unsignedBigInteger('bonus_amount')->default(0);
            $table->unsignedBigInteger('reversal_amount')->default(0);
            $table->unsignedBigInteger('advance_deduction')->default(0);
            $table->unsignedBigInteger('withholding_tax')->default(0);
            $table->unsignedBigInteger('other_deduction')->default(0);
            $table->unsignedBigInteger('net_payable')->default(0);
            $table->text('deduction_note')->nullable();

            $table->string('payment_method', 30)->nullable();
            $table->string('payment_reference', 100)->nullable();
            $table->timestamp('paid_at')->nullable();
            $table->foreignId('paid_by')->nullable()->constrained('users')->nullOnDelete();

            $table->foreignId('approved_by')->nullable()->constrained('users')->nullOnDelete();
            $table->timestamp('approved_at')->nullable();
            $table->text('notes')->nullable();

            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
            $table->timestamps();

            $table->unique(['company_id', 'settlement_no']);
            $table->unique(['company_id', 'payment_reference']);
            $table->index(['agent_id', 'status']);
        });

        Schema::table('commissions', function (Blueprint $table) {
            $table->foreign('agent_settlement_id')->references('id')->on('agent_settlements')->nullOnDelete();
        });
    }

    public function down(): void
    {
        Schema::table('commissions', function (Blueprint $table) {
            $table->dropForeign(['agent_settlement_id']);
        });

        Schema::dropIfExists('agent_settlements');
        Schema::dropIfExists('commissions');
        Schema::dropIfExists('commission_rules');
    }
};
