<?php

declare(strict_types=1);

namespace App\View\Composers;

use App\Events\UserLoggedIn;
use App\Models\Category;
use App\Models\Content;
use App\Models\Settings;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;

class GlobalDataComposer
{
    /**
     * Cache TTL in seconds (5 minutes).
     */
    private const CACHE_TTL = 300;

    /**
     * Resolved data for this request, so we only build it once
     * even when the composer matches multiple views (layout + child).
     *
     * @var array<string, mixed>|null
     */
    private static ?array $resolvedData = null;

    /**
     * Bind data to the view.
     */
    public function compose(View $view): void
    {
        if (self::$resolvedData === null) {
            self::$resolvedData = $this->resolveData();
        }

        $view->with(self::$resolvedData);
    }

    /**
     * Build the global view data (called once per request).
     *
     * @return array<string, mixed>
     */
    private function resolveData(): array
    {
        // Cached site settings (shared across all requests)
        $siteArray = $this->rememberWithCacheFallback('site_settings_array', self::CACHE_TTL, function () {
            return Settings::query()
                ->pluck('value', 'name')
                ->map(fn ($value) => Settings::convertValue($value))
                ->all();
        });

        $viewData = [
            'serverroot' => url('/'),
            'site' => $siteArray,
        ];

        // Cached useful links for sidebar
        $viewData['usefulLinks'] = $this->rememberWithCacheFallback('content_useful_links', self::CACHE_TTL, function () {
            return Content::active()
                ->ofType(Content::TYPE_USEFUL)
                ->ordered()
                ->get();
        });

        if (Auth::check()) {
            $userId = Auth::id();

            // User data with category exclusions (short cache per user)
            $userdata = $this->rememberWithCacheFallback('composer_user_'.$userId, self::CACHE_TTL, function () use ($userId) {
                $user = User::find($userId);
                $user->categoryexclusions = User::getCachedCategoryExclusionById($userId);

                return $user;
            });

            // Update last login every 3 hours (outside of cache to avoid stale checks)
            if (now()->subHours(3) > $userdata->lastlogin) {
                event(new UserLoggedIn($userdata));
            }

            // Cached menu categories per user (depends on their exclusions)
            $parentcatlist = $this->rememberWithCacheFallback('menu_user_'.$userId, self::CACHE_TTL, function () use ($userdata) {
                return Category::getForMenu($userdata->categoryexclusions);
            });

            $viewData = array_merge($viewData, [
                'userdata' => $userdata,
                'loggedin' => true,
                'isadmin' => $userdata->hasRole('Admin'),
                'ismod' => $userdata->hasRole('Moderator'),
                'parentcatlist' => $parentcatlist,
                'header_menu_cat' => request()->input('t', ''),
                'userTheme' => $userdata->theme_preference ?? 'light',
                'userColorScheme' => $userdata->color_scheme ?? 'blue',
            ]);
        } else {
            $viewData = array_merge($viewData, [
                'isadmin' => false,
                'ismod' => false,
                'loggedin' => false,
                'userTheme' => 'light',
                'userColorScheme' => 'blue',
            ]);
        }

        return $viewData;
    }

    /**
     * @param  callable(): mixed  $callback
     */
    private function rememberWithCacheFallback(string $key, int|\DateInterval $ttl, callable $callback): mixed
    {
        try {
            return Cache::remember($key, $ttl, $callback);
        } catch (\Throwable $e) {
            if (config('app.debug')) {
                Log::debug('GlobalDataComposer cache bypassed: '.$e->getMessage());
            }

            return $callback();
        }
    }
}
