<?php

namespace App\Providers;

use App\Enums\AppStatus;
use App\Enums\Status;
use App\Filesystem\Filesystem;
use App\Models\AppModel;
use App\Models\ConfigItem;
use App\Services\HookManager;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton('files', function () {
            return new Filesystem;
        });

        $this->app->singleton(HookManager::class, function () {
            return new HookManager;
        });
    }

    public function boot(): void
    {
        $this->applySystemTimezone();
        $this->applySessionLifetime();
        $this->registerEnabledApps();
        $this->registerDomainRoutesEarly();
    }

    protected function registerEnabledApps(): void
    {
        try {
            $apps = AppModel::enabled()->get();
        } catch (\Throwable) {
            return;
        }

        foreach ($apps as $app) {
            $manifest = $app->manifest ?? [];
            $providers = $manifest['providers'] ?? [];

            foreach ($providers as $providerClass) {
                $fullClass = "App\\Apps\\" . app_id_to_class_name($app->app_id) . "\\{$providerClass}";

                if (class_exists($fullClass)) {
                    try {
                        $this->app->register($fullClass);
                    } catch (\Throwable) {
                    }
                }
            }
        }

        // 延迟到框架完全启动后再执行各应用 ServiceProvider 的 boot() 方法，
        // 确保 view/route 等核心别名已注册完毕，避免 ReflectionException
        $this->app->booted(function () {
            try {
                $apps = AppModel::enabled()->get();
            } catch (\Throwable) {
                return;
            }

            foreach ($apps as $app) {
                $manifest = $app->manifest ?? [];
                $providers = $manifest['providers'] ?? [];

                foreach ($providers as $providerClass) {
                    $fullClass = "App\\Apps\\" . app_id_to_class_name($app->app_id) . "\\{$providerClass}";

                    if (class_exists($fullClass)) {
                        try {
                            $provider = $this->app->resolveProvider($fullClass);
                            if ($provider && method_exists($provider, 'boot')) {
                                $this->app->call([$provider, 'boot']);
                            }
                        } catch (\Throwable) {
                        }
                    }
                }
            }
        });
    }

    protected function applySessionLifetime(): void
    {
        try {
            $value = DB::table('config_items')
                ->where('code', 'session_lifetime')
                ->where('status', 1)
                ->value('value');
        } catch (\Throwable) {
            return;
        }

        if ($value === null) {
            return;
        }

        $minutes = (int) $value;
        // 0 表示永不超时，设置为1年（525600分钟）
        if ($minutes === 0) {
            $minutes = 525600;
        }

        config(['session.lifetime' => $minutes]);
    }

    protected function applySystemTimezone(): void
    {
        $timezone = 'Asia/Shanghai';
        try {
            $row = DB::table('config_items')
                ->where('code', 'default_timezone')
                ->where('status', 1)
                ->value('value');
            if ($row && in_array($row, timezone_identifiers_list(), true)) {
                $timezone = $row;
            }
        } catch (\Throwable $e) {
        }

        date_default_timezone_set($timezone);
        config(['app.timezone' => $timezone]);
        Carbon::setTestNow(null);
    }

    /**
     * 提前注册域名绑定路由
     *
     * 在 boot 阶段（而非 booted 回调）注册域名路由，
     * 确保域名路由优先于 routes/web.php 中的兜底路由被匹配，
     * 使子域名模式下访问绑定域名直接进入应用首页。
     */

    /**
     * 注册绑定映射：绑定了子域名的应用域名列表。
     * 启动时由 registerDomainRoutesEarly() 填充。
     */
    protected static array $boundDomains = [];

    /**
     * 获取已绑定的子域名列表
     *
     * 供 main_url() 等辅助函数判断当前请求是否在绑定子域名上。
     */
    public static function getBoundDomains(): array
    {
        return static::$boundDomains;
    }

    protected function registerDomainRoutesEarly(): void
    {
        try {
            $domainConfigs = ConfigItem::where('code', 'like', '%_access_domain')
                ->where('value', '!=', '')
                ->whereNotNull('value')
                ->get();
        } catch (\Throwable) {
            return;
        }

        foreach ($domainConfigs as $config) {
            // 支持逗号分隔的多域名，如 "a.x.com,b.x.com"
            $domains = array_filter(array_map('trim', explode(',', $config->value)));
            if (empty($domains)) {
                continue;
            }

            // 从配置 code 中提取 app_id，如 app_cmspro_forum_access_domain → cmspro.forum
            $appId = null;
            if (preg_match('/^app_(.+)_access_domain$/', $config->code, $matches)) {
                $appId = str_replace('_', '.', $matches[1]);
            }
            if (!$appId) {
                continue;
            }

            // 检查应用是否已启用
            try {
                $appEnabled = AppModel::where('app_id', $appId)
                    ->where('status', AppStatus::ENABLED)
                    ->exists();
                if (!$appEnabled) {
                    continue;
                }
            } catch (\Throwable) {
                continue;
            }

            // 检查访问模式是否为域名绑定
            $accessModeCode = 'app_' . str_replace('.', '_', $appId) . '_access_mode';
            try {
                $accessMode = ConfigItem::where('code', $accessModeCode)
                    ->where('status', Status::ENABLED)
                    ->value('value');
                if ($accessMode !== 'domain') {
                    continue;
                }
            } catch (\Throwable) {
                continue;
            }

            static::$boundDomains = array_merge(static::$boundDomains, $domains);

            // 注册域名前端路由（提前注册，确保优先于 web.php 兜底路由）
            $className = app_id_to_class_name($appId);

            // 支持两种前端路由文件命名：home.php（如 CmsproForum）和 front.php（如 Boxcode）
            $routeCandidates = [
                ['file' => 'home.php', 'namespace' => 'Home'],
                ['file' => 'front.php', 'namespace' => 'Front'],
            ];

            foreach ($domains as $domain) {
                foreach ($routeCandidates as $candidate) {
                    $routesPath = base_path("app/Apps/{$className}/Routes/{$candidate['file']}");
                    if (file_exists($routesPath)) {
                        Route::domain($domain)
                            ->middleware(['web'])
                            ->namespace("App\\Apps\\{$className}\\Controllers\\{$candidate['namespace']}")
                            ->group($routesPath);
                        break;
                    }
                }

                // 注册子域名 catch-all 路由：拦截所有未匹配的请求，返回 404
                // 推迟到 booted 阶段注册，确保在应用 API 路由（应用 Provider boot 在 booted 回调中执行）之后
                // 使用 abort(404) 抛出 NotFoundHttpException，由 bootstrap/app.php 中的异常处理器统一渲染
                // errors/404.blade.php 视图（HTTP 200）。若直接 response()->view(..., 404) 返回 HTTP 404，
                // nginx 的 fastcgi_intercept_errors on 会拦截并显示 nginx 自带的 404 页，而非 Laravel 视图。
                $this->app->booted(function () use ($domain) {
                    Route::domain($domain)
                        ->middleware(['web'])
                        ->any('{any}', function () {
                            abort(404);
                        })
                        ->where('any', '.*');
                });
            }

            // 标记域名路由已提前注册，避免应用 ServiceProvider 重复注册
            $this->app->instance("domain_routes_registered.{$appId}", true);
        }
    }
}
