<?php

namespace App\Services;

use App\Models\UserPreference;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;

class PreferencesStore
{
    public const SESSION_KEY = 'budgets.app.preferences';

    public const LANDING_SEEN_KEY = 'budgets_landing_seen';

    private static ?array $memoizedPreferences = null;

    private static ?int $memoizedUserId = null;

    private const CACHE_SECONDS = 120;

    private static function cacheKey(int $userId): string
    {
        return "prefs:user:{$userId}:v1";
    }

    /** @var list<string> */
    /** Matches DEFAULT_WIDGET_ORDER in bugets-smart-money/src/hooks/use-app-preferences.ts */
    public const DEFAULT_WIDGET_ORDER = [
        'balance',
        'income-expense',
        'upcoming',
        'subscriptions',
        'budgets',
        'recent-transactions',
        'goals',
        'debts',
        'ai',
        'safe-to-spend',
    ];

    public static function forgetMemo(): void
    {
        self::$memoizedPreferences = null;
        self::$memoizedUserId = null;
    }

    public static function defaults(): array
    {
        return [
            'profile' => [
                'firstName' => '',
                'lastName' => '',
                'displayName' => '',
                'email' => '',
                'phone' => '',
                'location' => '',
                'jobTitle' => '',
                'bio' => '',
                'timezone' => 'Europe/Lisbon',
                'savingsGoal' => 0,
                'monthlyIncome' => 0,
                'monthlyLimit' => 0,
            ],
            'notifications' => [
                'budgetAlerts' => true,
                'billReminders' => true,
                'weeklyDigest' => true,
                'monthlyReport' => true,
                'productUpdates' => false,
                'securityAlerts' => true,
                'emailNotifications' => true,
                'pushNotifications' => true,
                'pushPromptState' => 'pending',
                'pushPromptLastShownAt' => null,
                'digestFrequency' => 'weekly',
                'reminderLeadDays' => 3,
            ],
            'regional' => [
                'currency' => 'EUR',
                'region' => 'Europe/Lisbon',
                'dateFormat' => 'DD/MM/YYYY',
                'firstDayOfWeek' => 'monday',
            ],
            'appearance' => [
                'theme' => 'light',
                'compactMode' => false,
                'reducedMotion' => false,
                'dashboardDensity' => 'comfortable',
                'accent' => 'emerald',
            ],
            'privacy' => [
                'twoFactorEnabled' => true,
                'biometricUnlock' => false,
                'shareUsageData' => false,
                'privateMode' => true,
                'trustedDevicesOnly' => true,
                'sessionTimeoutMinutes' => 30,
            ],
            'exports' => [
                'includeCharts' => true,
                'includeNotes' => true,
                'defaultFormat' => 'pdf',
                'lastExportAt' => null,
            ],
            'dashboard' => [
                'order' => self::DEFAULT_WIDGET_ORDER,
                'hidden' => ['safe-to-spend'],
                'sizes' => [],
            ],
            'onboarding' => [
                'completed' => false,
                'invitedBy' => null,
                'completedAt' => null,
                'selectedGoals' => [],
            ],
            'importedTransactions' => [],
        ];
    }

    public static function all(): array
    {
        $currentUserId = Auth::id();

        if (self::$memoizedPreferences !== null && self::$memoizedUserId === $currentUserId) {
            return self::$memoizedPreferences;
        }

        if (Auth::check()) {
            $uid = (int) Auth::id();
            try {
                $stored = Cache::remember(
                    self::cacheKey($uid),
                    now()->addSeconds(self::CACHE_SECONDS),
                    static fn () => UserPreference::query()->where('user_id', $uid)->value('data')
                );
            } catch (\Throwable) {
                $stored = UserPreference::query()->where('user_id', $uid)->value('data');
            }

            self::$memoizedUserId = $currentUserId;
            self::$memoizedPreferences = self::hydrateProfileFromAuth(
                array_replace_recursive(self::defaults(), is_array($stored) ? $stored : [])
            );

            return self::$memoizedPreferences;
        }

        $stored = Session::get(self::SESSION_KEY, []);

        self::$memoizedUserId = null;
        self::$memoizedPreferences = array_replace_recursive(self::defaults(), is_array($stored) ? $stored : []);

        return self::$memoizedPreferences;
    }

    protected static function hydrateProfileFromAuth(array $prefs): array
    {
        $user = Auth::user();
        if ($user === null) {
            return $prefs;
        }

        $profile = is_array($prefs['profile'] ?? null) ? $prefs['profile'] : [];
        $name = trim((string) ($user->name ?? ''));
        $parts = preg_split('/\s+/', $name) ?: [];
        $firstName = trim((string) ($parts[0] ?? ''));
        $lastName = count($parts) > 1 ? trim((string) implode(' ', array_slice($parts, 1))) : '';

        $profile['firstName'] = $profile['firstName'] !== '' ? $profile['firstName'] : $firstName;
        $profile['lastName'] = $profile['lastName'] !== '' ? $profile['lastName'] : $lastName;
        $profile['displayName'] = $profile['displayName'] !== '' ? $profile['displayName'] : $name;
        $profile['email'] = $profile['email'] !== '' ? $profile['email'] : (string) ($user->email ?? '');

        $prefs['profile'] = $profile;

        return $prefs;
    }

    public static function save(array $prefs): void
    {
        self::$memoizedUserId = Auth::id();
        self::$memoizedPreferences = $prefs;

        if (Auth::check()) {
            try {
                Cache::forget(self::cacheKey((int) Auth::id()));
            } catch (\Throwable) {
                // ignore cache backend issues
            }
            UserPreference::query()->updateOrCreate(
                ['user_id' => Auth::id()],
                ['data' => $prefs]
            );

            return;
        }

        Session::put(self::SESSION_KEY, $prefs);
    }

    /** @param  callable(array):array  $fn */
    public static function update(callable $fn): void
    {
        self::save($fn(self::all()));
    }

    public static function landingSeen(): bool
    {
        return (bool) Session::get(self::LANDING_SEEN_KEY, false);
    }

    public static function markLandingSeen(): void
    {
        Session::put(self::LANDING_SEEN_KEY, true);
    }

    public static function reset(): void
    {
        self::$memoizedUserId = Auth::id();
        self::$memoizedPreferences = self::defaults();

        if (Auth::check()) {
            try {
                Cache::forget(self::cacheKey((int) Auth::id()));
            } catch (\Throwable) {
                // ignore cache backend issues
            }
            UserPreference::query()->where('user_id', Auth::id())->delete();

            return;
        }

        Session::forget(self::SESSION_KEY);
    }
}
