<?php

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

return new class extends Migration
{
    public function up(): void
    {
        $this->createSuppliers();
        $this->createAccounting();
        $this->createShowroomSessionsAndDevices();
        $this->createPaymentTables();
        $this->createPointLedger();
        $this->createStockMovements();
        $this->createCashClosing();
        $this->extendExistingTables();
    }

    public function down(): void
    {
        $this->dropExistingTableColumns();

        Schema::dropIfExists('showroom_cash_closing_payments');
        Schema::dropIfExists('showroom_cash_closings');
        Schema::dropIfExists('stock_movements');
        Schema::dropIfExists('customer_point_ledgers');
        Schema::dropIfExists('manual_payment_verifications');
        Schema::dropIfExists('bangla_qr_settlement_items');
        Schema::dropIfExists('bangla_qr_settlements');
        Schema::dropIfExists('payment_callback_logs');
        Schema::dropIfExists('payment_transactions');
        Schema::dropIfExists('showroom_sessions');
        Schema::dropIfExists('pos_devices');
        Schema::dropIfExists('supplier_ledger_entries');
        Schema::dropIfExists('suppliers');
        Schema::dropIfExists('accounting_voucher_lines');
        Schema::dropIfExists('accounting_vouchers');
        Schema::dropIfExists('accounting_accounts');
    }

    private function createSuppliers(): void
    {
        if (! Schema::hasTable('suppliers')) {
            Schema::create('suppliers', function (Blueprint $table) {
                $table->id();
                $table->string('name')->index();
                $table->string('phone', 40)->nullable()->index();
                $table->string('email')->nullable();
                $table->text('address')->nullable();
                $table->string('status', 30)->default('active')->index();
                $table->timestamps();
            });
        }

        if (! Schema::hasTable('supplier_ledger_entries')) {
            Schema::create('supplier_ledger_entries', function (Blueprint $table) {
                $table->id();
                $table->foreignId('supplier_id')->nullable()->constrained('suppliers')->nullOnDelete();
                $table->string('source_type', 80)->index();
                $table->unsignedBigInteger('source_id')->nullable()->index();
                $table->string('source_reference', 120)->nullable()->index();
                $table->decimal('debit', 24, 8)->default(0);
                $table->decimal('credit', 24, 8)->default(0);
                $table->decimal('balance', 24, 8)->default(0);
                $table->unsignedBigInteger('voucher_id')->nullable()->index();
                $table->text('note')->nullable();
                $table->foreignId('created_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->timestamps();
            });
        }
    }

    private function createAccounting(): void
    {
        if (! Schema::hasTable('accounting_accounts')) {
            Schema::create('accounting_accounts', function (Blueprint $table) {
                $table->id();
                $table->string('code', 60)->unique();
                $table->string('name')->index();
                $table->string('type', 40)->index();
                $table->foreignId('parent_id')->nullable()->constrained('accounting_accounts')->nullOnDelete();
                $table->boolean('is_system')->default(false);
                $table->boolean('is_active')->default(true);
                $table->timestamps();
            });
        }

        if (! Schema::hasTable('accounting_vouchers')) {
            Schema::create('accounting_vouchers', function (Blueprint $table) {
                $table->id();
                $table->string('voucher_no', 80)->unique();
                $table->string('source_type', 80)->index();
                $table->unsignedBigInteger('source_id')->nullable()->index();
                $table->string('source_reference', 120)->nullable()->index();
                $table->string('sale_channel', 30)->nullable()->index();
                $table->string('status', 30)->default('posted')->index();
                $table->date('voucher_date')->index();
                $table->decimal('total_debit', 24, 8)->default(0);
                $table->decimal('total_credit', 24, 8)->default(0);
                $table->text('note')->nullable();
                $table->foreignId('created_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->foreignId('voided_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->timestamp('voided_at')->nullable();
                $table->timestamps();
            });
        }

        if (! Schema::hasTable('accounting_voucher_lines')) {
            Schema::create('accounting_voucher_lines', function (Blueprint $table) {
                $table->id();
                $table->foreignId('voucher_id')->constrained('accounting_vouchers')->cascadeOnDelete();
                $table->foreignId('account_id')->constrained('accounting_accounts')->restrictOnDelete();
                $table->decimal('debit', 24, 8)->default(0);
                $table->decimal('credit', 24, 8)->default(0);
                $table->string('party_type', 80)->nullable();
                $table->unsignedBigInteger('party_id')->nullable();
                $table->text('description')->nullable();
                $table->timestamps();

                $table->index(['party_type', 'party_id']);
            });
        }
    }

    private function createShowroomSessionsAndDevices(): void
    {
        if (! Schema::hasTable('pos_devices')) {
            Schema::create('pos_devices', function (Blueprint $table) {
                $table->id();
                $table->string('device_uid')->unique();
                $table->string('device_name')->nullable();
                $table->string('showroom_id')->nullable()->index();
                $table->string('status', 30)->default('active')->index();
                $table->foreignId('registered_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->timestamp('last_seen_at')->nullable();
                $table->timestamps();
            });
        }

        if (! Schema::hasTable('showroom_sessions')) {
            Schema::create('showroom_sessions', function (Blueprint $table) {
                $table->id();
                $table->foreignId('admin_id')->constrained('admins')->cascadeOnDelete();
                $table->foreignId('pos_device_id')->nullable()->constrained('pos_devices')->nullOnDelete();
                $table->string('token_hash', 128)->unique();
                $table->json('abilities')->nullable();
                $table->timestamp('expires_at')->nullable()->index();
                $table->timestamp('last_used_at')->nullable();
                $table->timestamp('revoked_at')->nullable()->index();
                $table->string('ip_address', 64)->nullable();
                $table->text('user_agent')->nullable();
                $table->timestamps();
            });
        }
    }

    private function createPaymentTables(): void
    {
        if (! Schema::hasTable('payment_transactions')) {
            Schema::create('payment_transactions', function (Blueprint $table) {
                $table->id();
                $table->uuid('transaction_id')->unique();
                $table->string('payment_method', 80)->index();
                $table->string('provider_name', 80)->nullable()->index();
                $table->string('merchant_id')->nullable();
                $table->string('terminal_id')->nullable();
                $table->string('store_id')->nullable();
                $table->string('qr_type', 20)->nullable();
                $table->string('qr_reference', 160)->nullable()->index();
                $table->text('qr_payload')->nullable();
                $table->string('provider_transaction_id', 160)->nullable()->unique();
                $table->string('source_type', 80)->index();
                $table->unsignedBigInteger('source_id')->nullable()->index();
                $table->string('source_reference', 120)->nullable()->index();
                $table->foreignId('order_id')->nullable()->constrained('orders')->nullOnDelete();
                $table->string('payment_request_id', 191)->nullable()->index();
                $table->decimal('amount', 24, 8);
                $table->string('currency', 10)->default('BDT');
                $table->string('status', 30)->default('pending')->index();
                $table->timestamp('paid_at')->nullable();
                $table->timestamp('expires_at')->nullable();
                $table->json('request_payload')->nullable();
                $table->json('callback_payload')->nullable();
                $table->json('verification_payload')->nullable();
                $table->boolean('verified_by_system')->default(false);
                $table->foreignId('created_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->timestamps();

                $table->unique(['payment_method', 'source_type', 'source_id', 'amount'], 'payment_tx_source_unique');
            });
        }

        if (! Schema::hasTable('payment_callback_logs')) {
            Schema::create('payment_callback_logs', function (Blueprint $table) {
                $table->id();
                $table->foreignId('payment_transaction_id')->nullable()->constrained('payment_transactions')->nullOnDelete();
                $table->string('provider_name', 80)->nullable()->index();
                $table->string('event_type', 80)->nullable()->index();
                $table->string('status', 30)->default('received')->index();
                $table->string('signature_status', 30)->default('unchecked');
                $table->string('source_reference', 120)->nullable()->index();
                $table->json('headers')->nullable();
                $table->json('payload')->nullable();
                $table->text('error_message')->nullable();
                $table->timestamp('processed_at')->nullable();
                $table->timestamps();
            });
        }

        if (! Schema::hasTable('bangla_qr_settlements')) {
            Schema::create('bangla_qr_settlements', function (Blueprint $table) {
                $table->id();
                $table->string('settlement_no', 80)->unique();
                $table->string('provider_name', 80)->index();
                $table->date('settlement_date')->index();
                $table->decimal('gross_amount', 24, 8)->default(0);
                $table->decimal('charge_amount', 24, 8)->default(0);
                $table->decimal('net_amount', 24, 8)->default(0);
                $table->string('bank_reference', 160)->nullable()->index();
                $table->unsignedBigInteger('voucher_id')->nullable()->index();
                $table->string('status', 30)->default('posted')->index();
                $table->foreignId('created_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->timestamps();
            });
        }

        if (! Schema::hasTable('bangla_qr_settlement_items')) {
            Schema::create('bangla_qr_settlement_items', function (Blueprint $table) {
                $table->id();
                $table->foreignId('bangla_qr_settlement_id')->constrained('bangla_qr_settlements')->cascadeOnDelete();
                $table->foreignId('payment_transaction_id')->constrained('payment_transactions')->restrictOnDelete();
                $table->decimal('gross_amount', 24, 8)->default(0);
                $table->decimal('charge_amount', 24, 8)->default(0);
                $table->decimal('net_amount', 24, 8)->default(0);
                $table->timestamps();
            });
        }

        if (! Schema::hasTable('manual_payment_verifications')) {
            Schema::create('manual_payment_verifications', function (Blueprint $table) {
                $table->id();
                $table->foreignId('payment_transaction_id')->nullable()->constrained('payment_transactions')->nullOnDelete();
                $table->string('source_type', 80)->index();
                $table->unsignedBigInteger('source_id')->nullable()->index();
                $table->string('submitted_transaction_id', 160)->nullable()->index();
                $table->decimal('amount', 24, 8)->default(0);
                $table->string('status', 30)->default('pending')->index();
                $table->json('evidence_payload')->nullable();
                $table->foreignId('submitted_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->foreignId('verified_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->timestamp('verified_at')->nullable();
                $table->text('note')->nullable();
                $table->timestamps();
            });
        }
    }

    private function createPointLedger(): void
    {
        if (! Schema::hasTable('customer_point_ledgers')) {
            Schema::create('customer_point_ledgers', function (Blueprint $table) {
                $table->id();
                $table->foreignId('customer_id')->constrained('users')->cascadeOnDelete();
                $table->string('source_type', 80)->index();
                $table->unsignedBigInteger('source_id')->nullable()->index();
                $table->string('source_reference', 120)->nullable()->index();
                $table->string('point_type', 30)->index();
                $table->integer('points');
                $table->decimal('amount_value', 24, 8)->default(0);
                $table->integer('balance_after')->default(0);
                $table->text('note')->nullable();
                $table->foreignId('created_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->timestamps();

                $table->unique(['customer_id', 'source_type', 'source_id', 'point_type'], 'customer_point_source_unique');
            });
        }
    }

    private function createStockMovements(): void
    {
        if (! Schema::hasTable('stock_movements')) {
            Schema::create('stock_movements', function (Blueprint $table) {
                $table->id();
                $table->foreignId('item_id')->constrained('items')->cascadeOnDelete();
                $table->string('variation_type')->nullable()->index();
                $table->integer('quantity_delta');
                $table->integer('stock_before')->default(0);
                $table->integer('stock_after')->default(0);
                $table->integer('variation_stock_before')->nullable();
                $table->integer('variation_stock_after')->nullable();
                $table->string('movement_type', 30)->index();
                $table->string('source_type', 80)->index();
                $table->unsignedBigInteger('source_id')->nullable()->index();
                $table->string('source_reference', 120)->nullable()->index();
                $table->string('sale_channel', 30)->default('admin/manual')->index();
                $table->foreignId('created_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->unsignedBigInteger('voucher_id')->nullable()->index();
                $table->text('note')->nullable();
                $table->timestamps();

                $table->index(['item_id', 'source_type', 'source_id']);
            });
        }
    }

    private function createCashClosing(): void
    {
        if (! Schema::hasTable('showroom_cash_closings')) {
            Schema::create('showroom_cash_closings', function (Blueprint $table) {
                $table->id();
                $table->string('closing_no', 80)->unique();
                $table->date('business_date')->index();
                $table->string('showroom_id')->nullable()->index();
                $table->foreignId('closed_by_admin_id')->nullable()->constrained('admins')->nullOnDelete();
                $table->decimal('opening_cash', 24, 8)->default(0);
                $table->decimal('cash_sales', 24, 8)->default(0);
                $table->decimal('cash_refunds', 24, 8)->default(0);
                $table->decimal('expected_cash', 24, 8)->default(0);
                $table->decimal('counted_cash', 24, 8)->default(0);
                $table->decimal('difference_amount', 24, 8)->default(0);
                $table->string('status', 30)->default('closed')->index();
                $table->text('note')->nullable();
                $table->timestamps();
            });
        }

        if (! Schema::hasTable('showroom_cash_closing_payments')) {
            Schema::create('showroom_cash_closing_payments', function (Blueprint $table) {
                $table->id();
                $table->foreignId('showroom_cash_closing_id')->constrained('showroom_cash_closings')->cascadeOnDelete();
                $table->string('payment_method', 80)->index();
                $table->decimal('amount', 24, 8)->default(0);
                $table->unsignedInteger('transaction_count')->default(0);
                $table->timestamps();
            });
        }
    }

    private function extendExistingTables(): void
    {
        $this->addColumn('orders', 'sale_channel', fn (Blueprint $table) => $table->string('sale_channel', 30)->nullable()->index());
        $this->addColumn('orders', 'pos_created_by_admin_id', fn (Blueprint $table) => $table->unsignedBigInteger('pos_created_by_admin_id')->nullable()->index());
        $this->addColumn('orders', 'pos_device_id', fn (Blueprint $table) => $table->unsignedBigInteger('pos_device_id')->nullable()->index());
        $this->addColumn('orders', 'showroom_id', fn (Blueprint $table) => $table->string('showroom_id')->nullable()->index());
        $this->addColumn('orders', 'accounting_voucher_id', fn (Blueprint $table) => $table->unsignedBigInteger('accounting_voucher_id')->nullable()->index());
        $this->addColumn('orders', 'point_redeemed_points', fn (Blueprint $table) => $table->integer('point_redeemed_points')->default(0));
        $this->addColumn('orders', 'point_redeemed_amount', fn (Blueprint $table) => $table->decimal('point_redeemed_amount', 24, 8)->default(0));
        $this->addColumn('orders', 'point_earned', fn (Blueprint $table) => $table->integer('point_earned')->default(0));

        $this->addColumn('order_payments', 'payment_transaction_id', fn (Blueprint $table) => $table->unsignedBigInteger('payment_transaction_id')->nullable()->index());
        $this->addColumn('order_payments', 'provider_name', fn (Blueprint $table) => $table->string('provider_name', 80)->nullable()->index());
        $this->addColumn('order_payments', 'provider_transaction_id', fn (Blueprint $table) => $table->string('provider_transaction_id', 160)->nullable()->index());
        $this->addColumn('order_payments', 'qr_reference', fn (Blueprint $table) => $table->string('qr_reference', 160)->nullable()->index());
        $this->addColumn('order_payments', 'payment_payload', fn (Blueprint $table) => $table->json('payment_payload')->nullable());
        $this->addColumn('order_payments', 'paid_at', fn (Blueprint $table) => $table->timestamp('paid_at')->nullable());

        $this->addColumn('purchases', 'supplier_id', fn (Blueprint $table) => $table->unsignedBigInteger('supplier_id')->nullable()->index());
        $this->addColumn('purchases', 'payment_status', fn (Blueprint $table) => $table->string('payment_status', 30)->default('unpaid')->index());
        $this->addColumn('purchases', 'paid_amount', fn (Blueprint $table) => $table->decimal('paid_amount', 24, 8)->default(0));
        $this->addColumn('purchases', 'due_amount', fn (Blueprint $table) => $table->decimal('due_amount', 24, 8)->default(0));
        $this->addColumn('purchases', 'voucher_id', fn (Blueprint $table) => $table->unsignedBigInteger('voucher_id')->nullable()->index());

        $this->addColumn('purchase_items', 'stock_movement_id', fn (Blueprint $table) => $table->unsignedBigInteger('stock_movement_id')->nullable()->index());

        $this->addColumn('users', 'phone_normalized', fn (Blueprint $table) => $table->string('phone_normalized', 40)->nullable()->index());
    }

    private function dropExistingTableColumns(): void
    {
        $this->dropColumns('orders', [
            'sale_channel',
            'pos_created_by_admin_id',
            'pos_device_id',
            'showroom_id',
            'accounting_voucher_id',
            'point_redeemed_points',
            'point_redeemed_amount',
            'point_earned',
        ]);
        $this->dropColumns('order_payments', [
            'payment_transaction_id',
            'provider_name',
            'provider_transaction_id',
            'qr_reference',
            'payment_payload',
            'paid_at',
        ]);
        $this->dropColumns('purchases', [
            'supplier_id',
            'payment_status',
            'paid_amount',
            'due_amount',
            'voucher_id',
        ]);
        $this->dropColumns('purchase_items', ['stock_movement_id']);
        $this->dropColumns('users', ['phone_normalized']);
    }

    private function addColumn(string $table, string $column, callable $definition): void
    {
        if (! Schema::hasTable($table) || Schema::hasColumn($table, $column)) {
            return;
        }

        Schema::table($table, function (Blueprint $table) use ($definition) {
            $definition($table);
        });
    }

    private function dropColumns(string $table, array $columns): void
    {
        if (! Schema::hasTable($table)) {
            return;
        }

        $existing = array_values(array_filter($columns, fn ($column) => Schema::hasColumn($table, $column)));
        if ($existing === []) {
            return;
        }

        Schema::table($table, function (Blueprint $table) use ($existing) {
            $table->dropColumn($existing);
        });
    }
};
