<?php

namespace Modules\ClinicCore\Providers;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Factory;
use Modules\ClinicCore\Console\ClcSealControlledRegister;
use Modules\ClinicCore\Console\ClcSendVaccineReminders;

class ClinicCoreServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->registerConfig();
        $this->registerViews();
        $this->registerFactories();
        $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
        $this->registerTranslations();
        $this->shareClcSettings();
        $this->registerRoleLandingMiddleware();
        $this->registerModuleAliasFallback();
        $this->registerControlledTransactionObserver();
        $this->registerBusinessObserver();
        $this->registerBookingDriver();

        if ($this->app->runningInConsole()) {
            $this->commands([
                ClcSendVaccineReminders::class,
                ClcSealControlledRegister::class,
            ]);
        }

        $this->registerSchedule();
    }

    /**
     * Ensure the `module:{Name}` route-middleware alias always resolves.
     * SaasSwitcher registers the real entitlement check on multi-project
     * hosts; on single-project deployments (standalone Shifa One) it is
     * disabled and routes carrying `module:X` would 500 with
     * "Target class [module] does not exist". Register a fail-open
     * pass-through only when nothing has claimed the alias — SaasSwitcher's
     * own aliasMiddleware() overwrites it whenever that module boots.
     */
    protected function registerModuleAliasFallback(): void
    {
        try {
            // Guarded global-helper fallback (module_entitled) — no-op when
            // SaasSwitcher already defined the real one in its register phase.
            require_once __DIR__ . '/../Support/helpers_fallback.php';

            $router = $this->app['router'];
            if (! array_key_exists('module', $router->getMiddleware())) {
                $router->aliasMiddleware('module', \Modules\ClinicCore\Http\Middleware\ModuleAliasFallback::class);
            }
        } catch (\Throwable $e) {
            // never break boot
        }
    }

    /** Plug the ClinicCore public-booking driver into PortalApi (when installed). */
    protected function registerBookingDriver(): void
    {
        if (! class_exists(\Modules\PortalApi\Support\BookingDriverRegistry::class)) {
            return;
        }
        try {
            $this->app->make(\Modules\PortalApi\Support\BookingDriverRegistry::class)
                ->register(new \Modules\ClinicCore\Services\Booking\ClcBookingDriver());
        } catch (\Throwable $e) {
            // optional integration — never break boot
        }
    }

    /**
     * Register ClinicCore cron tasks without touching app/Console/Kernel.php —
     * same pattern as the ActivityLog module. Runs only in 'live' env to
     * match the rest of the app's scheduling convention.
     */
    protected function registerSchedule(): void
    {
        $this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
            if (config('app.env') !== 'live') return;

            // Vaccination reminders (email + SMS via core notification engine).
            $schedule->command('clc:send-vaccine-reminders')->dailyAt('08:00');
        });
    }

    /** Share ClinicCore-wide settings (action_target, etc.) with all views in the clinicCore namespace */
    protected function shareClcSettings()
    {
        view()->composer('clinicCore::*', function ($view) {
            try {
                $businessId = session()->get('user.business_id');
                $value = '_self';
                if ($businessId) {
                    $value = \Modules\ClinicCore\Entities\ClcModuleSetting::get($businessId, 'action_target', '_self');
                }
                $view->with('clcActionTarget', $value);
            } catch (\Throwable $e) {
                // Composer must never break the view render; default to same-tab.
                $view->with('clcActionTarget', '_self');
            }
        });
    }

    /**
     * Push the per-role landing-redirect middleware onto the web stack so it
     * runs on every authenticated GET /home (once per session).
     */
    protected function registerRoleLandingMiddleware()
    {
        try {
            $this->app['router']->pushMiddlewareToGroup(
                'web',
                \Modules\ClinicCore\Http\Middleware\ApplyRoleLandingRedirect::class
            );
        } catch (\Throwable $e) {
            // Never let middleware wiring break the rest of boot().
        }
    }

    /**
     * Mirror controlled-medicine purchases / sales / returns into the
     * controlled-drug register the moment a Transaction is saved. The
     * observer is wrapped in try/catch inside the service so a register
     * write failure never breaks the underlying ERP sale.
     */
    /**
     * Out-of-scope follow-up #OOS-a — auto-seed the six default vet roles
     * whenever a new business is created. Existing businesses were covered
     * by the 2026_05_31_000009 migration; this observer covers everything
     * after that point.
     */
    protected function registerBusinessObserver()
    {
        try {
            if (class_exists(\App\Business::class)
                && class_exists(\Modules\ClinicCore\Observers\ClcBusinessObserver::class)) {
                \App\Business::observe(\Modules\ClinicCore\Observers\ClcBusinessObserver::class);
            }
        } catch (\Throwable $e) {
            // Never let observer wiring break boot().
        }
    }

    protected function registerControlledTransactionObserver()
    {
        try {
            \App\Transaction::saved(function ($tx) {
                \Modules\ClinicCore\Entities\Services\ControlledStockSyncService::syncTransaction($tx);
            });
            // Phase 3a: deleting a synced transaction writes reversing entries
            // for every register row that originated from it.
            \App\Transaction::deleted(function ($tx) {
                \Modules\ClinicCore\Entities\Services\ControlledStockSyncService::reverseTransaction($tx, 'delete');
            });
        } catch (\Throwable $e) {
            // Never let observer wiring break boot().
        }
    }

    public function register()
    {
        $this->app->register(RouteServiceProvider::class);
    }

    protected function registerConfig()
    {
        $this->publishes([
            __DIR__.'/../Config/config.php' => config_path('clinicCore.php'),
        ], 'config');
        $this->mergeConfigFrom(
            __DIR__.'/../Config/config.php', 'cliniccore'
        );
    }

    public function registerViews()
    {
        $viewPath = resource_path('views/modules/ClinicCore');
        $sourcePath = __DIR__.'/../Resources/views';

        $this->publishes([
            $sourcePath => $viewPath
        ], 'views');

        $this->loadViewsFrom(array_merge(array_map(function ($path) {
            return $path . '/modules/clinicCore';
        }, \Config::get('view.paths')), [$sourcePath]), 'clinicCore');
    }

    public function registerTranslations()
    {
        $langPath = resource_path('lang/modules/ClinicCore');

        if (is_dir($langPath)) {
            $this->loadTranslationsFrom($langPath, 'clinicCore');
        } else {
            $this->loadTranslationsFrom(__DIR__ .'/../Resources/lang', 'clinicCore');
        }
    }

    public function registerFactories()
    {
        if (! app()->environment('production') && $this->app->runningInConsole()) {
            app(Factory::class)->load(__DIR__ . '/../Database/factories');
        }
    }

    public function provides()
    {
        return [];
    }
}
