<?php

namespace App\Providers;

use App\CarConfiguratorRepository;
use App\CountryRepository;
use App\Mail\EmailWhitelistFilter;
use App\Master\Models\BodyStyleType;
use App\Master\Models\DrivetrainType;
use App\Master\Models\FuelType;
use App\Master\Models\TransmissionType;
use App\Master\Models\VehicleMake;
use App\Master\Models\VehicleModel;
use App\Models\ExportMap;
use App\Models\ImportCondition;
use App\Models\ImportConditionRule;
use App\Models\ImportMap;
use App\Models\ImportMapField;
use App\Modules\Stock\Motordat;
use App\Modules\Stock\Motors;
use App\Modules\VehicleLabels\LabelRepository;
use App\Modules\VehicleLabels\LeaseLabelRepository;
use App\Observers\DealershipContentObserver;
use App\Observers\DealershipObserver;
use App\Observers\LeaseVehicleObserver;
use App\Observers\NewCarObserver;
use App\Observers\ReservationObserver;
use App\Observers\VehicleAttributeObserver;
use App\Observers\VehicleObserver;
use App\Observers\VehicleOfferContentObserver;
use App\Observers\VehicleOfferObserver;
use App\ReportRepository;
use App\Services\AutoTraderAIApi;
use App\Services\AutoTraderApi;
use App\Services\TaxonomyMappingService;
use App\SettingRepository;
use App\SiteRepository;
use App\Support\CronLogger;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Auth\Notifications\VerifyEmail;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Console\Events\CommandFinished;
use Illuminate\Console\Events\CommandStarting;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Mail\Events\MessageSending;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;
use Laravel\Telescope\TelescopeApplicationServiceProvider;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\DealershipContent;
use Mtc\MercuryDataModels\KeyloopLeaseVehicleVariant;
use Mtc\MercuryDataModels\NewCar;
use Mtc\MercuryDataModels\Repositories\FeatureRepository;
use Mtc\MercuryDataModels\Tools\UiUrlGenerator;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleAttribute;
use Mtc\MercuryDataModels\VehicleOffer;
use Mtc\MercuryDataModels\VehicleOfferContent;
use Mtc\VehicleReservations\Reservation;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    #[\Override]
    public function register()
    {
        $this->setMigrationLocation();

        $this->app->singleton(CronLogger::class);

        if (class_exists(TelescopeApplicationServiceProvider::class)) {
            $this->app->register(TelescopeServiceProvider::class);
        }
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        setPermissionsTeamId('');

        $this->overwriteCallbackUrls();

        $this->app->bind(AutoTraderApi::class, fn () => new AutoTraderApi(config('services.auto-trader', [])));
        $this->app->bind(AutoTraderAIApi::class, fn () => new AutoTraderAIApi(config('services.auto-trader-ai', [])));
        $this->app->bind('site-settings', SettingRepository::class);
        $this->app->bind('reports', ReportRepository::class);
        $this->app->bind('site-repository', SiteRepository::class);
        $this->app->bind('label-repository', LabelRepository::class);
        $this->app->bind('lease-label-repository', LeaseLabelRepository::class);
        $this->app->bind('feature-flags', FeatureRepository::class);
        $this->app->bind('country-repository', CountryRepository::class);
        $this->app->bind('taxonomy-mapper', TaxonomyMappingService::class);

        $this->app->bind('motordat', Motordat::class);
        $this->app->bind('Motors', Motors::class);
        $this->app->bind('Configurator', CarConfiguratorRepository::class);

        Vehicle::observe(VehicleObserver::class);
        NewCar::observe(NewCarObserver::class);
        Dealership::observe(DealershipObserver::class);
        VehicleOffer::observe(VehicleOfferObserver::class);
        Reservation::observe(ReservationObserver::class);
        VehicleOfferContent::observe(VehicleOfferContentObserver::class);
        DealershipContent::observe(DealershipContentObserver::class);
        VehicleAttribute::observe(VehicleAttributeObserver::class);
        KeyloopLeaseVehicleVariant::observe(LeaseVehicleObserver::class);

        Relation::morphMap([
            'master-make' => VehicleMake::class,
            'master-model' => VehicleModel::class,
            'master-fuel_type' => FuelType::class,
            'master-transmission' => TransmissionType::class,
            'master-body_type' => BodyStyleType::class,
            'master-body_style' => BodyStyleType::class,
            'master-drivetrain' => DrivetrainType::class,
            'export-map' => ExportMap::class,
            'import-map' => ImportMap::class,
            'import-map-field' => ImportMapField::class,
            'import-condition' => ImportCondition::class,
            'import-condition-rule' => ImportConditionRule::class,
        ]);

        // Model::preventLazyLoading(!$this->app->isProduction());

        RateLimiter::for('sync', fn ($job) => Limit::perMinute('30'));

        // Register email whitelist filter for non-local environments
        if (!$this->app->environment('local')) {
            Event::listen(MessageSending::class, EmailWhitelistFilter::class);
        }

        $this->registerCronLogging();
    }

    /**
     * Track cron/command execution timing into the central cron_logs table.
     *
     * Console events capture the top-level (system) command; tenancy lifecycle
     * events capture each tenant slice of a fanned-out command. CronLogger is a
     * singleton so its in-process state is shared across all four listeners.
     */
    private function registerCronLogging()
    {
        Event::listen(CommandStarting::class, [CronLogger::class, 'commandStarting']);
        Event::listen(CommandFinished::class, [CronLogger::class, 'commandFinished']);
        Event::listen(TenancyInitialized::class, [CronLogger::class, 'tenancyInitialized']);
        Event::listen(TenancyEnded::class, [CronLogger::class, 'tenancyEnded']);
    }

    private function overwriteCallbackUrls()
    {
        ResetPassword::$createUrlCallback = (fn ($notifiable, $token) => UiUrlGenerator::make('reset-password', [
            'token' => $token,
            'email' => $notifiable->email,
        ]));

        VerifyEmail::$createUrlCallback = (fn ($notifiable, $token) => UiUrlGenerator::make('email/verify'
            . $notifiable->getKey() . '/' . sha1((string) $notifiable->getEmailForVerification())));
    }

    /**
     * Due to the shared data models migrations are moved in mtcmedia/mercury-data-models
     *
     * @return void
     */
    private function setMigrationLocation()
    {
        $this->app->useDatabasePath($this->app->basePath('vendor/mtcmedia/mercury-data-models/database'));
        Config::set(
            'tenancy.migration_parameters.--path',
            $this->app->basePath('vendor/mtcmedia/mercury-data-models/database/migrations/tenant')
        );
    }
}
