<?php

namespace App\Livewire;

use App\Services\FinanceRepository;
use App\Services\PreferencesStore;
use Carbon\CarbonImmutable;
use Illuminate\Support\Str;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Url;
use Livewire\Component;

#[Layout('layouts.marketing')]
class OnboardingPage extends Component
{
    public const TOTAL_STEPS = 6;

    #[Url]
    public ?string $invitedBy = null;

    public int $step = 0;

    public string $income = '1200';

    public int $incomePayday = 5;

    /** @var list<array{key: string, name: string, amount: float, on: bool, icon: string}> */
    public array $bills = [];

    public string $budgetCategory = 'food';

    public string $budgetLimit = '400';

    /** @var list<array{key: string, name: string, balance: float, minimumPayment: float, dueDay: int, type: string, icon: string}> */
    public array $debts = [];

    public function mount(): void
    {
        $prefs = PreferencesStore::all();
        if ($prefs['onboarding']['completed'] ?? false) {
            $this->redirect(route('dashboard'), navigate: true);
        }

        $profile = is_array($prefs['profile'] ?? null) ? $prefs['profile'] : [];
        $income = (float) ($profile['monthlyIncome'] ?? 0);
        if ($income > 0) {
            $this->income = (string) ((int) round($income));
        }
        $this->incomePayday = max(1, min(28, (int) ($prefs['onboarding']['incomePayday'] ?? 5)));

        $this->bills = collect(__('onboarding.bills_presets'))->values()->map(
            fn (array $b, int $index) => [
                'key' => 'preset-'.Str::slug((string) ($b['name'] ?? 'bill')).'-'.$index,
                'name' => $b['name'],
                'amount' => (float) $b['amount'],
                'on' => false,
                'icon' => (string) ($b['icon'] ?? 'receipt'),
            ]
        )->all();

        $this->debts = [[
            'key' => (string) Str::uuid(),
            'name' => '',
            'balance' => 0.0,
            'minimumPayment' => 0.0,
            'dueDay' => 5,
            'type' => 'loan',
            'icon' => 'credit-card',
        ]];
    }

    public function getIsInvitedProperty(): bool
    {
        return filled($this->invitedBy);
    }

    public function getProgressProperty(): int
    {
        return (int) round((($this->step + 1) / self::TOTAL_STEPS) * 100);
    }

    public function getCanAdvanceProperty(): bool
    {
        return match ($this->step) {
            0 => true,
            1 => (float) $this->income > 0,
            default => true,
        };
    }

    public function getSelectedBillsTotalProperty(): float
    {
        return (float) collect($this->bills)->where('on', true)->sum('amount');
    }

    public function getSelectedBillsCountProperty(): int
    {
        return (int) collect($this->bills)->where('on', true)->count();
    }

    public function getDebtEntriesProperty(): array
    {
        return collect($this->debts)
            ->filter(function (array $debt): bool {
                $name = trim((string) ($debt['name'] ?? ''));
                $balance = (float) ($debt['balance'] ?? 0);

                return $name !== '' || $balance > 0;
            })
            ->values()
            ->all();
    }

    public function getDebtCountProperty(): int
    {
        return count($this->debtEntries);
    }

    public function getDebtTotalBalanceProperty(): float
    {
        return (float) collect($this->debtEntries)->sum(fn (array $debt) => (float) ($debt['balance'] ?? 0));
    }

    public function getDebtMinimumTotalProperty(): float
    {
        return (float) collect($this->debtEntries)->sum(fn (array $debt) => (float) ($debt['minimumPayment'] ?? 0));
    }

    public function getMonthlyPlannedSpendProperty(): float
    {
        return $this->selectedBillsTotal + (float) $this->budgetLimit + $this->debtMinimumTotal;
    }

    public function getFreeCashProperty(): float
    {
        return (float) $this->income - $this->monthlyPlannedSpend;
    }

    public function getPlannedSpendPctProperty(): int
    {
        $income = (float) $this->income;
        if ($income <= 0) {
            return 0;
        }

        return (int) round(($this->monthlyPlannedSpend / $income) * 100);
    }

    public function toggleBill(int $index): void
    {
        if (! isset($this->bills[$index])) {
            return;
        }
        $bills = $this->bills;
        $bills[$index]['on'] = ! $bills[$index]['on'];
        $this->bills = $bills;
    }

    public function setIncomePreset(int $amount): void
    {
        $this->income = (string) $amount;
    }

    public function bumpIncome(int $delta): void
    {
        $next = ((int) round((float) $this->income)) + $delta;
        $this->income = (string) max(0, $next);
    }

    public function addDebtRow(): void
    {
        $this->debts[] = [
            'key' => (string) Str::uuid(),
            'name' => '',
            'balance' => 0.0,
            'minimumPayment' => 0.0,
            'dueDay' => 5,
            'type' => 'loan',
            'icon' => 'credit-card',
        ];
    }

    public function removeDebtRow(int $index): void
    {
        if (! isset($this->debts[$index])) {
            return;
        }

        unset($this->debts[$index]);
        $this->debts = array_values($this->debts);
    }

    public function setBudgetCategory(string $id): void
    {
        $this->budgetCategory = $id;
    }

    public function nextStep(): void
    {
        if (! $this->canAdvance) {
            return;
        }
        $this->step = min(self::TOTAL_STEPS - 1, $this->step + 1);
    }

    public function prevStep(): void
    {
        $this->step = max(0, $this->step - 1);
    }

    public function skip(): void
    {
        PreferencesStore::update(function (array $p) {
            $p['onboarding'] = array_replace_recursive($p['onboarding'] ?? [], [
                'completed' => true,
                'completedAt' => now()->toIso8601String(),
                'invitedBy' => $this->isInvited ? $this->invitedBy : null,
            ]);

            return $p;
        });

        session()->flash('toast', __('onboarding.toast_skipped'));
        $this->redirect(route('dashboard'), navigate: true);
    }

    public function finish(): void
    {
        $this->persistCompletedOnboarding();
        session()->flash('toast', __('onboarding.toast_welcome'));
        $this->redirect(route('dashboard'), navigate: true);
    }

    public function finishAndImport(): void
    {
        $this->persistCompletedOnboarding();
        session()->flash('toast', __('onboarding.toast_welcome'));
        $this->redirect(route('import'), navigate: true);
    }

    protected function persistCompletedOnboarding(): void
    {
        PreferencesStore::update(function (array $p) {
            $p['profile'] = array_replace_recursive($p['profile'] ?? [], [
                'monthlyIncome' => (float) $this->income > 0
                    ? (float) $this->income
                    : (float) ($p['profile']['monthlyIncome'] ?? 0),
            ]);

            $p['onboarding'] = array_replace_recursive($p['onboarding'] ?? [], [
                'completed' => true,
                'completedAt' => now()->toIso8601String(),
                'invitedBy' => $this->isInvited ? $this->invitedBy : null,
                'incomePayday' => $this->incomePayday,
                'selectedBills' => $this->bills,
            ]);

            return $p;
        });

        $this->persistFinanceData();
    }

    protected function persistFinanceData(): void
    {
        /** @var FinanceRepository $finance */
        $finance = app(FinanceRepository::class);

        $this->persistIncomeAndBills($finance);
        $this->persistBudget($finance);
        $this->persistDebts($finance);
    }

    protected function persistIncomeAndBills(FinanceRepository $finance): void
    {
        $this->ensureOnboardingKeys();

        $rows = collect($finance->transactions())
            ->reject(function (array $row): bool {
                $id = (string) ($row['id'] ?? '');

                return Str::startsWith($id, [
                    'onboarding-income',
                    'onboarding-tx-income',
                    'onboarding-bill-',
                    'onboarding-expense-',
                    'onboarding-tx-bill-',
                ]);
            })
            ->keyBy('id');
        $payDate = CarbonImmutable::now()->day(min(28, max(1, $this->incomePayday)));
        if ($payDate->isPast()) {
            $payDate = $payDate->addMonth();
        }

        $rows->put('onboarding-tx-income', [
            'id' => 'onboarding-tx-income',
            'name' => (string) __('onboarding.seed.monthly_income'),
            'categoryId' => 'income',
            'amount' => (float) $this->income,
            'date' => $payDate->toDateString(),
            'type' => 'income',
            'isRecurring' => true,
        ]);

        foreach ($this->bills as $index => $bill) {
            $externalId = 'onboarding-tx-bill-'.$this->sanitizeOnboardingKey((string) ($bill['key'] ?? ('idx-'.$index)));
            if (! ((bool) ($bill['on'] ?? false))) {
                $rows->forget($externalId);

                continue;
            }

            $rows->put($externalId, [
                'id' => $externalId,
                'name' => (string) ($bill['name'] ?? (string) __('onboarding.seed.recurring_bill')),
                'categoryId' => $this->resolveBillCategory($bill),
                'amount' => -abs((float) ($bill['amount'] ?? 0)),
                'date' => CarbonImmutable::now()->day(min(28, $index + 1))->toDateString(),
                'type' => 'expense',
                'isRecurring' => true,
            ]);
        }

        $finance->setTransactions($rows->values()->all());
    }

    /**
     * @param array{name?: string, icon?: string} $bill
     */
    protected function resolveBillCategory(array $bill): string
    {
        $name = mb_strtolower((string) ($bill['name'] ?? ''));
        $icon = (string) ($bill['icon'] ?? '');

        if (str_contains($name, 'netflix') || str_contains($name, 'spotify') || str_contains($name, 'disney') || str_contains($name, 'apple') || in_array($icon, ['tv', 'music-2'], true)) {
            return 'subscriptions';
        }
        if (str_contains($name, 'rent') || str_contains($name, 'mortgage') || str_contains($name, 'renda') || str_contains($name, 'habita') || $icon === 'home') {
            return 'housing';
        }
        if (str_contains($name, 'internet') || str_contains($name, 'phone') || str_contains($name, 'telem') || str_contains($name, 'electric') || str_contains($name, 'eletric') || str_contains($name, 'utility') || str_contains($name, 'seguro') || str_contains($name, 'insurance') || in_array($icon, ['wifi', 'smartphone', 'zap', 'shield'], true)) {
            return 'utilities';
        }
        if (str_contains($name, 'gym') || str_contains($name, 'ginás') || str_contains($name, 'ginas') || $icon === 'dumbbell') {
            return 'fitness';
        }

        return 'other';
    }

    protected function persistBudget(FinanceRepository $finance): void
    {
        $rows = collect($finance->budgets())->keyBy('id');
        $categoryLabel = $this->budgetCategoryLabel($this->budgetCategory);
        $rows->put('onboarding-budget-main', [
            'id' => 'onboarding-budget-main',
            'name' => (string) __('onboarding.seed.monthly_budget_name', ['category' => $categoryLabel]),
            'categoryId' => $this->budgetCategory,
            'limit' => (float) $this->budgetLimit,
            'spent' => (float) ($rows->get('onboarding-budget-main')['spent'] ?? 0),
            'period' => 'monthly',
            'icon' => '📊',
        ]);

        $finance->setBudgets($rows->values()->all());
    }

    protected function persistDebts(FinanceRepository $finance): void
    {
        $this->ensureOnboardingKeys();

        $rows = collect($finance->debts())
            ->reject(function (array $row): bool {
                $id = (string) ($row['id'] ?? '');

                return Str::startsWith($id, [
                    'onboarding-debt-',
                    'onboarding-debt-entry-',
                ]);
            })
            ->keyBy('id');
        foreach ($this->debts as $index => $debt) {
            $name = trim((string) ($debt['name'] ?? ''));
            $balance = (float) ($debt['balance'] ?? 0);
            if ($name === '' || $balance <= 0) {
                continue;
            }

            $externalId = 'onboarding-debt-entry-'.$this->sanitizeOnboardingKey((string) ($debt['key'] ?? ('idx-'.$index)));
            $minimum = (float) ($debt['minimumPayment'] ?? 0);
            $rows->put($externalId, [
                'id' => $externalId,
                'name' => $name,
                'balance' => $balance,
                'originalBalance' => $balance,
                'interestRate' => 0,
                'minimumPayment' => $minimum,
                'dueDay' => max(1, min(28, (int) ($debt['dueDay'] ?? 5))),
                'icon' => (string) ($debt['icon'] ?? '💳'),
                'type' => (string) ($debt['type'] ?? 'loan'),
            ]);
        }

        $finance->setDebts($rows->values()->all());
    }

    protected function ensureOnboardingKeys(): void
    {
        $this->bills = collect($this->bills)->values()->map(function (array $bill, int $index): array {
            $key = trim((string) ($bill['key'] ?? ''));
            if ($key === '') {
                $bill['key'] = 'bill-'.Str::uuid()->toString().'-'.$index;
            }

            return $bill;
        })->all();

        $this->debts = collect($this->debts)->values()->map(function (array $debt, int $index): array {
            $key = trim((string) ($debt['key'] ?? ''));
            if ($key === '') {
                $debt['key'] = 'debt-'.Str::uuid()->toString().'-'.$index;
            }

            return $debt;
        })->all();
    }

    protected function sanitizeOnboardingKey(string $value): string
    {
        $key = Str::slug($value);
        if ($key === '') {
            $key = (string) Str::uuid();
        }

        return Str::limit($key, 64, '');
    }

    protected function budgetCategoryLabel(string $categoryId): string
    {
        $categories = collect(__('onboarding.budget.categories'));
        $label = (string) ($categories->firstWhere('id', $categoryId)['label'] ?? '');

        return $label !== '' ? $label : Str::headline($categoryId);
    }

    public function getAdviceProperty(): array
    {
        $income = (float) $this->income;
        $bills = $this->selectedBillsTotal;
        $budget = (float) $this->budgetLimit;
        $debtPayment = collect($this->debts)->sum(fn (array $debt) => (float) ($debt['minimumPayment'] ?? 0));
        $fixedCosts = $bills + $budget + $debtPayment;
        $buffer = $income - $fixedCosts;

        return [
            __('onboarding.summary.advice_fixed_costs', ['percent' => $income > 0 ? (int) round(($fixedCosts / $income) * 100) : 0]),
            $buffer >= 0
                ? __('onboarding.summary.advice_buffer_positive', ['amount' => number_format($buffer, 0, ',', '.')])
                : __('onboarding.summary.advice_buffer_negative', ['amount' => number_format(abs($buffer), 0, ',', '.')]),
            __('onboarding.summary.advice_payday', ['day' => $this->incomePayday]),
        ];
    }

    public function render()
    {
        return view('livewire.onboarding-page')
            ->title((string) __('onboarding.document_title'));
    }
}
