<?php

namespace App\Http\Controllers\Admin;

use App\Enums\InvestmentStatus;
use App\Enums\InvestmentTransactionType;
use App\Http\Controllers\Controller;
use App\Models\Investment;
use App\Models\InvestmentTransaction;
use App\Services\ForexCsvImportService;
use App\Support\InvestmentLedger;
use App\Support\UserInvestmentPortfolio;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Inertia\Response;

class InvestmentManageController extends Controller
{
    private const INVESTMENTS_PER_PAGE = 15;

    /**
     * Pending and confirmed (active) investments for administration.
     */
    public function index(Request $request): Response
    {
        $q = trim((string) $request->query('q', ''));
        $tab = (string) $request->query('tab', 'active');
        if (! in_array($tab, ['active', 'pending', 'completed', 'cancelled'], true)) {
            $tab = 'active';
        }

        $baseQuery = Investment::query()
            ->where('txid', 'not like', InvestmentLedger::ADMIN_CREDIT_TXID_PREFIX.'%')
            ->when($q !== '', function ($query) use ($q): void {
                $needle = '%'.$q.'%';
                $query->where(function ($sub) use ($needle, $q): void {
                    $sub->where('txid', 'like', $needle)
                        ->orWhereHas('user', function ($userQuery) use ($needle): void {
                            $userQuery->where('email', 'like', $needle)
                                ->orWhere('first_name', 'like', $needle)
                                ->orWhere('last_name', 'like', $needle);
                        });
                    if (is_numeric($q)) {
                        $sub->orWhere('id', (int) $q);
                    }
                });
            });

        $columns = [
            'id',
            'user_id',
            'amount',
            'capital_balance',
            'months_credited',
            'duration_months',
            'monthly_return_snapshot',
            'txid',
            'status',
            'created_at',
            'confirmed_at',
            'maturity_at',
        ];

        $mapContractMonths = static function (Investment $investment): Investment {
            $investment->months_credited = InvestmentLedger::accreditedContractMonths($investment);

            return $investment;
        };

        $statusForTab = match ($tab) {
            'pending' => InvestmentStatus::Pending,
            'completed' => InvestmentStatus::Completed,
            'cancelled' => InvestmentStatus::Cancelled,
            default => InvestmentStatus::Active,
        };

        $investmentsQuery = (clone $baseQuery)
            ->with(['user:id,first_name,last_name,email'])
            ->where('status', $statusForTab)
            ->orderByDesc('created_at');

        $paginator = $investmentsQuery
            ->paginate(self::INVESTMENTS_PER_PAGE, $columns)
            ->withQueryString();

        if (in_array($tab, ['active', 'completed'], true)) {
            $paginator->through($mapContractMonths);
        }

        $tabCounts = [
            'active' => (clone $baseQuery)->where('status', InvestmentStatus::Active)->count(),
            'pending' => (clone $baseQuery)->where('status', InvestmentStatus::Pending)->count(),
            'completed' => (clone $baseQuery)->where('status', InvestmentStatus::Completed)->count(),
            'cancelled' => (clone $baseQuery)->where('status', InvestmentStatus::Cancelled)->count(),
        ];

        $totalActiveCapital = (float) ((clone $baseQuery)
            ->where('status', InvestmentStatus::Active)
            ->selectRaw('COALESCE(SUM(COALESCE(capital_balance, amount)), 0) as total')
            ->value('total') ?? 0);

        return Inertia::render('admin/investments-overview', [
            'investments' => $paginator,
            'tab_counts' => $tabCounts,
            'summary' => [
                'active_count' => $tabCounts['active'],
                'total_capital_usd' => number_format($totalActiveCapital, 2, '.', ''),
            ],
            'filters' => [
                'q' => $q,
                'tab' => $tab,
            ],
            'adjustments_enabled' => config('app.allow_adjustments'),
        ]);
    }

    /**
     * Detalle de acreditaciones (ganancias) de una inversión.
     */
    public function accruals(Investment $investment): Response
    {
        if (InvestmentLedger::isAdminAdjustmentInvestment($investment)) {
            abort(404);
        }

        $investment->load(['user:id,first_name,last_name,email']);

        $gainTypes = [
            InvestmentTransactionType::DailyGain,
            InvestmentTransactionType::PeriodAccrual,
            InvestmentTransactionType::MonthlyGain,
        ];

        $lines = InvestmentTransaction::query()
            ->where('investment_id', $investment->id)
            ->whereIn('type', array_map(static fn (InvestmentTransactionType $t): string => $t->value, $gainTypes))
            ->orderByDesc('accrual_on')
            ->orderByDesc('id')
            ->get();

        $sourceIds = $lines->pluck('id')->all();

        $referralsBySource = $sourceIds === []
            ? collect()
            : InvestmentTransaction::query()
                ->whereIn('referral_source_transaction_id', $sourceIds)
                ->with(['beneficiary:id,first_name,last_name,email'])
                ->orderBy('referral_level')
                ->orderBy('id')
                ->get()
                ->groupBy('referral_source_transaction_id');

        $totalGain = (float) $lines->sum('amount');
        $totalReferralPaid = (float) $referralsBySource->flatten(1)->sum('amount');

        $user = $investment->user;

        return Inertia::render('admin/investment-accruals', [
            'investment' => [
                'id' => $investment->id,
                'txid' => $investment->txid,
                'status' => $investment->status->value,
                'amount' => number_format((float) $investment->amount, 2, '.', ''),
                'capital_balance' => number_format(
                    (float) ($investment->capital_balance ?? $investment->amount),
                    2,
                    '.',
                    '',
                ),
                'duration_months' => $investment->duration_months,
                'months_credited' => InvestmentLedger::accreditedContractMonths($investment),
                'monthly_return_snapshot' => number_format((float) $investment->monthly_return_snapshot, 2, '.', ''),
                'investor_name' => $user !== null
                    ? trim("{$user->first_name} {$user->last_name}")
                    : '—',
                'investor_email' => $user?->email,
            ],
            'summary' => [
                'accrual_count' => $lines->count(),
                'total_gain_usd' => number_format($totalGain, 2, '.', ''),
                'total_referral_usd' => number_format($totalReferralPaid, 2, '.', ''),
            ],
            'lines' => $lines->map(function (InvestmentTransaction $txn) use ($referralsBySource): array {
                $typeLabel = match ($txn->type) {
                    InvestmentTransactionType::DailyGain => 'Ganancia diaria',
                    InvestmentTransactionType::PeriodAccrual => 'Acreditación por período',
                    InvestmentTransactionType::MonthlyGain => 'Ganancia mensual (histórico)',
                    default => $txn->type->value,
                };

                /** @var Collection<int, InvestmentTransaction> $referralRows */
                $referralRows = $referralsBySource->get($txn->id, collect());

                $referrals = $referralRows->map(static function (InvestmentTransaction $ref): array {
                    $beneficiary = $ref->beneficiary;

                    $typeLabel = match ($ref->type) {
                        InvestmentTransactionType::ReferralDirectBonus => 'Directo (nivel 1)',
                        InvestmentTransactionType::ReferralIndirectBonus => 'Indirecto (nivel 2)',
                        default => $ref->type->value,
                    };

                    return [
                        'id' => $ref->id,
                        'level' => $ref->referral_level,
                        'type_label' => $typeLabel,
                        'beneficiary_name' => $beneficiary !== null
                            ? trim("{$beneficiary->first_name} {$beneficiary->last_name}")
                            : '—',
                        'beneficiary_email' => $beneficiary?->email,
                        'amount_usd' => number_format((float) $ref->amount, 2, '.', ''),
                        'percent' => $ref->applied_percent !== null
                            ? number_format((float) $ref->applied_percent, 4, '.', '')
                            : null,
                    ];
                })->values()->all();

                $referralTotal = (float) $referralRows->sum('amount');

                return [
                    'id' => $txn->id,
                    'type' => $txn->type->value,
                    'type_label' => $typeLabel,
                    'accrual_on' => $txn->accrual_on?->toDateString(),
                    'amount_usd' => number_format((float) $txn->amount, 2, '.', ''),
                    'balance_after_usd' => $txn->balance_after !== null
                        ? number_format((float) $txn->balance_after, 2, '.', '')
                        : null,
                    'applied_percent_label' => InvestmentLedger::appliedPercentLabel($txn),
                    'gain_period_start' => $txn->gain_period_start?->toDateString(),
                    'gain_period_end' => $txn->gain_period_end?->toDateString(),
                    'period_label' => $txn->gain_period_start !== null && $txn->gain_period_end !== null
                        ? UserInvestmentPortfolio::formatContractMonthLabel(
                            Carbon::parse($txn->gain_period_start)->startOfDay(),
                            Carbon::parse($txn->gain_period_end)->copy()->addDay()->startOfDay(),
                        )
                        : null,
                    'investment_month_index' => $txn->investment_month_index,
                    'referral_paid' => $referralRows->isNotEmpty(),
                    'referral_total_usd' => number_format($referralTotal, 2, '.', ''),
                    'referrals' => $referrals,
                ];
            })->values()->all(),
        ]);
    }

    /**
     * Confirm a pending investment (activate it).
     */
    public function confirm(Request $request, Investment $investment): RedirectResponse
    {
        if (InvestmentLedger::isAdminAdjustmentInvestment($investment)) {
            abort(404);
        }

        if ($investment->status !== InvestmentStatus::Pending) {
            Inertia::flash('toast', [
                'type' => 'error',
                'message' => 'La inversión ya no está pendiente.',
            ]);

            return redirect()->route('admin.investments.index', $request->only(['q', 'tab', 'page']));
        }

        $isForexImport = str_starts_with($investment->txid, ForexCsvImportService::IMPORT_TXID_PREFIX);

        if ($isForexImport && $investment->confirmed_at !== null) {
            $confirmedAt = Carbon::parse($investment->confirmed_at);
            $maturityAt = $investment->maturity_at !== null
                ? Carbon::parse($investment->maturity_at)
                : $confirmedAt->copy()->addMonths((int) $investment->duration_months);
        } else {
            $confirmedAt = now();
            $maturityAt = Carbon::parse($confirmedAt)->addMonths((int) $investment->duration_months);
        }

        $investment->update([
            'status' => InvestmentStatus::Active,
            'confirmed_at' => $confirmedAt,
            'maturity_at' => $maturityAt,
            'confirmed_by' => $request->user()->id,
        ]);

        InvestmentLedger::recordInitialDepositIfMissing($investment->fresh());
        InvestmentLedger::creditReferralBonusesOnInvestmentApproval($investment->fresh());
        InvestmentLedger::syncInvestmentContractProgress($investment->fresh());

        Inertia::flash('toast', [
            'type' => 'success',
            'message' => 'Inversión confirmada y marcada como activa.',
        ]);

        return redirect()->route('admin.investments.index', $request->only(['q', 'tab', 'page']));
    }

    /**
     * Anula una inversión pendiente o activa.
     */
    public function void(Request $request, Investment $investment): RedirectResponse
    {
        if (InvestmentLedger::isAdminAdjustmentInvestment($investment)) {
            abort(404);
        }

        $wasPending = $investment->status === InvestmentStatus::Pending;

        try {
            InvestmentLedger::voidInvestment($investment);
        } catch (ValidationException $exception) {
            Inertia::flash('toast', [
                'type' => 'error',
                'message' => collect($exception->errors())->flatten()->first()
                    ?? 'No se pudo anular la inversión.',
            ]);

            return redirect()->route('admin.investments.index', $request->only(['q', 'tab', 'page']));
        }

        Inertia::flash('toast', [
            'type' => 'success',
            'message' => $wasPending
                ? 'Solicitud de inversión anulada.'
                : 'Inversión anulada y capital liquidado en el libro.',
        ]);

        return redirect()->route('admin.investments.index', $request->only(['q', 'tab', 'page']));
    }

    /**
     * Reactiva una inversión finalizada.
     */
    public function reactivate(Request $request, Investment $investment): RedirectResponse
    {
        if (InvestmentLedger::isAdminAdjustmentInvestment($investment)) {
            abort(404);
        }

        $validated = $request->validate([
            'extension_months' => ['nullable', 'integer', 'min:0', 'max:12'],
        ]);

        try {
            InvestmentLedger::reactivateInvestment($investment, (int) ($validated['extension_months'] ?? 0));
        } catch (ValidationException $exception) {
            Inertia::flash('toast', [
                'type' => 'error',
                'message' => collect($exception->errors())->flatten()->first()
                    ?? 'No se pudo reactivar la inversión.',
            ]);

            return redirect()->route('admin.investments.index', $request->only(['q', 'tab', 'page']));
        }

        Inertia::flash('toast', [
            'type' => 'success',
            'message' => ((int) ($validated['extension_months'] ?? 0)) > 0
                ? 'Inversión reactivada y contrato prorrogado.'
                : 'Inversión reactivada. Volverá a recibir acreditaciones mientras siga activa.',
        ]);

        return redirect()->route('admin.investments.index', $request->only(['q', 'tab', 'page']));
    }

    /**
     * Finaliza manualmente una inversión activa con todas sus acreditaciones (solo ALLOW_ADJUSTMENTS).
     */
    public function finalize(Request $request, Investment $investment): RedirectResponse
    {
        abort_unless(config('app.allow_adjustments'), 404);

        if (InvestmentLedger::isAdminAdjustmentInvestment($investment)) {
            abort(404);
        }

        try {
            InvestmentLedger::manualFinalizeFullyAccreditedInvestment($investment);
        } catch (ValidationException $exception) {
            Inertia::flash('toast', [
                'type' => 'error',
                'message' => collect($exception->errors())->flatten()->first()
                    ?? 'No se pudo finalizar la inversión.',
            ]);

            return redirect()->route('admin.investments.index', $request->only(['q', 'tab', 'page']));
        }

        Inertia::flash('toast', [
            'type' => 'success',
            'message' => 'Inversión marcada como finalizada.',
        ]);

        return redirect()->route('admin.investments.index', $request->only(['q', 'tab', 'page']));
    }

    /**
     * Referidos pendientes de acreditar para una inversión (recálculo manual).
     */
    public function pendingReferrals(Investment $investment): JsonResponse
    {
        if (InvestmentLedger::isAdminAdjustmentInvestment($investment)) {
            abort(404);
        }

        $investment->load(['user:id,first_name,last_name,email']);

        return response()->json([
            'investment' => [
                'id' => $investment->id,
                'txid' => $investment->txid,
                'amount_usd' => number_format((float) $investment->amount, 2, '.', ''),
                'investor_name' => $investment->user !== null
                    ? trim("{$investment->user->first_name} {$investment->user->last_name}")
                    : '—',
                'investor_email' => $investment->user?->email,
            ],
            'pending' => collect(InvestmentLedger::pendingInvestmentReferralBonuses($investment))
                ->map(static fn (array $row): array => [
                    ...$row,
                    'target_amount_usd' => number_format($row['target_amount_usd'], 2, '.', ''),
                    'already_paid_usd' => number_format($row['already_paid_usd'], 2, '.', ''),
                    'pending_amount_usd' => number_format($row['pending_amount_usd'], 2, '.', ''),
                    'percent' => number_format($row['percent'], 4, '.', ''),
                ])
                ->values()
                ->all(),
        ]);
    }

    /**
     * Acredita comisiones de referido pendientes seleccionadas.
     */
    public function applyReferrals(Request $request, Investment $investment): RedirectResponse
    {
        if (InvestmentLedger::isAdminAdjustmentInvestment($investment)) {
            abort(404);
        }

        $validated = $request->validate([
            'levels' => ['required', 'array', 'min:1'],
            'levels.*' => ['integer', 'in:1,2'],
        ]);

        $credited = InvestmentLedger::applyInvestmentReferralBonuses(
            $investment,
            array_map('intval', $validated['levels']),
        );

        Inertia::flash('toast', [
            'type' => $credited > 0 ? 'success' : 'error',
            'message' => $credited > 0
                ? "Se acreditaron {$credited} comisión(es) de referido."
                : 'No había comisiones pendientes para acreditar.',
        ]);

        return redirect()->route('admin.investments.index', $request->only(['q', 'tab', 'page']));
    }
}
