<?php

namespace App\Livewire;

use App\Services\FinanceRepository;
use App\Services\NotificationService;
use App\Services\PreferencesStore;
use App\Support\DescriptionAutocomplete;
use App\Support\FinanceListOrdering;
use App\Support\GoalInference;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Url;
use Livewire\Component;

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

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

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

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

    public string $goalSearch = '';

    public bool $mobileSearchOpen = false;

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

    public string $editName = '';

    public ?float $editTarget = null;

    public ?float $editSaved = null;

    public string $editDeadline = '';

    public ?float $editMonthly = null;

    public string $editPriority = 'medium';

    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 $manualPriority = false;

    public bool $manualMonthly = false;

    public function mount(): void
    {
        if ($this->create === 'goal') {
            $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
    {
        $goal = collect($finance->goals())->firstWhere('id', $id);
        if (! $goal) {
            return;
        }
        $this->editing = $id;
        $this->bootstrapForm($goal);
    }

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

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

    public function setEditPriority(string $p): void
    {
        $this->manualPriority = true;
        $this->editPriority = $p;
    }

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

    public function updatedEditMonthly(): void
    {
        $this->manualMonthly = true;
    }

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

    public function updatedEditTarget(): void
    {
        $this->syncIfEditing();
    }

    public function updatedEditSaved(): void
    {
        $this->syncIfEditing();
    }

    public function updatedEditDeadline(): void
    {
        $this->syncIfEditing();
    }

    protected function syncIfEditing(): void
    {
        if ($this->editing !== null) {
            $this->syncGoalInference();
        }
    }

    /**
     * @param  array<string, mixed>|null  $goal
     */
    protected function bootstrapForm(?array $goal): void
    {
        if ($goal === null) {
            $this->editName = '';
            $this->editTarget = null;
            $this->editSaved = null;
            $this->editDeadline = '';
            $this->editMonthly = 100;
            $this->editPriority = 'medium';
            $this->editIcon = '🎯';
            $this->manualPriority = false;
            $this->manualMonthly = false;
        } else {
            $this->editName = (string) ($goal['name'] ?? '');
            $this->editTarget = isset($goal['target']) ? (float) $goal['target'] : null;
            $this->editSaved = isset($goal['saved']) ? (float) $goal['saved'] : 0.0;
            $this->editDeadline = (string) ($goal['deadline'] ?? '');
            $this->editMonthly = isset($goal['monthlyContribution']) ? (float) $goal['monthlyContribution'] : 100.0;
            $this->editPriority = (string) ($goal['priority'] ?? 'medium');
            $this->editIcon = (string) ($goal['icon'] ?? '🎯');
            $this->manualPriority = false;
            $this->manualMonthly = false;
        }
        $this->smartSuggestions = [];
        $this->descriptionHints = [];
    }

    protected function syncGoalInference(): void
    {
        $inferred = GoalInference::infer(
            $this->editName,
            (string) ($this->editTarget ?? ''),
            $this->editDeadline,
            (string) ($this->editSaved ?? ''),
            $this->goalHistory(),
        );
        $this->smartSuggestions = $inferred['suggestions'] ?? [];

        if (! empty($inferred['icon'])) {
            $this->editIcon = $inferred['icon'];
        }
        if (! empty($inferred['priority']) && ! $this->manualPriority) {
            $this->editPriority = $inferred['priority'];
        }
        if (isset($inferred['monthlyContribution']) && is_numeric($inferred['monthlyContribution']) && ! $this->manualMonthly) {
            $this->editMonthly = (float) $inferred['monthlyContribution'];
        }
        $this->descriptionHints = DescriptionAutocomplete::suggest(
            $this->editName,
            DescriptionAutocomplete::CONTEXT_GOAL,
            $this->goalNameHistory(),
            12,
        );
    }

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

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

        return $this->goalNameHistoryCache;
    }

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

    public function saveGoal(FinanceRepository $finance, NotificationService $notifications): void
    {
        if ($this->editing === null) {
            return;
        }

        $rows = collect($finance->goals())->keyBy('id');
        $isNew = $this->editing === 'new';
        $existing = $isNew ? null : $rows->get($this->editing);
        $id = $this->editing === 'new'
            ? 'g'.(string) (int) round(microtime(true) * 1000)
            : $this->editing;

        $beforePct = 0.0;
        $beforeDeadline = '';
        if (is_array($existing)) {
            $t = (float) ($existing['target'] ?? 0);
            $s = (float) ($existing['saved'] ?? 0);
            $beforePct = $t > 0 ? ($s / $t) * 100.0 : 0.0;
            $beforeDeadline = (string) ($existing['deadline'] ?? '');
        }

        $row = [
            'id' => $id,
            'name' => $this->editName,
            'target' => (float) ($this->editTarget ?? 0),
            'saved' => (float) ($this->editSaved ?? 0),
            'icon' => $this->editIcon,
            'deadline' => $this->editDeadline,
            'monthlyContribution' => (float) ($this->editMonthly ?? 0),
            'priority' => $this->editPriority,
        ];

        $rows->put($id, $row);
        $finance->setGoals($rows->values()->all());
        $this->invalidateInferenceCaches();

        $user = auth()->user();
        if ($user !== null) {
            $currency = (string) ((PreferencesStore::all()['regional']['currency'] ?? 'EUR'));
            $target = (float) ($this->editTarget ?? 0);
            $saved = (float) ($this->editSaved ?? 0);
            $afterPct = $target > 0 ? ($saved / $target) * 100.0 : 0.0;
            $wasComplete = is_array($existing) && $target > 0 && (float) ($existing['saved'] ?? 0) >= (float) ($existing['target'] ?? 0);
            $nowComplete = $target > 0 && $saved >= $target;

            if ($isNew) {
                $notifications->emit($user, 'goal.created', [
                    'title' => __('notifications.types.goal_created_title'),
                    'message' => __('notifications.types.goal_created_message', ['name' => $this->editName]),
                    'entity_type' => 'goal',
                    'entity_id' => $id,
                    'dedupe_key' => 'goal:created:'.$id,
                ]);
            } else {
                $deadlineChanged = is_array($existing)
                    && $beforeDeadline !== (string) $this->editDeadline;

                $changed = ! is_array($existing)
                    || (string) ($existing['name'] ?? '') !== $this->editName
                    || (float) ($existing['target'] ?? 0) !== $target
                    || (float) ($existing['saved'] ?? 0) !== $saved
                    || (float) ($existing['monthlyContribution'] ?? 0) !== (float) ($this->editMonthly ?? 0)
                    || (string) ($existing['priority'] ?? '') !== (string) $this->editPriority
                    || (string) ($existing['icon'] ?? '') !== (string) $this->editIcon;

                if ($deadlineChanged) {
                    $notifications->emit($user, 'goal.deadline_updated', [
                        'title' => __('notifications.types.goal_deadline_updated_title'),
                        'message' => __('notifications.types.goal_deadline_updated_message', [
                            'name' => $this->editName,
                            'date' => (string) $this->editDeadline,
                        ]),
                        'entity_type' => 'goal',
                        'entity_id' => $id,
                        'dedupe_key' => 'goal:deadline_change:'.$id.':'.(string) $this->editDeadline,
                    ]);
                } elseif ($changed) {
                    $notifications->emit($user, 'goal.updated', [
                        'title' => __('notifications.types.goal_updated_title'),
                        'message' => __('notifications.types.goal_updated_message', ['name' => $this->editName]),
                        'entity_type' => 'goal',
                        'entity_id' => $id,
                        'dedupe_key' => 'goal:updated:'.$id.':'.now()->format('Y-m-d-H'),
                    ]);
                }
            }

            if (! $wasComplete && $nowComplete) {
                $notifications->emit($user, 'goal.completed', [
                    'title' => __('notifications.types.goal_completed_title'),
                    'message' => __('notifications.types.goal_completed_message', ['name' => $this->editName]),
                    'entity_type' => 'goal',
                    'entity_id' => $id,
                    'dedupe_key' => 'goal:completed:'.$id,
                ]);
            } elseif (! $isNew && $target > 0 && ! $nowComplete) {
                foreach ([25, 50, 75] as $milestone) {
                    if ($beforePct < $milestone && $afterPct >= $milestone) {
                        $notifications->emit($user, 'goal.milestone', [
                            'title' => __('notifications.types.goal_milestone_title'),
                            'message' => __('notifications.types.goal_milestone_message', [
                                'name' => $this->editName,
                                'pct' => $milestone,
                                'saved' => number_format($saved, 2, '.', ','),
                                'target' => number_format($target, 2, '.', ','),
                                'currency' => $currency,
                            ]),
                            'entity_type' => 'goal',
                            'entity_id' => $id,
                            'dedupe_key' => 'goal:milestone:'.$id.':'.$milestone.':'.(string) round($target),
                        ]);
                    }
                }
            }
        }

        $this->closeModal();
    }

    public function deleteGoal(NotificationService $notifications): void
    {
        if ($this->editing === null || $this->editing === 'new') {
            return;
        }
        $finance = app(FinanceRepository::class);
        $existing = collect($finance->goals())->firstWhere('id', $this->editing);
        $name = is_array($existing) ? (string) ($existing['name'] ?? '') : '';
        $deleted = $finance->deleteGoal($this->editing);
        if (! $deleted) {
            $msg = (string) __('budgets.goals_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, 'goal.deleted', [
                'title' => __('notifications.types.goal_deleted_title'),
                'message' => __('notifications.types.goal_deleted_message', ['name' => $name]),
                'entity_type' => 'goal',
                'entity_id' => (string) $this->editing,
                'dedupe_key' => 'goal:deleted:'.(string) $this->editing,
            ]);
        }
        $this->invalidateInferenceCaches();
        $this->closeModal();
        $msg = (string) __('budgets.goals_page.toast_deleted');
        session()->flash('toast', $msg);
        $this->dispatch('app-toast', message: $msg);
    }

    public function render(FinanceRepository $finance)
    {
        $goals = $finance->goals();
        $q = mb_strtolower(trim($this->goalSearch));
        $listGoals = $q === ''
            ? $goals
            : array_values(array_filter($goals, fn ($g) => str_contains(mb_strtolower((string) ($g['name'] ?? '')), $q)));
        $listGoals = FinanceListOrdering::goalsByDeadline($listGoals);

        $totalSaved = (float) collect($goals)->sum(fn ($g) => (float) ($g['saved'] ?? 0));
        $totalTarget = (float) collect($goals)->sum(fn ($g) => (float) ($g['target'] ?? 0));
        $progressPct = $totalTarget > 0 ? (int) round(($totalSaved / $totalTarget) * 100) : 0;
        $completedGoals = (int) collect($goals)->filter(fn ($g) => (float) ($g['target'] ?? 0) > 0 && (float) ($g['saved'] ?? 0) >= (float) ($g['target'] ?? 0))->count();
        $onTrackGoals = (int) collect($goals)->filter(function ($g) {
            $target = (float) ($g['target'] ?? 0);
            if ($target <= 0) {
                return false;
            }
            $saved = (float) ($g['saved'] ?? 0);

            return ($saved / $target) >= 0.5;
        })->count();
        $needsAttentionGoals = max(0, count($goals) - $onTrackGoals);

        return view('livewire.goals-page', [
            'listGoals' => $listGoals,
            'allGoalsCount' => count($goals),
            'totalSaved' => $totalSaved,
            'totalTarget' => $totalTarget,
            'progressPct' => $progressPct,
            'completedGoals' => $completedGoals,
            'onTrackGoals' => $onTrackGoals,
            'needsAttentionGoals' => $needsAttentionGoals,
        ])->title((string) __('budgets.goals_page.title'));
    }

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

        $this->goalHistoryCache = app(FinanceRepository::class)->goals();

        return $this->goalHistoryCache;
    }

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