<?php

namespace Database\Seeders;

use App\Models\Credit;
use App\Models\Installment;
use App\Models\Payment;
use App\Models\PaymentAuditLog;
use Carbon\Carbon;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class LegacyCredifyMigrationSeeder extends Seeder
{
    protected int $groupWindowSeconds = 10;

    public function run(): void
    {
        $this->command->info('--- Iniciando migración completa desde base legacy ---');

        try {

            // 🔥 OJO: NO transacciones internas
            DB::statement('SET FOREIGN_KEY_CHECKS=0');

            // 0) Tablas base
            $this->migrateCoreReferenceTables();

            // 1) Créditos
            $this->migrateCredits();

            // 2) Cuotas
            $this->migrateInstallments();

            // 3) Reconstrucción de pagos
            $this->rebuildPaymentsFromInstallments();

            // 4) Validación
            $this->validateTotals();

            DB::statement('SET FOREIGN_KEY_CHECKS=1');

            $this->command->info('--- Migración legacy COMPLETADA exitosamente ---');

        } catch (\Throwable $e) {

            DB::statement('SET FOREIGN_KEY_CHECKS=1');

            $this->command->error('❌ Error en migración legacy: '.$e->getMessage());
            throw $e;
        }
    }

    // =========================================================================
    // 0) TABLAS BASE
    // =========================================================================

    protected function migrateCoreReferenceTables(): void
    {
        $this->command->info('--- Migrando tablas base desde mysql_legacy ---');

        $this->copyTableFromLegacy('role_has_permissions');
        $this->copyTableFromLegacy('model_has_roles');
        $this->copyTableFromLegacy('model_has_permissions');

        $this->copyTableFromLegacy('permissions');
        $this->copyTableFromLegacy('roles');

        $this->copyTableFromLegacy('companies');
        $this->copyTableFromLegacy('users');

        $this->copyTableFromLegacy('clients');
        $this->copyTableFromLegacy('client_addresses');

        $this->copyTableFromLegacy('subscriptions');
        $this->copyTableFromLegacy('incomes');

        $this->copyTableFromLegacy('personal_access_tokens');

        $this->command->info('Tablas base migradas correctamente.');
    }

    /**
     * Copia tabla legacy → actual respetando columnas existentes.
     */
    protected function copyTableFromLegacy(string $table): void
    {
        $existsLegacy = DB::connection('mysql_legacy')
            ->getSchemaBuilder()
            ->hasTable($table);

        if (! $existsLegacy) {
            $this->command->warn("Tabla legacy '{$table}' no existe, omitida.");

            return;
        }

        $existsCurrent = DB::getSchemaBuilder()->hasTable($table);

        if (! $existsCurrent) {
            $this->command->warn("Tabla actual '{$table}' no existe, omitida.");

            return;
        }

        $this->command->info("Copiando tabla '{$table}'...");

        // Limpiar tabla actual
        DB::table($table)->truncate();

        $rows = DB::connection('mysql_legacy')->table($table)->get();
        if ($rows->isEmpty()) {
            $this->command->info("Tabla '{$table}' sin filas.");

            return;
        }

        $currentColumns = DB::getSchemaBuilder()->getColumnListing($table);

        $rows->chunk(500)->each(function ($chunk) use ($table, $currentColumns) {
            $filtered = $chunk->map(function ($row) use ($currentColumns) {
                return array_intersect_key((array) $row, array_flip($currentColumns));
            })->toArray();

            if (! empty($filtered)) {
                DB::table($table)->insert($filtered);
            }
        });

        $this->command->info("Tabla '{$table}' copiada ({$rows->count()} filas).");
    }

    // =========================================================================
    // 1) CRÉDITOS
    // =========================================================================

    protected function migrateCredits(): void
    {
        $this->command->info('Migrando créditos...');

        if (! DB::connection('mysql_legacy')->getSchemaBuilder()->hasTable('credits')) {
            $this->command->warn('Tabla legacy credits no existe.');

            return;
        }

        DB::table('credits')->truncate();

        $legacyCredits = DB::connection('mysql_legacy')
            ->table('credits')
            ->orderBy('id')
            ->get();

        $this->command->info("Encontrados {$legacyCredits->count()} créditos.");

        Credit::unguard();

        foreach ($legacyCredits as $legacy) {
            Credit::create([
                'id' => $legacy->id,
                'company_id' => $legacy->company_id,
                'client_id' => $legacy->client_id,
                'collector_user_id' => $legacy->collector_user_id,
                'created_by_user_id' => $legacy->created_by_user_id,
                'status' => $legacy->status,
                'amount' => $legacy->amount,
                'interest_rate' => $legacy->interest_rate,
                'installments_count' => $legacy->installments_count,
                'periodicity' => $legacy->periodicity,
                'start_date' => $legacy->start_date,
                'due_date' => $legacy->due_date,
                'due_day_1' => $legacy->due_day_1,
                'due_day_2' => $legacy->due_day_2,
                'created_at' => $legacy->created_at,
                'updated_at' => $legacy->updated_at,
            ]);
        }

        Credit::reguard();

        $this->command->info('Créditos migrados.');
    }

    // =========================================================================
    // 2) CUOTAS
    // =========================================================================

    protected function migrateInstallments(): void
    {
        $this->command->info('Migrando cuotas...');

        if (! DB::connection('mysql_legacy')->getSchemaBuilder()->hasTable('installments')) {
            $this->command->warn('Tabla legacy installments no existe.');

            return;
        }

        DB::table('installments')->truncate();

        $legacy = DB::connection('mysql_legacy')
            ->table('installments')
            ->orderBy('id')
            ->get();

        Installment::unguard();

        foreach ($legacy as $i) {
            Installment::create([
                'id' => $i->id,
                'credit_id' => $i->credit_id,
                'installment_number' => $i->installment_number,
                'status' => $i->status,
                'due_date' => $i->due_date,
                'paid_date' => $i->paid_date,
                'principal_amount' => $i->principal_amount,
                'interest_amount' => $i->interest_amount,
                'total_amount' => $i->total_amount,
                'principal_balance_after' => $i->principal_balance_after,
                'amount_paid' => $i->amount_paid,
                'created_at' => $i->created_at,
                'updated_at' => $i->updated_at,
            ]);
        }

        Installment::reguard();

        $this->command->info('Cuotas migradas.');
    }

    // =========================================================================
    // 3) PAGOS + PIVOT
    // =========================================================================

    protected function rebuildPaymentsFromInstallments(): void
    {
        $this->command->info('Reconstruyendo pagos...');

        DB::table('payment_installment')->truncate();
        DB::table('payments')->truncate();
        DB::table('payment_audit_logs')->truncate();

        $credits = Credit::with(['installments' => fn ($q) => $q->where('amount_paid', '>', 0)
            ->orderBy('updated_at')
            ->orderBy('id'),
        ])->orderBy('id')->get();

        $count = 0;

        foreach ($credits as $credit) {
            $inst = $credit->installments;

            if ($inst->isEmpty()) {
                continue;
            }

            $this->command->info("Reconstruyendo pagos para crédito #{$credit->id}...");

            $groups = $this->groupInstallmentsByTimeWindow($inst);

            foreach ($groups as $g) {
                $payment = $this->createPaymentFromGroup($credit, $g);
                if ($payment) {
                    $count++;
                }
            }
        }

        $this->command->info("Pagos reconstruidos: {$count}.");
    }

    protected function groupInstallmentsByTimeWindow($inst): array
    {
        $groups = [];
        $current = [];
        $lastTs = null;

        foreach ($inst as $i) {
            if ($i->amount_paid <= 0) {
                continue;
            }

            $ts = Carbon::parse($i->updated_at ?? $i->paid_date ?? $i->created_at ?? now());

            if (! $lastTs) {
                $current = [$i];
                $lastTs = $ts;

                continue;
            }

            if ($ts->diffInSeconds($lastTs) <= $this->groupWindowSeconds) {
                $current[] = $i;
                $lastTs = $ts;
            } else {
                $groups[] = $current;
                $current = [$i];
                $lastTs = $ts;
            }
        }

        if (! empty($current)) {
            $groups[] = $current;
        }

        return $groups;
    }

    protected function createPaymentFromGroup(Credit $credit, array $group): ?Payment
    {
        $total = collect($group)->sum('amount_paid');
        if ($total <= 0) {
            return null;
        }

        $user = $credit->collector_user_id ?? $credit->created_by_user_id;

        $paymentDate = collect($group)
            ->map(fn ($i) => $i->paid_date ?? $i->updated_at ?? $i->created_at)
            ->filter()
            ->map(fn ($d) => Carbon::parse($d))
            ->sort()
            ->last() ?? Carbon::parse($credit->start_date);

        $p = Payment::create([
            'company_id' => $credit->company_id,
            'credit_id' => $credit->id,
            'installment_id' => null,
            'amount' => $total,
            'applied_amount' => $total,
            'extra_amount' => 0,
            'payment_date' => $paymentDate->toDateString(),
            'payment_method' => 'transfer',
            'registered_by_user_id' => $user,
            'voided' => false,
            'created_at' => $paymentDate,
            'updated_at' => $paymentDate,
        ]);

        foreach ($group as $i) {
            DB::table('payment_installment')->insert([
                'payment_id' => $p->id,
                'installment_id' => $i->id,
                'applied_amount' => $i->amount_paid,
                'created_at' => $paymentDate,
                'updated_at' => $paymentDate,
            ]);
        }

        PaymentAuditLog::create([
            'payment_id' => $p->id,
            'credit_id' => $credit->id,
            'user_id' => $user,
            'company_id' => $credit->company_id,
            'action' => 'imported_from_legacy',
            'reason' => 'Migración legacy',
            'after_state' => json_encode(['payment' => $p->toArray()]),
        ]);

        return $p;
    }

    // =========================================================================
    // 4) VALIDACIÓN FINAL
    // =========================================================================

    protected function validateTotals(): void
    {
        $sumInstallments = Installment::sum('amount_paid');
        $sumPayments = Payment::sum('applied_amount');

        $this->command->info("Total installments.amount_paid = {$sumInstallments}");
        $this->command->info("Total payments.applied_amount = {$sumPayments}");

        if ($sumInstallments != $sumPayments) {
            $this->command->warn('⚠ Descuadre final.');
        } else {
            $this->command->info('✅ Totales coinciden correctamente.');
        }
    }
}
