<?php

namespace App\Providers;

use App\Audit\DberpDatabaseAuditLogger;
use App\Auth\DberpEloquentUserProvider;
use App\Filament\Auth\DberpLoginResponse;
use App\Models\DberpDictionaryItem;
use App\Models\DberpDictionaryType;
use App\Models\DberpInventoryRule;
use App\Models\DberpInventoryTransaction;
use App\Models\DberpNumberSequence;
use App\Models\DberpProduct;
use App\Models\User;
use App\Observers\DberpDictionaryItemObserver;
use App\Observers\DberpDictionaryTypeObserver;
use App\Observers\DberpInventoryRuleObserver;
use App\Observers\DberpInventoryTransactionObserver;
use App\Observers\DberpNumberSequenceObserver;
use App\Observers\DberpProductObserver;
use App\Services\Install\Contracts\WebInstallMigrationCompletionGuardContract;
use App\Services\Install\Contracts\WebInstallStagedPhaseExecutor;
use App\Services\Install\Contracts\WebInstallStepRunnerContract;
use App\Services\Install\DBErpInstallStatus;
use App\Services\Install\DBErpWebInstallCoordinator;
use App\Services\Install\EnvironmentFileWriter;
use App\Services\Install\WebInstallSchemaMigrationCompletionGuard;
use App\Services\Install\WebInstallStepRunner;
use App\Support\AuditLogDisplay\DberpAuditLogDisplayFormatter;
use App\Support\AuditLogDisplay\DberpAuditLogRelationLabelResolver;
use App\Support\DberpMaintenanceDownCommandOptions;
use App\Support\DberpProductVersion;
use App\Support\Filesystem\WindowsSafeFilesystem;
use Carbon\Carbon;
use DBCore\Audit\AuditManager;
use Filament\Auth\Http\Responses\Contracts\LoginResponse as FilamentLoginResponseContract;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Console\AboutCommand;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        Auth::provider('dberp_eloquent', function ($app, array $config): DberpEloquentUserProvider {
            return new DberpEloquentUserProvider($app['hash'], $config['model']);
        });

        $this->app->bind(WebInstallStagedPhaseExecutor::class, DBErpWebInstallCoordinator::class);
        $this->app->bind(WebInstallStepRunnerContract::class, WebInstallStepRunner::class);
        $this->app->bind(WebInstallMigrationCompletionGuardContract::class, WebInstallSchemaMigrationCompletionGuard::class);

        $this->app->booting(function ($app): void {
            if ($app->runningInConsole()) {
                return;
            }

            try {
                $currentKey = (string) $app->make('config')->get('app.key', '');

                if ($currentKey === '' || $currentKey === 'base64:') {
                    $appKey = (new EnvironmentFileWriter)->ensureApplicationKey(base_path());

                    if ($appKey !== null && $appKey !== '') {
                        $app['config']->set('app.key', $appKey);
                    }
                }

                if (! $app->make(DBErpInstallStatus::class)->isInstalled()) {
                    $app['config']->set('session.driver', 'file');
                    $app['config']->set('cache.default', 'file');
                    $app['config']->set('queue.default', 'sync');
                }
            } catch (\Throwable) {
                try {
                    $appKey = (new EnvironmentFileWriter)->ensureApplicationKey(base_path());

                    if ($appKey !== null && $appKey !== '') {
                        $app['config']->set('app.key', $appKey);
                    }
                } catch (\Throwable) {
                    // .env 不可写时仅能依赖服务器日志排查
                }

                $app['config']->set('session.driver', 'file');
                $app['config']->set('cache.default', 'file');
                $app['config']->set('queue.default', 'sync');
            }
        });

        $this->app->singleton(FilamentLoginResponseContract::class, DberpLoginResponse::class);
        $this->app->singleton(DberpProductVersion::class, static fn (): DberpProductVersion => new DberpProductVersion);

        $this->app->scoped(DberpAuditLogRelationLabelResolver::class);
        $this->app->scoped(DberpAuditLogDisplayFormatter::class);

        if (PHP_OS_FAMILY !== 'Windows') {
            return;
        }

        $this->app->singleton('files', static fn (): WindowsSafeFilesystem => new WindowsSafeFilesystem);
        $this->app->singleton(Filesystem::class, static fn ($app): WindowsSafeFilesystem => $app->make('files'));
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        $this->registerMaintenanceDownExceptPaths();

        $locale = (string) config('app.locale');
        Carbon::setLocale($locale);

        Gate::before(function (User $user): ?bool {
            if ($user->isAdminEnabled() && $user->isSuperAdmin()) {
                return true;
            }

            return null;
        });

        DberpNumberSequence::observe(DberpNumberSequenceObserver::class);
        DberpDictionaryType::observe(DberpDictionaryTypeObserver::class);
        DberpDictionaryItem::observe(DberpDictionaryItemObserver::class);
        DberpInventoryTransaction::observe(DberpInventoryTransactionObserver::class);
        DberpInventoryRule::observe(DberpInventoryRuleObserver::class);
        DberpProduct::observe(DberpProductObserver::class);

        View::composer(['install.*', 'install.partials.*', 'errors.install'], static function ($view): void {
            $view->with('dberpProductVersion', app(DberpProductVersion::class));
        });

        AboutCommand::add('DBErp Product', static function (): array {
            return app(DberpProductVersion::class)->aboutSectionRows();
        });

        $this->app->booted(function (): void {
            if (! config('dbcore.modules.audit', true) || ! config('dberp.audit.use_dbcore', true)) {
                return;
            }

            $this->app->make(AuditManager::class)->addLogger(
                $this->app->make(DberpDatabaseAuditLogger::class),
                (string) config('dbcore.audit.driver', 'database'),
            );
        });
    }

    /**
     * 仅在为 down 命令收集 except 时注册（写入 framework/down），勿在 Web 请求周期内常驻 inExceptArray。
     */
    private function registerMaintenanceDownExceptPaths(): void
    {
        if (! $this->app->runningInConsole()) {
            return;
        }

        if (($GLOBALS['argv'][1] ?? null) !== 'down') {
            return;
        }

        app(DberpMaintenanceDownCommandOptions::class)->applyExceptPathsForDownCommand();
    }
}
