<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\URL;
use Illuminate\Pagination\Paginator;
use App\Models\Community;
use App\Observers\CommunityObserver;
use App\Models\CommunityHomecontents;
use App\Observers\CommunityHomeContentsObserver;
use App\Models\CommunityCategory;
use App\Observers\CommunityCategoryObserver;
use App\Models\CommunityPages;
use App\Observers\CommunityPagesObserver;
use App\Models\Post;
use App\Observers\PostObserver;
use App\Models\AdditionalPage;
use App\Observers\AdditionalPageObserver;
use App\Models\Page;
use App\Observers\PageObserver;
use App\Models\BusinessProductsCategories;
use App\Observers\BusinessProductsCategoriesObserver;
use App\Models\BusinessDesignSettings;
use App\Observers\BusinessDesignSettingsObserver;
use App\Models\BusinessPages;
use App\Observers\BusinessPagesObserver;
use App\Models\BusinessReviews;
use App\Observers\BusinessReviewsObserver;
use App\Models\BusinessSocialFeed;
use App\Observers\BusinessSocialFeedObserver;
use App\Models\BusinessProducts;
use App\Observers\BusinessProductsObserver;
use App\Models\BusinessRatings;
use App\Observers\BusinessRatingsObserver;
use App\Models\PageBusinessdays;
use App\Observers\PageBusinessdaysObserver;
use App\Models\UserCommunitySettings;
use App\Observers\UserCommunitySettingsObserver;
use App\Models\CommunityMenuSettings;
use App\Observers\CommunityMenuSettingsObserver;
use App\Models\CommunityLanguages;
use App\Observers\CommunityLanguagesObserver;
use App\Models\UserSettings;
use App\Observers\UserSettingsObserver;
use App\Models\PostSeen;
use App\Observers\PostSeenObserver;
use App\View\Engines\ProfiledCompilerEngine;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Cache;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        // Override the default Blade compiler with a hardened variant.
        // This prevents transient Windows failures where compiled view files can
        // disappear between exists() and hash_file() during compilation.
        $this->app->singleton('blade.compiler', function ($app) {
            return tap(new \App\View\Compilers\SafeBladeCompiler(
                $app['files'],
                $app['config']['view.compiled'],
                $app['config']->get('view.relative_hash', false) ? $app->basePath() : '',
                $app['config']->get('view.cache', true),
                $app['config']->get('view.compiled_extension', 'php'),
                $app['config']->get('view.check_cache_timestamps', true),
            ), function ($blade) {
                $blade->component('dynamic-component', \Illuminate\View\DynamicComponent::class);
            });
        });
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // Allow Blade to resolve Laravel's exception renderer components during view:cache.
        // Without this, `php artisan view:cache` / `php artisan optimize` may fail when APP_DEBUG=true.
        $exceptionRendererComponentsPath = base_path('vendor/laravel/framework/src/Illuminate/Foundation/resources/exceptions/renderer/components');
        if (is_dir($exceptionRendererComponentsPath)) {
            Blade::anonymousComponentPath($exceptionRendererComponentsPath, 'laravel-exceptions-renderer');
        }

        // Register Community Observer for automatic cache invalidation
        Community::observe(CommunityObserver::class);
        
        // Register CommunityHomeContents Observer for automatic cache invalidation
        CommunityHomecontents::observe(CommunityHomeContentsObserver::class);
        
        // Register CommunityCategory Observer for automatic cache invalidation
        CommunityCategory::observe(CommunityCategoryObserver::class);
        
        // Register CommunityPages Observer for automatic cache invalidation
        CommunityPages::observe(CommunityPagesObserver::class);
        
        // Register Post Observer for automatic cache invalidation
        Post::observe(PostObserver::class);
        
        // Register AdditionalPage Observer for automatic cache invalidation
        AdditionalPage::observe(AdditionalPageObserver::class);
        
        // Register Page Observer for automatic cache invalidation
        Page::observe(PageObserver::class);
        
        // Register BusinessProductsCategories Observer for automatic cache invalidation
        BusinessProductsCategories::observe(BusinessProductsCategoriesObserver::class);
        
        // Register BusinessDesignSettings Observer for automatic cache invalidation
        BusinessDesignSettings::observe(BusinessDesignSettingsObserver::class);
        
        // Register BusinessPages Observer for automatic cache invalidation
        BusinessPages::observe(BusinessPagesObserver::class);
        
        // Register BusinessReviews Observer for automatic cache invalidation
        BusinessReviews::observe(BusinessReviewsObserver::class);
        
        // Register BusinessSocialFeed Observer for automatic cache invalidation
        BusinessSocialFeed::observe(BusinessSocialFeedObserver::class);
        
        // Register BusinessProducts Observer for automatic cache invalidation
        BusinessProducts::observe(BusinessProductsObserver::class);
        
        // Register BusinessRatings Observer for automatic cache invalidation
        BusinessRatings::observe(BusinessRatingsObserver::class);
        
        // Register PageBusinessdays Observer for automatic cache invalidation
        PageBusinessdays::observe(PageBusinessdaysObserver::class);
        
        // Register UserCommunitySettings Observer for automatic cache invalidation
        UserCommunitySettings::observe(UserCommunitySettingsObserver::class);
        
        // Register CommunityMenuSettings Observer for automatic cache invalidation
        CommunityMenuSettings::observe(CommunityMenuSettingsObserver::class);
        
        // Register CommunityLanguages Observer for automatic cache invalidation
        CommunityLanguages::observe(CommunityLanguagesObserver::class);
        
        // Register UserSettings Observer for automatic cache invalidation
        UserSettings::observe(UserSettingsObserver::class);
        
        // Register PostSeen Observer for automatic cache invalidation
        PostSeen::observe(PostSeenObserver::class);

        // Provide language switcher data globally so the Blade partial can be pure rendering.
        // This avoids DB/repository calls in `layouts/language-change.blade.php`.
        try {
            View::composer([
                // Theme::section('layouts.language-change') resolves to theme::layouts.language-change
                'theme::layouts.language-change',
                // Fallback namespace used by the theme loader
                'theme-default::layouts.language-change',
                // Compatibility: older direct view references
                'themes.frontend.default.views.layouts.language-change',

                // Community header variant
                'theme::community-layouts.language-change',
                'theme-default::community-layouts.language-change',
                'themes.frontend.default.views.community-layouts.language-change',

                // Other theme variants that may render this partial directly
                'themes.idh.default.views.layouts.language-change',
            ], function ($view) {
                try {
                    $profile = false;
                    $profile = \App\Performance\Profiling::enabled();

                    $visitingCommunity = 0;
                    try {
                        $visitingCommunity = (int) (function_exists('getVisitingCommunityId') ? getVisitingCommunityId() : 0);
                    } catch (\Throwable $e) {
                        $visitingCommunity = 0;
                    }

                    // Cache only static data (language list, lookup maps). Do NOT cache the
                    // currently selected language, since that's per-session and should update
                    // immediately after the user changes language.
                    $cacheKey = 'lang_switcher_static_c' . (int) $visitingCommunity;
                    $ttlSeconds = 120;

                    $data = null;
                    if (! $profile) {
                        $data = Cache::get($cacheKey);
                    }

                    if (! is_array($data)) {
                        $languages = [];
                        if ($visitingCommunity > 0) {
                            $visitingCm = app('App\\Repositories\\CommunityRepository')->getById($visitingCommunity);
                            if ($visitingCm) {
                                $languages = $visitingCm->present()->getActiveLanguages();
                            }
                        }

                        if (! $languages) {
                            $languages = app('App\\Repositories\\LanguageRepository')->getActiveall();
                        }

                        $languageVars = [];
                        if (! empty($languages)) {
                            foreach ($languages as $language) {
                                if (isset($language->var)) {
                                    $languageVars[] = $language->var;
                                }
                            }
                        }

                        if ($languageVars) {
                            $languageVars = array_values(array_unique($languageVars));
                        }

                        $mainLangByVar = [];
                        if ($languageVars) {
                            $rawMainLang = app('App\\Repositories\\LanguageRepository')->findManyByVar($languageVars);

                            if ($rawMainLang instanceof \Illuminate\Support\Collection) {
                                $mainLangByVar = $rawMainLang->keyBy('var')->all();
                            } elseif (is_array($rawMainLang)) {
                                // Normalize to [var => model] for fast Blade lookups.
                                foreach ($rawMainLang as $row) {
                                    if (is_object($row) && isset($row->var)) {
                                        $mainLangByVar[$row->var] = $row;
                                    }
                                }
                            }
                        }

                        $data = [
                            'visitingCommunity' => $visitingCommunity,
                            'languages' => $languages,
                            'mainLangByVar' => $mainLangByVar,
                        ];

                        if (! $profile) {
                            Cache::put($cacheKey, $data, now()->addSeconds($ttlSeconds));
                        }
                    }

                    $data['lang'] = (string) (\Session::get('lang') ?? '');
                    $view->with($data);
                } catch (\Throwable $e) {
                    // Never break rendering
                    $view->with([
                        'visitingCommunity' => 0,
                        'languages' => [],
                        'lang' => (string) (\Session::get('lang') ?? ''),
                        'mainLangByVar' => [],
                    ]);
                }
            });
        } catch (\Throwable $e) {
            // Never break rendering
        }

        // Provide side-nav data globally so the Blade partial can be pure rendering.
        // This avoids presenter/repository/DB calls in `layouts/side-nav.blade.php`.
        try {
            View::composer([
                // Theme::section('layouts.side-nav') resolves to theme::layouts.side-nav
                'theme::layouts.side-nav',
                // Fallback namespace used by the theme loader
                'theme-default::layouts.side-nav',
                // Compatibility: older direct view references
                'themes.frontend.default.views.layouts.side-nav',
            ], function ($view) {
                try {
                    if (! \Auth::check()) {
                        $view->with([
                            'loggedInUser' => null,
                            'loggedInUserPresenter' => null,
                            'userBaseCommunity' => 0,
                            'createbusinessUrl' => \URL::route('pages-create'),
                            'rideDashboardUrl' => '',
                            'canShowStoreLink' => false,
                            'totalCartCount' => 0,
                            'isIdentityVerified' => false,
                        ]);
                        return;
                    }

                    $profile = false;
                    $profile = \App\Performance\Profiling::enabled();

                    $user = \Auth::user();
                    $userPresenter = $user ? $user->present() : null;

                    $userBaseCommunity = 0;
                    try {
                        $userBaseCommunity = (int) (function_exists('getVisitingCommunityId') ? getVisitingCommunityId() : 0);
                    } catch (\Throwable $e) {
                        $userBaseCommunity = (int) ($user->visiting_community ?? 0);
                    }

                    $community = null;
                    if ($userBaseCommunity > 0) {
                        // Request-scoped runtime cache to avoid duplicate community lookups
                        // across multiple partials during the same request (especially when
                        // profiling bypasses Cache::remember).
                        $req = request();
                        $attrKey = 'runtime_community_by_id_map';
                        $map = $req->attributes->get($attrKey);
                        if (! is_array($map)) {
                            $map = [];
                        }

                        if (array_key_exists((int) $userBaseCommunity, $map)) {
                            $community = $map[(int) $userBaseCommunity];
                        } else {
                            // Centralize community lookup through the repository (includes caching).
                            $community = app('App\\Repositories\\CommunityRepository')->getById((int) $userBaseCommunity);

                            $map[(int) $userBaseCommunity] = $community;
                            $req->attributes->set($attrKey, $map);
                        }
                    }

                    $createbusinessUrl = \URL::route('pages-create');
                    $rideDashboardUrl = '';
                    if ($community) {
                        try {
                            $communityPresenter = $community->present();
                            $createbusinessUrl = $communityPresenter ? $communityPresenter->url('createbusiness') : $createbusinessUrl;
                        } catch (\Throwable $e) {
                            // ignore
                        }

                        try {
                            if ((int) ($community->race_community ?? 0) === 1) {
                                $rideDashboardUrl = \URL::route('community-ride-booking-dashboard', ['slug' => $community->id]);
                            }
                        } catch (\Throwable $e) {
                            $rideDashboardUrl = '';
                        }
                    }

                    $canShowStoreLink = false;
                    $totalCartCount = 0;
                    $isIdentityVerified = false;

                    if ($user && $userPresenter) {
                        // Cache these for normal traffic; bypass for profiling.
                        $canShowStoreKey = 'side_nav_can_show_store_u' . (int) $user->id;
                        $cartCountKey = 'side_nav_cart_count_u' . (int) $user->id;
                        $identityVerifiedKey = 'side_nav_identity_verified_u' . (int) $user->id;

                        if (! $profile) {
                            $canShowStoreLink = (bool) Cache::remember($canShowStoreKey, now()->addSeconds(60), function () use ($userPresenter) {
                                return method_exists($userPresenter, 'canShowStoreLink') ? (bool) $userPresenter->canShowStoreLink() : false;
                            });

                            $totalCartCount = (int) Cache::remember($cartCountKey, now()->addSeconds(20), function () {
                                return (int) app('App\\Repositories\\BusinessProductsCartRepository')->totalCartCount();
                            });

                            $isIdentityVerified = (bool) Cache::remember($identityVerifiedKey, now()->addSeconds(120), function () use ($userPresenter) {
                                $uid = (int) (\Auth::id() ?? 0);
                                return (method_exists($userPresenter, 'isIdentityVerified') && $uid > 0)
                                    ? (bool) $userPresenter->isIdentityVerified($uid)
                                    : false;
                            });
                        } else {
                            $canShowStoreLink = method_exists($userPresenter, 'canShowStoreLink') ? (bool) $userPresenter->canShowStoreLink() : false;
                            $totalCartCount = (int) app('App\\Repositories\\BusinessProductsCartRepository')->totalCartCount();
                            $uid = (int) (\Auth::id() ?? 0);
                            $isIdentityVerified = (method_exists($userPresenter, 'isIdentityVerified') && $uid > 0)
                                ? (bool) $userPresenter->isIdentityVerified($uid)
                                : false;
                        }
                    }

                    $view->with([
                        'loggedInUser' => $user,
                        'loggedInUserPresenter' => $userPresenter,
                        'userBaseCommunity' => $userBaseCommunity,
                        'createbusinessUrl' => $createbusinessUrl,
                        'rideDashboardUrl' => $rideDashboardUrl,
                        'canShowStoreLink' => $canShowStoreLink,
                        'totalCartCount' => $totalCartCount,
                        'isIdentityVerified' => $isIdentityVerified,
                    ]);
                } catch (\Throwable $e) {
                    $view->with([
                        'loggedInUser' => null,
                        'loggedInUserPresenter' => null,
                        'userBaseCommunity' => 0,
                        'createbusinessUrl' => \URL::route('pages-create'),
                        'rideDashboardUrl' => '',
                        'canShowStoreLink' => false,
                        'totalCartCount' => 0,
                        'isIdentityVerified' => false,
                    ]);
                }
            });
        } catch (\Throwable $e) {
            // Never break rendering
        }

        // Provide header-menubar community data globally so the Blade partial can be pure rendering.
        // This avoids Community::find() calls in `layouts/header-menubar.blade.php`.
        try {
            View::composer([
                'theme::layouts.header-menubar',
                'theme-default::layouts.header-menubar',
                'themes.frontend.default.views.layouts.header-menubar',
            ], function ($view) {
                try {
                    $profile = false;
                    $profile = \App\Performance\Profiling::enabled();

                    if (! \Auth::check()) {
                        $view->with([
                            'headerMenuVisitingCommunityId' => 0,
                            'headerMenuCommunity' => null,
                            'headerMenuCommunityPresenter' => null,
                            'headerMenuCreatebusinessUrl' => \URL::route('pages-create'),
                        ]);
                        return;
                    }

                    $user = \Auth::user();
                    $userBaseCommunity = 0;
                    try {
                        $userBaseCommunity = (int) (function_exists('getVisitingCommunityId') ? getVisitingCommunityId() : 0);
                    } catch (\Throwable $e) {
                        $userBaseCommunity = (int) ($user->visiting_community ?? 0);
                    }

                    $community = null;
                    if ($userBaseCommunity > 0) {
                        $req = request();
                        $attrKey = 'runtime_community_by_id_map';
                        $map = $req->attributes->get($attrKey);
                        if (! is_array($map)) {
                            $map = [];
                        }

                        if (array_key_exists((int) $userBaseCommunity, $map)) {
                            $community = $map[(int) $userBaseCommunity];
                        } else {
                            // Centralize community lookup through the repository (includes caching).
                            $community = app('App\\Repositories\\CommunityRepository')->getById((int) $userBaseCommunity);

                            $map[(int) $userBaseCommunity] = $community;
                            $req->attributes->set($attrKey, $map);
                        }
                    }

                    $createbusinessUrl = \URL::route('pages-create');
                    $communityPresenter = null;
                    if ($community) {
                        try {
                            $communityPresenter = $community->present();
                            if ($communityPresenter) {
                                $createbusinessUrl = $communityPresenter->url('createbusiness');
                            }
                        } catch (\Throwable $e) {
                            $communityPresenter = null;
                        }
                    }

                    $view->with([
                        'headerMenuVisitingCommunityId' => (int) $userBaseCommunity,
                        'headerMenuCommunity' => $community,
                        'headerMenuCommunityPresenter' => $communityPresenter,
                        'headerMenuCreatebusinessUrl' => $createbusinessUrl,
                    ]);
                } catch (\Throwable $e) {
                    // Never break rendering
                }
            });
        } catch (\Throwable $e) {
            // Never break rendering
        }

        if (config('app.force_https') === true) {
            URL::forceScheme('https');
            
            // Force HTTPS in request context
            if (isset($_SERVER['HTTPS'])) {
                $_SERVER['HTTPS'] = 'on';
            }
            if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] !== 443) {
                $_SERVER['SERVER_PORT'] = 443;
            }
        }

        Paginator::defaultView('pagination::bootstrap-4');

        // When profiling is enabled, replace the Blade engine with an instrumented compiler engine
        // so we can separate Blade compile time vs view evaluation time.
        try {
            if ($this->app->bound('view.engine.resolver')) {
                $resolver = $this->app->make('view.engine.resolver');

                $resolver->register('blade', function () {
                    $compiler = new ProfiledCompilerEngine(
                        app()->make('blade.compiler'),
                        app()->make('files')
                    );

                    app()->terminating(static function () use ($compiler) {
                        $compiler->forgetCompiledOrNotExpired();
                    });

                    return $compiler;
                });
            }
        } catch (\Throwable $e) {
            // Never allow profiling plumbing to break application boot.
        }
    }
}
