<?php

namespace App\Livewire;

use App\Services\FinanceRepository;
use App\Services\NotificationService;
use App\Services\PreferencesStore;
use App\Support\DebtDueDate;
use App\Support\DebtInference;
use App\Support\DescriptionAutocomplete;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Url;
use Livewire\Component;

#[Layout('layouts.app')]
class DebtsPage extends Component
{
    /** @var list<array<string,mixed>>|null */
    private ?array $debtHistoryCache = null;

    /** @var list<string>|null */
    private ?array $debtNameHistoryCache = null;

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

    #[Url(as: 'search')]
    public ?string $openSheet = null;

    public string $search = '';

    public bool $mobileSearchOpen = false;

    public string $strategy = 'upcoming';

    /** null | 'new' | id */
    public ?string $editing = null;

    public string $editName = '';

    public ?float $editBalance = null;

    public ?float $editOriginalBalance = null;

    public string $editRate = '';

    public ?float $editMinimum = null;

    public string $editDueDay = '1';

    public string $editType = 'credit_card';

    public string $editIcon = '💳';

    /** @var list<array{id: string, label: string, detail?: string, tone?: string}> */
    public array $smartSuggestions = [];

    /** @var list<array{value: string, hint?: string}> */
    public array $descriptionHints = [];

    public bool $manualType = false;

    public bool $manualRate = false;

    public bool $manualDueDay = false;

    public function mount(): void
    {
        if ($this->create === 'debt') {
            $this->editing = 'new';
            $this->create = null;
            $this->bootstrapForm(null);
        }
        if ($this->openSheet === '1') {
            $this->mobileSearchOpen = true;
            $this->openSheet = null;
        }
    }

    public function openNew(): void
    {
        $this->editing = 'new';
        $this->bootstrapForm(null);
    }

    public function openEdit(string $id, FinanceRepository $finance): void
    {
        $row = collect($finance->debts())->firstWhere('id', $id);
        if (! $row) {
            return;
        }
        $this->editing = $id;
        $this->bootstrapForm($row);
    }

    public function closeModal(): void
    {
        $this->editing = null;
    }

    public function closeMobileSearch(): void
    {
        $this->mobileSearchOpen = false;
    }

    /**
     * @param  array<string, mixed>|null  $debt
     */
    protected function bootstrapForm(?array $debt): void
    {
        $this->manualType = false;
        $this->manualRate = false;
        $this->manualDueDay = false;

        if ($debt === null) {
            $this->editName = '';
            $this->editBalance = null;
            $this->editOriginalBalance = null;
            $this->editRate = '';
            $this->editMinimum = null;
            $this->editDueDay = '1';
            $this->editType = 'credit_card';
            $this->editIcon = '💳';
        } else {
            $this->editName = (string) ($debt['name'] ?? '');
            $this->editBalance = isset($debt['balance']) ? (float) $debt['balance'] : null;
            $this->editOriginalBalance = isset($debt['originalBalance']) ? (float) $debt['originalBalance'] : null;
            $this->editRate = isset($debt['interestRate']) ? (string) $debt['interestRate'] : '';
            $this->editMinimum = isset($debt['minimumPayment']) ? (float) $debt['minimumPayment'] : null;
            $this->editDueDay = (string) ($debt['dueDay'] ?? '1');
            $this->editType = (string) ($debt['type'] ?? 'credit_card');
            $this->editIcon = (string) ($debt['icon'] ?? '💳');
        }
        $this->smartSuggestions = [];
        $this->descriptionHints = [];
    }

    protected function syncInference(): void
    {
        $inf = DebtInference::infer($this->editName, $this->editRate, $this->debtHistory());
        $this->smartSuggestions = $inf['suggestions'] ?? [];
        if (! empty($inf['icon'])) {
            $this->editIcon = $inf['icon'];
        }
        if (! empty($inf['type']) && ! $this->manualType) {
            $this->editType = $inf['type'];
        }
        if (isset($inf['interestRate']) && is_numeric($inf['interestRate']) && ! $this->manualRate && $this->editRate === '') {
            $this->editRate = (string) $inf['interestRate'];
        }
        if (isset($inf['dueDay']) && ! $this->manualDueDay) {
            $this->editDueDay = (string) $inf['dueDay'];
        }
        $this->descriptionHints = DescriptionAutocomplete::suggest(
            $this->editName,
            DescriptionAutocomplete::CONTEXT_DEBT,
            $this->debtNameHistory(),
            12,
        );
    }

    /**
     * @return list<string>
     */
    protected function debtNameHistory(): array
    {
        if ($this->debtNameHistoryCache !== null) {
            return $this->debtNameHistoryCache;
        }

        $this->debtNameHistoryCache = collect($this->debtHistory())
            ->pluck('name')
            ->map(fn ($n) => trim((string) $n))
            ->filter()
            ->unique(fn (string $n) => mb_strtolower($n))
            ->values()
            ->all();

        return $this->debtNameHistoryCache;
    }

    public function pickDescriptionHint(int $index): void
    {
        $row = $this->descriptionHints[$index] ?? null;
        if (! is_array($row) || ($row['value'] ?? '') === '') {
            return;
        }
        $this->editName = (string) $row['value'];
        $this->syncInference();
    }

    public function updatedEditName(): void
    {
        $this->syncInference();
    }

    public function updatedEditRate(): void
    {
        $this->manualRate = true;
        $this->syncInference();
    }

    public function setEditType(string $t): void
    {
        $this->manualType = true;
        $this->editType = $t;
    }

    public function setEditIcon(string $ic): void
    {
        $this->editIcon = $ic;
    }

    public function updatedEditDueDay(): void
    {
        $this->manualDueDay = true;
    }

    public function saveDebt(FinanceRepository $finance, NotificationService $notifications): void
    {
        if ($this->editing === null) {
            return;
        }
        $rows = collect($finance->debts())->keyBy('id');
        $id = $this->editing === 'new'
            ? 'd'.(string) (int) round(microtime(true) * 1000)
            : $this->editing;
        $isNew = $this->editing === 'new';

        $bal = (float) ($this->editBalance ?? 0);
        $orig = (float) ($this->editOriginalBalance ?? $bal);

        $row = [
            'id' => $id,
            'name' => $this->editName,
            'balance' => $bal,
            'originalBalance' => $orig,
            'interestRate' => (float) ($this->editRate !== '' ? $this->editRate : 0),
            'minimumPayment' => (float) ($this->editMinimum ?? 0),
            'dueDay' => max(1, min(31, (int) $this->editDueDay)),
            'icon' => $this->editIcon,
            'type' => $this->editType,
        ];
        $rows->put($id, $row);
        $finance->setDebts($rows->values()->all());
        $this->invalidateInferenceCaches();
        $user = auth()->user();
        if ($user !== null) {
            $currency = (string) ((PreferencesStore::all()['regional']['currency'] ?? 'EUR'));
            $dueDay = (int) $row['dueDay'];
            $payload = [
                'title' => $isNew
                    ? __('notifications.types.debt_created_title')
                    : __('notifications.types.debt_updated_title'),
                'message' => $isNew
                    ? __('notifications.types.debt_created_message', [
                        'name' => $this->editName,
                        'balance' => number_format($bal, 2, '.', ','),
                        'currency' => $currency,
                        'day' => $dueDay,
                    ])
                    : __('notifications.types.debt_updated_message', [
                        'name' => $this->editName,
                        'balance' => number_format($bal, 2, '.', ','),
                        'currency' => $currency,
                        'day' => $dueDay,
                    ]),
                'entity_type' => 'debt',
                'entity_id' => $id,
                'dedupe_key' => $isNew
                    ? 'debt:created:'.$id
                    : 'debt:updated:'.$id.':'.now()->format('Y-m-d-H'),
            ];

            $notifications->emit($user, $isNew ? 'debt.created' : 'debt.updated', $payload);
        }
        $this->closeModal();
    }

    public function deleteDebt(NotificationService $notifications): void
    {
        if ($this->editing === null || $this->editing === 'new') {
            return;
        }
        $finance = app(FinanceRepository::class);
        $existing = collect($finance->debts())->firstWhere('id', $this->editing);
        $name = is_array($existing) ? (string) ($existing['name'] ?? '') : '';
        $deleted = $finance->deleteDebt($this->editing);
        if (! $deleted) {
            $msg = (string) __('budgets.debts_page.toast_delete_failed');
            session()->flash('toast', $msg);
            $this->dispatch('app-toast', message: $msg);

            return;
        }
        $user = auth()->user();
        if ($user !== null && $name !== '') {
            $notifications->emit($user, 'debt.deleted', [
                'title' => __('notifications.types.debt_deleted_title'),
                'message' => __('notifications.types.debt_deleted_message', ['name' => $name]),
                'entity_type' => 'debt',
                'entity_id' => (string) $this->editing,
                'dedupe_key' => 'debt:deleted:'.(string) $this->editing,
            ]);
        }
        $this->invalidateInferenceCaches();
        $this->closeModal();
        $msg = (string) __('budgets.debts_page.toast_deleted');
        session()->flash('toast', $msg);
        $this->dispatch('app-toast', message: $msg);
    }

    public function render(FinanceRepository $finance)
    {
        $debts = $finance->debts();
        $q = mb_strtolower(trim($this->search));
        $visible = $q === ''
            ? $debts
            : array_values(array_filter($debts, fn ($d) => str_contains(mb_strtolower((string) ($d['name'] ?? '')), $q)));

        $totalDebt = (float) collect($debts)->sum(fn ($d) => (float) ($d['balance'] ?? 0));
        $totalOriginal = (float) collect($debts)->sum(fn ($d) => (float) ($d['originalBalance'] ?? 0));
        $totalMin = (float) collect($debts)->sum(fn ($d) => (float) ($d['minimumPayment'] ?? 0));
        $paidPct = $totalOriginal > 0 ? (1 - $totalDebt / $totalOriginal) * 100 : 0;

        $sorted = $visible;
        $strategy = $this->strategy;
        usort($sorted, static function ($a, $b) use ($strategy) {
            if ($strategy === 'upcoming') {
                return DebtDueDate::nextOccurrenceTimestamp($a) <=> DebtDueDate::nextOccurrenceTimestamp($b);
            }
            if ($strategy === 'avalanche') {
                return ((float) ($b['interestRate'] ?? 0)) <=> ((float) ($a['interestRate'] ?? 0));
            }

            return ((float) ($a['balance'] ?? 0)) <=> ((float) ($b['balance'] ?? 0));
        });
        $focusDebt = $sorted[0] ?? null;

        return view('livewire.debts-page', [
            'visibleDebts' => $visible,
            'sortedDebts' => $sorted,
            'totalDebt' => $totalDebt,
            'totalOriginal' => $totalOriginal,
            'totalMin' => $totalMin,
            'paidPct' => $paidPct,
            'focusDebt' => $focusDebt,
        ])->title((string) __('budgets.debts_page.title'));
    }

    /** @return list<array<string,mixed>> */
    protected function debtHistory(): array
    {
        if ($this->debtHistoryCache !== null) {
            return $this->debtHistoryCache;
        }

        $this->debtHistoryCache = app(FinanceRepository::class)->debts();

        return $this->debtHistoryCache;
    }

    protected function invalidateInferenceCaches(): void
    {
        $this->debtHistoryCache = null;
        $this->debtNameHistoryCache = null;
    }
}
