<?php

/**
 * ============================================================
 *
 * @project   biiproject
 *
 * @author    Andika Debi Putra
 *
 * @email     andikadebiputra@gmail.com
 *
 * @website   https://biiproject.com
 *
 * @copyright Copyright (c) 2026 Andika Debi Putra
 * @license   Proprietary - All Rights Reserved
 *
 * @version   1.0.0
 *
 * @created   2026-05-01
 * ============================================================
 *
 * Unauthorized copying, modification, distribution, or use
 * of this file is strictly prohibited without prior written
 * permission from the author.
 * ============================================================
 */

namespace App\Providers;

use App\Events\ActivityLogCreated;
use App\Listeners\AnalyzeSystemError;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use App\Observers\PermissionObserver;
use App\Observers\RoleObserver;
use App\Observers\UserObserver;
use App\Services\Monitoring\SystemMonitoringService;
use App\Services\SystemConfig\SystemConfigService;
use Google\Client;
use Google\Service\Drive;
use Illuminate\Auth\Events\Login;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Http\Request;
use Illuminate\Log\Events\MessageLogged;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Masbug\Flysystem\GoogleDriveAdapter;
use Spatie\Activitylog\Models\Activity;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // 🔒 Configure Rate Limiters
        $this->configureRateLimiting();

        // Super Admin Gate (Developer has full power)
        Gate::before(function ($user, $capability) {
            // Monitoring dynamic gates
            $monitoringKeys = [
                'view pulse' => 'engine_pulse_enabled',
                'view telescope' => 'engine_telescope_enabled',
                'view api docs' => 'engine_swagger_enabled',
                'viewHorizon' => 'engine_horizon_enabled',
            ];

            if (isset($monitoringKeys[$capability])) {
                if (!get_setting($monitoringKeys[$capability], true)) {
                    return false; // Force deny if disabled at engine level
                }
            }

            return $user->hasRole('Developer') ? true : null;
        });

        // Force HTTPS in production
        if ($this->app->environment('production')) {
            URL::forceScheme('https');
        }

        // Global Password Reset Expiry Override
        if ($this->app->bound(SystemConfigService::class)) {
            $expiry = get_setting('password_reset_link_expiry', 60);
            config(['auth.passwords.users.expire' => $expiry]);
        }

        // User Observer
        User::observe(UserObserver::class);

        // Role Observer
        Role::observe(RoleObserver::class);

        // Permission Observer
        Permission::observe(PermissionObserver::class);

        // Windows Backup Compatibility Layer
        $this->ensureWindowsBackupCompatibility();

        // Google Drive Filesystem Adapter
        $this->registerGoogleDriveAdapter();

        // S3 Dynamic Config
        $this->injectS3DynamicConfig();

        // Backup Dynamic Config
        $this->injectBackupDynamicConfig();

        // Monitoring Dynamic Config
        $this->injectMonitoringDynamicConfig();

        // Dashboard View Composer
        $this->registerDashboardComposer();

        // Tab-permission Blade directives
        $this->registerTabPermissionDirectives();

        // 📡 Real-time Activity Log Broadcasting
        Activity::created(function ($activity) {
            try {
                broadcast(new ActivityLogCreated($activity))->toOthers();
            } catch (\Throwable $e) {
                // Silently fail if Reverb is not running
            }
        });

        // 🔍 Register AI error analysis listener (not auto-discovered for MessageLogged)
        Event::listen(
            MessageLogged::class,
            AnalyzeSystemError::class
        );
    }

    /**
     * Configure the rate limiters for the application.
     */
    protected function configureRateLimiting(): void
    {
        // Rate limiting for API
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });

        // Rate limiting for Login attempts
        RateLimiter::for('login', function (Request $request) {
            return Limit::perMinute(5)->by($request->ip())->response(function (Request $request, array $headers) {
                return response()->json([
                    'message' => 'Too many login attempts. Please try again in 1 minute.',
                    'status' => 'error',
                ], 429, $headers);
            });
        });
    }

    /**
     * Inject Monitoring configuration from database.
     */
    protected function injectMonitoringDynamicConfig(): void
    {
        if (!$this->app->bound(SystemConfigService::class)) {
            return;
        }

        config(['pulse.enabled' => get_setting('engine_pulse_enabled', true)]);
        config(['telescope.enabled' => get_setting('engine_telescope_enabled', true)]);

        if (!get_setting('engine_swagger_enabled', true)) {
            config(['l5-swagger.documentations.default.routes.api' => null]);
            config(['l5-swagger.defaults.routes.docs' => null]);
        }
    }

    /**
     * Register a view composer for the dashboard.
     */
    protected function registerDashboardComposer(): void
    {
        view()->composer('pages.dashboard', function ($view) {
            $stats = Cache::remember('dashboard_stats', now()->addMinutes(5), function () {
                return [
                    'total_users' => User::count(),
                    'active_sessions' => app(SystemMonitoringService::class)->getActiveUsers(),
                    'today_actions' => Activity::whereDate('created_at', now()->today())->count(),
                    'failed_jobs' => DB::table('failed_jobs')->count(),
                    'recent_activities' => Activity::with('causer')->latest()->take(5)->get(),
                ];
            });

            $view->with('dashboardStats', $stats);
        });
    }

    /**
     * Register Google Drive filesystem adapter.
     */
    protected function registerGoogleDriveAdapter(): void
    {
        Storage::extend('google', function ($app, $config) {
            $clientId = get_setting('gdrive_client_id', $config['clientId'] ?? '');
            $clientSecret = get_setting('gdrive_client_secret', $config['clientSecret'] ?? '');
            $refreshToken = get_setting('gdrive_refresh_token', $config['refreshToken'] ?? '');
            $folder = get_setting('gdrive_folder', $config['folder'] ?? 'LaravelBackups');

            $client = new Client;
            $client->setClientId($clientId);
            $client->setClientSecret($clientSecret);

            if ($refreshToken) {
                $token = $client->refreshToken($refreshToken);
                if (!isset($token['error'])) {
                    $client->setAccessToken($token);
                }
            }

            $service = new Drive($client);
            $adapter = new GoogleDriveAdapter($service, $folder, []);
            $driver = new Filesystem($adapter);

            return new FilesystemAdapter($driver, $adapter, $config);
        });
    }

    /**
     * Inject S3 credentials from database if set.
     */
    protected function injectS3DynamicConfig(): void
    {
        if (get_setting('backup_db_driver') === 's3') {
            config([
                'filesystems.disks.s3.key' => get_setting('s3_key', config('filesystems.disks.s3.key')),
                'filesystems.disks.s3.secret' => get_setting('s3_secret', config('filesystems.disks.s3.secret')),
                'filesystems.disks.s3.region' => get_setting('s3_region', config('filesystems.disks.s3.region')),
                'filesystems.disks.s3.bucket' => get_setting('s3_bucket', config('filesystems.disks.s3.bucket')),
                'filesystems.disks.s3.endpoint' => get_setting('s3_endpoint', config('filesystems.disks.s3.endpoint')),
            ]);
        }
    }

    /**
     * Ensures Windows backup compatibility.
     */
    protected function ensureWindowsBackupCompatibility(): void
    {
        if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
            return;
        }

        $dbDriver = config('database.default');
        if (config("database.connections.{$dbDriver}.dump.dump_binary_path")) {
            return;
        }

        $isPg = $dbDriver === 'pgsql';
        $binary = $isPg ? 'pg_dump.exe' : 'mysqldump.exe';
        $paths = $isPg ? ['C:\laragon\bin\postgresql\*\bin', 'C:\Program Files\PostgreSQL\*\bin'] : ['C:\laragon\bin\mysql\*\bin', 'C:\xampp\mysql\bin'];

        foreach ($paths as $path) {
            $matchedPaths = glob($path, GLOB_ONLYDIR);
            if (!$matchedPaths) {
                continue;
            }
            $foundPath = $matchedPaths[count($matchedPaths) - 1];
            if (file_exists($foundPath . DIRECTORY_SEPARATOR . $binary)) {
                config(["database.connections.{$dbDriver}.dump.dump_binary_path" => $foundPath]);
                break;
            }
        }
    }

    /*
     * Register Blade if/unless directives for tab-level permission checks.
     *
     * Usage in Blade:
     *   @cantab('global settings', 'login-security') ... @endcantab
     *   @managetab('mobile settings', 'branding')   ... @endmanagetab
     */
    protected function registerTabPermissionDirectives(): void
    {
        Blade::if('cantab', fn (string $menu, string $tab) => can_view_tab($menu, $tab));
        Blade::if('managetab', fn (string $menu, string $tab) => can_manage_tab($menu, $tab));
        Blade::if('canviewanytab', fn (string $menu) => can_view_any_tab($menu));
        Blade::if('canmanageanytab', fn (string $menu) => can_manage_any_tab($menu));
    }

    /**
     * Inject Backup configuration from database.
     */
    protected function injectBackupDynamicConfig(): void
    {
        if (!$this->app->bound(SystemConfigService::class)) {
            return;
        }

        $driver = get_setting('backup_db_driver', 'local');
        config(['backup.backup.destination.disks' => [$driver]]);
        config(['backup.monitor_backups.0.disks' => [$driver]]);

        if ($driver === 'gdrive') {
            config([
                'filesystems.disks.gdrive.clientId' => get_setting('gdrive_client_id'),
                'filesystems.disks.gdrive.clientSecret' => get_setting('gdrive_client_secret'),
                'filesystems.disks.gdrive.refreshToken' => get_setting('gdrive_refresh_token'),
                'filesystems.disks.gdrive.folder' => get_setting('gdrive_folder', 'LaravelBackups'),
            ]);
        }
    }
}
