<?php

namespace App\Providers;

use App\Listeners\SendEmailNewMessage;
use App\Models\Bank;
use App\Models\Call;
use App\Models\Post;
use App\Models\Transaction;
use App\Observers\BankObserver;
use App\Observers\CallObserver;
use App\Observers\PostObserver;
use App\Observers\TransactionObserver;
use App\Services\PresenceService;
use App\View\Components\GuestLayout;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Vite;
use Illuminate\Support\ServiceProvider;
use Laravel\Scout\EngineManager;
use Livewire\Livewire;
use Matchish\ScoutElasticSearch\Engines\ElasticSearchEngine;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
            'Matchish\ScoutElasticSearch\ElasticSearch\EloquentHitsIteratorAggregate',
            'app\Overrides\Matchish\ScoutElasticSearch\ElasticSearch\EloquentHitsIteratorAggregate'
        );



        // Register custom Blade directive to conditionally check on activeProfile type (User, Organization, Bank, Admin)
        Blade::directive('profile', function ($expression) {
            return "<?php if (strtolower(getActiveProfileType()) === strtolower($expression)): ?>";
        });

        Blade::directive('endprofile', function () {
            return "<?php endif; ?>";
        });

        // Register PresenceService
        $this->app->singleton(PresenceService::class);

        // Register custom SocialShare service to support custom icons
        $this->app->bind(\Enflow\SocialShare\SocialShare::class, \App\Services\CustomSocialShare::class);
    }

    /**

     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // Fix manifest not found error: enforce the correct location of the manifest.json file
        Vite::useManifestFilename('.vite/manifest.json');

        // Fix 'Specified key was too long error' when storing emoji's as string in DB.
        Schema::defaultStringLength(191);


        if (!Collection::hasMacro('paginate')) {

            Collection::macro(
                'paginate',
                function ($perPage = 15, $page = null, $options = []) {
                    $page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
                    return (new LengthAwarePaginator(
                        $this->forPage($page, $perPage),
                        $this->count(),
                        $perPage,
                        $page,
                        $options
                    ))
                        ->withPath('');
                }
            );
        }

        // Manual Elasticsearch driver registration
        resolve(EngineManager::class)->extend('matchish-elasticsearch', function ($app) {
            return resolve(ElasticSearchEngine::class);
        });

        // Register Guest Layout
        Blade::component('guest-layout', GuestLayout::class);


        // Register lay-outs depending ob authorization
        Blade::directive('layout', function () {
            return Auth::check() ? 'app-layout' : 'guest-layout';

        });


        Blade::if('usercan', function ($permission) {
            // Get the web user (all permissions are assigned to Users, not to Organizations/Banks/Admins)
            $webUser = Auth::guard('web')->user();
            if (!$webUser) {
                return false;
            }

            // Use request-level cache to prevent recursion during view rendering
            static $permissionCache = [];
            $cacheKey = $webUser->id . '_' . $permission;

            if (isset($permissionCache[$cacheKey])) {
                return $permissionCache[$cacheKey];
            }

            // Permissions are always checked on the web user (guard 'web')
            // Organizations/Banks/Admins don't have their own permissions
            try {
                // Use can() method instead of hasPermissionTo to avoid loading all permissions
                $result = $webUser->can($permission);
                $permissionCache[$cacheKey] = $result;
                return $result;
            } catch (\Exception $e) {
                // Any error - return false and cache it
                $permissionCache[$cacheKey] = false;
                return false;
            }
        });


        // Override the vendor Wirechat components with custom extended ones
        Livewire::component('wirechat.chat', \App\Http\Livewire\WireChat\Chat\Chat::class);
        Livewire::component('wirechat.chats', \App\Http\Livewire\WireChat\Chats\Chats::class);
        Livewire::component('wirechat.new.chat', \App\Http\Livewire\WireChat\New\Chat::class);

        // Register WireChat event listener
        Event::listen(
            \Namu\WireChat\Events\MessageCreated::class,
            SendEmailNewMessage::class,
        );

        Livewire::component('wire-chat.typing-indicator', \App\Http\Livewire\WireChat\TypingIndicator::class);

        // Register Bank observer to auto-register new banks as love reacters/reactables
        Bank::observe(BankObserver::class);

        // Register Post observer to auto-register new posts as love reactables
        Post::observe(PostObserver::class);

        // Register Call observer to auto-register new calls as love reactables
        Call::observe(CallObserver::class);

        // Pause calls when spending profile runs out of credits after a transaction
        Transaction::observe(TransactionObserver::class);

        // Register Conversation observer to auto-set disappearing_started_at
        \Namu\WireChat\Models\Conversation::observe(\App\Observers\ConversationObserver::class);
    }
}
