<?php

namespace App\Http\Controllers\Admin;

use App\Enums\ForexImportStagingStatus;
use App\Enums\InvestmentStatus;
use App\Http\Controllers\Controller;
use App\Models\ForexImportStagingItem;
use App\Models\Investment;
use App\Models\User;
use App\Services\ForexCsvImportService;
use App\Support\InvestmentLedger;
use Carbon\Carbon;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Inertia\Response;
use RuntimeException;

class ForexImportStagingController extends Controller
{
    public function index(): Response
    {
        $pending = ForexImportStagingItem::query()
            ->where('status', ForexImportStagingStatus::Pending)
            ->orderBy('email_hint')
            ->orderBy('csv_record_index')
            ->orderBy('id')
            ->get();

        /** @var Collection<string, Collection<int, ForexImportStagingItem>> $grouped */
        $grouped = $pending->groupBy(static fn (ForexImportStagingItem $row): string => mb_strtolower((string) $row->email_hint));

        $groups = $grouped->map(function (Collection $items, string $emailKey): array {
            /** @var ForexImportStagingItem $head */
            $head = $items->sortBy(['csv_record_index', 'id'])->first();

            $email = (string) $head->email_hint;
            $user = User::query()->where('email', $email)->first();

            $plans = $items->sortBy(['csv_record_index', 'id'])->values()->map(function (ForexImportStagingItem $row): array {
                $payload = $row->payload ?? [];
                $preview = InvestmentLedger::previewForexImportCatchUpFromStagingItem($row);

                return [
                    'id' => $row->id,
                    'csv_record_index' => $row->csv_record_index,
                    'email_hint' => $row->email_hint,
                    'first_name' => $row->first_name,
                    'last_name' => $row->last_name,
                    'contact_number' => $row->contact_number,
                    'country' => $row->country,
                    'amount' => (string) $row->amount,
                    'duration_months' => $row->duration_months,
                    'confirmed_at' => $row->confirmed_at?->toIso8601String(),
                    'maturity_at' => $row->maturity_at?->toIso8601String(),
                    'monthly_return_snapshot' => (string) $row->monthly_return_snapshot,
                    'wallet_address' => $row->wallet_address,
                    'csv_saldo_ganancias' => $payload['csv_saldo_ganancias'] ?? null,
                    'csv_saldo_total' => $payload['csv_saldo_total'] ?? null,
                    'csv_saldo_bloqueado' => $payload['csv_saldo_bloqueado'] ?? null,
                    'csv_saldo_pagado' => $payload['csv_saldo_pagado'] ?? null,
                    'accrual_preview' => $preview,
                ];
            })->all();

            $newPrincipal = round($items->sum(static fn (ForexImportStagingItem $i): float => (float) $i->amount), 2);

            $accrualGroupTotal = round(array_sum(array_map(
                static fn (array $p): float => (float) ($p['accrual_preview']['total_gain_usd'] ?? 0),
                $plans,
            )), 2);

            $headPreview = $plans[0]['accrual_preview'] ?? null;

            return [
                'email' => $email,
                'email_key' => $emailKey,
                'is_new_user' => $user === null,
                'proposed' => [
                    'first_name' => $head->first_name,
                    'last_name' => $head->last_name,
                    'contact_number' => $head->contact_number,
                    'country' => $head->country,
                    'wallet_address' => $items->first(static fn (ForexImportStagingItem $i): bool => filled($i->wallet_address))?->wallet_address,
                ],
                'existing_user' => $user === null ? null : [
                    'id' => $user->id,
                    'name' => $user->name,
                    'email' => $user->email,
                    'kyc_status' => $user->kyc_status->value,
                ],
                'portfolio' => $user === null ? null : self::portfolioSnapshotForUser($user),
                'plans' => $plans,
                'plans_count' => count($plans),
                'new_principal_total_usd' => $newPrincipal,
                'accrual_group' => [
                    'total_gain_usd' => $accrualGroupTotal,
                    'cutoff_date' => is_array($headPreview) ? $headPreview['cutoff_date'] : now()->toDateString(),
                    'mode' => is_array($headPreview) ? $headPreview['mode'] : 'daily',
                    'accrual_day_of_month' => is_array($headPreview) ? $headPreview['accrual_day_of_month'] : null,
                ],
            ];
        })->values()->all();

        return Inertia::render('admin/forex-import-staging', [
            'groups' => $groups,
            'pending_count' => $pending->count(),
            'groups_count' => count($groups),
        ]);
    }

    /**
     * @return array<string, float|int>
     */
    private static function portfolioSnapshotForUser(User $user): array
    {
        $active = Investment::query()
            ->where('user_id', $user->id)
            ->where('status', InvestmentStatus::Active)
            ->get(['amount', 'capital_balance']);

        $principalLocked = round($active->sum(static fn (Investment $i): float => (float) $i->amount), 2);
        $capitalInContracts = round($active->sum(static fn (Investment $i): float => (float) ($i->capital_balance ?? $i->amount)), 2);
        $withdrawable = round(InvestmentLedger::totalWithdrawableForUser($user->id), 2);

        $pendingApp = Investment::query()
            ->where('user_id', $user->id)
            ->where('status', InvestmentStatus::Pending)
            ->count();

        return [
            'withdrawable_usd' => $withdrawable,
            'principal_locked_usd' => $principalLocked,
            'capital_in_contracts_usd' => $capitalInContracts,
            'active_investments' => $active->count(),
            'pending_investments' => $pendingApp,
        ];
    }

    public function approveUser(Request $request, ForexCsvImportService $importer): RedirectResponse
    {
        $validated = $request->validate([
            'email' => ['required', 'string', 'email:rfc'],
        ]);

        $email = mb_strtolower(trim($validated['email']));

        try {
            $count = $importer->approveStagingGroupByEmail($email, $request->user());
        } catch (RuntimeException $e) {
            Inertia::flash('toast', [
                'type' => 'error',
                'message' => $e->getMessage(),
            ]);

            return redirect()->route('admin.forex-import.index');
        }

        Inertia::flash('toast', [
            'type' => 'success',
            'message' => $count === 1
                ? 'Importación aprobada: 1 inversión activa con depósito en libro e intereses atrasados según la política actual.'
                : "Importación aprobada: {$count} inversiones activas con depósito en libro e intereses atrasados según la política actual.",
        ]);

        return redirect()->route('admin.forex-import.index');
    }

    public function update(Request $request, ForexImportStagingItem $forexImportStagingItem): RedirectResponse
    {
        if ($forexImportStagingItem->status !== ForexImportStagingStatus::Pending) {
            Inertia::flash('toast', [
                'type' => 'error',
                'message' => 'Solo se pueden editar registros pendientes.',
            ]);

            return redirect()->route('admin.forex-import.index');
        }

        $validated = $request->validate([
            'email_hint' => ['required', 'string', 'email:rfc', 'max:255'],
            'first_name' => ['nullable', 'string', 'max:255'],
            'last_name' => ['nullable', 'string', 'max:255'],
            'contact_number' => ['nullable', 'string', 'max:50'],
            'country' => ['nullable', 'string', 'max:64'],
            'wallet_address' => ['nullable', 'string', 'max:512'],
            'amount' => ['required', 'numeric', 'min:0.01', 'max:999999999999'],
            'duration_months' => ['required', 'integer', 'min:1', 'max:1200'],
            'confirmed_at' => ['nullable', 'date'],
            'maturity_at' => ['nullable', 'date'],
            'monthly_return_snapshot' => ['required', 'numeric', 'min:0', 'max:100'],
        ]);

        $email = ForexCsvImportService::normalizeEmail($validated['email_hint']);

        $confirmedAt = isset($validated['confirmed_at']) && $validated['confirmed_at'] !== null && $validated['confirmed_at'] !== ''
            ? Carbon::parse($validated['confirmed_at'])->startOfDay()
            : null;
        $maturityAt = isset($validated['maturity_at']) && $validated['maturity_at'] !== null && $validated['maturity_at'] !== ''
            ? Carbon::parse($validated['maturity_at'])->startOfDay()
            : null;

        if ($confirmedAt !== null && $maturityAt !== null && $maturityAt->lt($confirmedAt)) {
            throw ValidationException::withMessages([
                'maturity_at' => ['La fecha de vencimiento debe ser el mismo día o posterior al inicio.'],
            ]);
        }

        $amount = round((float) $validated['amount'], 2);
        $duration = (int) $validated['duration_months'];

        $checksum = ForexCsvImportService::computeStagingRowChecksum(
            $email,
            (int) $forexImportStagingItem->csv_record_index,
            $amount,
            $duration,
            $confirmedAt,
            $maturityAt,
        );

        if (ForexImportStagingItem::query()
            ->where('row_checksum', $checksum)
            ->whereKeyNot($forexImportStagingItem->id)
            ->exists()) {
            throw ValidationException::withMessages([
                'amount' => ['Estos datos coinciden con otro registro pendiente. Cambia monto, fechas o correo.'],
            ]);
        }

        $txid = ForexCsvImportService::IMPORT_TXID_PREFIX.substr($checksum, 0, 24);
        if (Investment::query()->where('txid', $txid)->exists()) {
            throw ValidationException::withMessages([
                'amount' => ['Ya existe una inversión registrada con el identificador derivado de estos valores.'],
            ]);
        }

        $country = ForexCsvImportService::normalizeCountryCodeForUser($validated['country'] ?? null);

        $forexImportStagingItem->update([
            'row_checksum' => $checksum,
            'email_hint' => $email,
            'first_name' => $validated['first_name'] !== null && $validated['first_name'] !== '' ? $validated['first_name'] : null,
            'last_name' => $validated['last_name'] !== null && $validated['last_name'] !== '' ? $validated['last_name'] : null,
            'contact_number' => $validated['contact_number'] !== null && $validated['contact_number'] !== '' ? $validated['contact_number'] : null,
            'country' => $country,
            'wallet_address' => $validated['wallet_address'] !== null && $validated['wallet_address'] !== '' ? $validated['wallet_address'] : null,
            'amount' => $amount,
            'duration_months' => $duration,
            'confirmed_at' => $confirmedAt,
            'maturity_at' => $maturityAt,
            'monthly_return_snapshot' => round((float) $validated['monthly_return_snapshot'], 2),
        ]);

        Inertia::flash('toast', [
            'type' => 'success',
            'message' => 'Registro actualizado. Los intereses simulados y el TXID se recalculan; no afecta a datos ya importados.',
        ]);

        return redirect()->route('admin.forex-import.index');
    }

    public function destroy(ForexImportStagingItem $forexImportStagingItem): RedirectResponse
    {
        if ($forexImportStagingItem->status !== ForexImportStagingStatus::Pending) {
            Inertia::flash('toast', [
                'type' => 'error',
                'message' => 'Solo se pueden eliminar planes pendientes.',
            ]);

            return redirect()->route('admin.forex-import.index');
        }

        $forexImportStagingItem->delete();

        Inertia::flash('toast', [
            'type' => 'success',
            'message' => 'Plan eliminado de la cola de importación. Esta acción es permanente.',
        ]);

        return redirect()->route('admin.forex-import.index');
    }

    public function discardUser(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'email' => ['required', 'string', 'email:rfc'],
        ]);

        $email = mb_strtolower(trim($validated['email']));

        $n = ForexImportStagingItem::query()
            ->where('status', ForexImportStagingStatus::Pending)
            ->whereRaw('LOWER(email_hint) = ?', [$email])
            ->update(['status' => ForexImportStagingStatus::Discarded]);

        Inertia::flash('toast', [
            'type' => $n > 0 ? 'success' : 'error',
            'message' => $n > 0
                ? "Se descartaron {$n} registro(s) para este correo. No se pueden recuperar en esta cola."
                : 'No había registros pendientes para ese correo.',
        ]);

        return redirect()->route('admin.forex-import.index');
    }

    public function discard(ForexImportStagingItem $forexImportStagingItem): RedirectResponse
    {
        if ($forexImportStagingItem->status !== ForexImportStagingStatus::Pending) {
            Inertia::flash('toast', [
                'type' => 'error',
                'message' => 'Este ítem ya no está pendiente.',
            ]);

            return redirect()->route('admin.forex-import.index');
        }

        $forexImportStagingItem->update([
            'status' => ForexImportStagingStatus::Discarded,
        ]);

        Inertia::flash('toast', [
            'type' => 'success',
            'message' => 'Registro descartado. No se puede revertir desde esta cola.',
        ]);

        return redirect()->route('admin.forex-import.index');
    }
}
