<?php

declare(strict_types=1);

namespace Maestro\Core\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Maestro\Core\Console\Commands\CoreInstallCommand;
use Maestro\Core\Console\Commands\MakeCustomFeatureCommand;
use Maestro\Core\Http\Middleware\EnsureMaestroModuleEnabled;
use Maestro\Core\Modules\Customers\Providers\CustomersModuleServiceProvider;
use Maestro\Core\Modules\HourRegistration\Providers\HourRegistrationModuleServiceProvider;
use Maestro\Core\Modules\Invoices\Providers\InvoicesModuleServiceProvider;
use Maestro\Core\Modules\Items\Providers\ItemsModuleServiceProvider;
use Maestro\Core\Modules\Offers\Providers\OffersModuleServiceProvider;
use Maestro\Core\Services\AutoSyncPackageStubs;
use Maestro\Core\Support\AuthUserResolver;
use Maestro\Core\Support\CorePaths;
use SocialiteProviders\Manager\SocialiteWasCalled;
use SocialiteProviders\Microsoft\MicrosoftExtendSocialite;

class CoreServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(CorePaths::packageFile('config/core.php'), 'core');
        $this->mergeConfigFrom(CorePaths::packageFile('config/datatable.php'), 'datatable');
        $this->app->register(DataTablesServiceProvider::class);
        // Register before module providers boot — boot()-time registration can be skipped when
        // another provider fails or the translator resolves early on some deployments.
        $this->loadTranslationsFrom(CorePaths::packageRoot().'/lang', 'maestro');
    }

    public function boot(): void
    {
        $this->bindUserRouteParameter();
        $this->registerRouteMiddleware();

        Event::listen(
            SocialiteWasCalled::class,
            MicrosoftExtendSocialite::class.'@handle',
        );

        $this->loadModules();

        $this->loadMigrationsFrom(CorePaths::packageRoot().'/database/migrations');
        $this->loadRoutesFrom(CorePaths::packageFile('routes/core.php'));
        $this->loadViewsFrom(CorePaths::packageRoot().'/resources/views', 'maestro-core');

        if ($this->app->runningInConsole()) {
            (new AutoSyncPackageStubs)->syncIfStubsAreNewer();
            $this->commands([
                CoreInstallCommand::class,
                MakeCustomFeatureCommand::class,
            ]);
        }

        $this->publishes([
            CorePaths::packageFile('config/core.php') => config_path('core.php'),
            CorePaths::packageFile('config/datatable.php') => config_path('datatable.php'),
        ], 'core-config');
    }

    private function loadModules(): void
    {
        // Published configs often list only some module keys; missing module keys mirror defaults in config/core.php.
        if (config('core.modules.customers', true) === true) {
            $this->app->register(CustomersModuleServiceProvider::class);
        }

        if (config('core.modules.hours', true) === true) {
            $this->app->register(HourRegistrationModuleServiceProvider::class);
        }

        if (config('core.modules.offers', true) === true) {
            $this->app->register(OffersModuleServiceProvider::class);
        }

        if (config('core.modules.invoices', true) === true) {
            $this->app->register(InvoicesModuleServiceProvider::class);
        }

        if (config('core.modules.items', true) === true) {
            $this->app->register(ItemsModuleServiceProvider::class);
        }
    }

    /**
     * Ensure `api/users/{user}` resolves the same User class as login (auth.providers.users.model),
     * so Spatie pivots stay on a consistent `model_type`.
     *
     * Apps may override with their own Route::bind('user', ...) registered after this provider.
     */
    private function bindUserRouteParameter(): void
    {
        Route::bind('user', function (string $value): Model {
            $class = AuthUserResolver::eloquentModelClass();

            /** @var Model $model */
            $model = $class::query()->whereKey($value)->firstOrFail();

            return $model;
        });
    }

    private function registerRouteMiddleware(): void
    {
        $router = $this->app->make(Router::class);
        $router->aliasMiddleware(
            'maestro.module',
            EnsureMaestroModuleEnabled::class,
        );
    }
}
