<?php

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

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('telepagos_payment_candidates', function (Blueprint $table) {
            $table->id();
            $table->foreignId('telepagos_payment_id')
                ->constrained('telepagos_payments')
                ->cascadeOnDelete();
            $table->foreignId('compra_id')->constrained('compras')->cascadeOnDelete();
            $table->boolean('dni_matches');
            $table->boolean('amount_matches');
            $table->decimal('payment_amount', 10, 2);
            $table->decimal('purchase_amount', 10, 2);
            $table->decimal('amount_difference', 10, 2);
            $table->string('match_reason');
            $table->string('confidence');
            $table->timestamps();

            $table->unique(
                ['telepagos_payment_id', 'compra_id'],
                'telepagos_payment_candidate_unique',
            );
        });

        $this->migrateExistingCandidates();

        Schema::table('telepagos_payments', function (Blueprint $table) {
            $table->dropColumn('matched_purchase_ids');
        });
    }

    public function down(): void
    {
        Schema::table('telepagos_payments', function (Blueprint $table) {
            $table->json('matched_purchase_ids')->nullable()->after('compra_id');
        });

        DB::table('telepagos_payments')
            ->whereNull('compra_id')
            ->orderBy('id')
            ->each(function (object $payment): void {
                $candidateIds = DB::table('telepagos_payment_candidates')
                    ->where('telepagos_payment_id', $payment->id)
                    ->pluck('compra_id')
                    ->all();

                if ($candidateIds !== []) {
                    DB::table('telepagos_payments')
                        ->where('id', $payment->id)
                        ->update(['matched_purchase_ids' => json_encode($candidateIds)]);
                }
            });

        Schema::dropIfExists('telepagos_payment_candidates');
    }

    private function migrateExistingCandidates(): void
    {
        DB::table('telepagos_payments')
            ->whereNull('compra_id')
            ->whereNotNull('matched_purchase_ids')
            ->orderBy('id')
            ->each(function (object $payment): void {
                $candidateIds = json_decode((string) $payment->matched_purchase_ids, true);

                if (! is_array($candidateIds)) {
                    return;
                }

                $paymentAmount = number_format((float) $payment->amount, 2, '.', '');
                $payerDni = $payment->cuit_buyer
                    ? substr((string) $payment->cuit_buyer, 2, -1)
                    : null;

                foreach ($candidateIds as $candidateId) {
                    $purchase = DB::table('compras')->find($candidateId);

                    if ($purchase === null) {
                        continue;
                    }

                    $purchaseAmount = number_format((float) $purchase->total, 2, '.', '');
                    $dniMatches = $payerDni !== null && $purchase->transfer_payer_dni === $payerDni;
                    $amountMatches = $purchaseAmount === $paymentAmount;

                    DB::table('telepagos_payment_candidates')->insertOrIgnore([
                        'telepagos_payment_id' => $payment->id,
                        'compra_id' => $purchase->id,
                        'dni_matches' => $dniMatches,
                        'amount_matches' => $amountMatches,
                        'payment_amount' => $paymentAmount,
                        'purchase_amount' => $purchaseAmount,
                        'amount_difference' => number_format(
                            abs((float) $purchaseAmount - (float) $paymentAmount),
                            2,
                            '.',
                            '',
                        ),
                        'match_reason' => $this->matchReason($dniMatches, $amountMatches),
                        'confidence' => $this->confidence($dniMatches, $amountMatches),
                        'created_at' => $payment->created_at,
                        'updated_at' => $payment->updated_at,
                    ]);
                }
            });
    }

    private function matchReason(bool $dniMatches, bool $amountMatches): string
    {
        if ($dniMatches && $amountMatches) {
            return 'ambiguous_exact_match';
        }

        if ($dniMatches) {
            return 'exact_dni_near_amount';
        }

        if ($amountMatches) {
            return 'exact_amount_different_dni';
        }

        return 'legacy_candidate';
    }

    private function confidence(bool $dniMatches, bool $amountMatches): string
    {
        if ($dniMatches && $amountMatches) {
            return 'exact';
        }

        return $dniMatches ? 'high' : 'medium';
    }
};
