<?php

namespace App\Modules;

use App\Modules\Core\ModulePackageType;
use Composer\Composer;
use Composer\InstalledVersions;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;

class ModuleServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     */
    public function register(): void
    {
        $this->app->singleton(ModuleRepository::class, function (Application $app) {
            return new ModuleRepository;
        });
    }

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

        modules()->each(function (BaseModule $module) {
            // Verify module composer requirements
            $this->verifyModuleComposerRequirements($module);

            // Suggest module composer suggestions
            $this->suggestModuleComposerSuggestions($module);

            // Module Routes
            $this->loadModuleRoutes($module);

            // Module Migrations
            $this->loadModuleMigrations($module);

            // Module Views
            $this->loadModuleViews($module);

            // Module Helpers
            $this->loadModuleHelpers($module);

            // Module Translations
            $this->loadModuleTranslations($module);

            // Module Configs
            $this->loadModuleConfigs($module);
        });
    }

    /**
     * Verify module composer requirements
     */
    public function verifyModuleComposerRequirements(BaseModule $module): void
    {
        foreach ($module->app->getComposerRequirements() as $package) {
            $packageInstalled = match ($package->type) {
                ModulePackageType::PACKAGE => InstalledVersions::isInstalled($package->name),
                ModulePackageType::MODULE => module_exists($package->name), // @phpstan-ignore-line
                default => true,
            };

            abort_if(
                boolean: ! $packageInstalled,
                code: 500,
                message: "{$package->name} {$package->version} {$package->type->value} must be installed (module: {$module->name}) \n
                    {$package->message}"
            );
        }
    }

    /**
     * Suggest module composer suggestions
     */
    public function suggestModuleComposerSuggestions(BaseModule $module): void
    {
        foreach ($module->app->getComposerRequirements() as $package) {
            $packageInstalled = match ($package->type) {
                ModulePackageType::PACKAGE => InstalledVersions::isInstalled($package->name),
                ModulePackageType::MODULE => module_exists($package->name), // @phpstan-ignore-line
                default => true,
            };

            if (! $packageInstalled) {
                echo "{$package->name} {$package->version} {$package->type->value} must be installed (module: {$module->name}) \n
                    {$package->message}";
            }
        }
    }

    /**
     * Load module configs
     */
    public function loadModuleConfigs(BaseModule $module): void
    {
        $moduleName = Str::kebab($module->name);

        $configPath = sprintf('%s/Configs/%s-config.php', $module->path, $moduleName);

        if (is_file($configPath)) {
            $this->mergeConfigFrom($configPath, 'module.'.$moduleName);
        }
    }

    /**
     * Load module views
     */
    public function loadModuleViews(BaseModule $module): void
    {
        $moduleName = Str::kebab($module->name);

        $viewsPath = $module->path.'/Resources/Views';

        if (is_dir($viewsPath)) {
            $this->loadViewsFrom($viewsPath, 'module.'.$moduleName);
        }
    }

    /**
     * Load module migrations
     */
    public function loadModuleMigrations(BaseModule $module): void
    {
        $migrationDirectory = $module->path.'/Database/Migrations';

        if (is_dir($migrationDirectory)) {
            $this->loadMigrationsFrom($migrationDirectory);
        }
    }

    /**
     * Load module routes
     */
    public function loadModuleRoutes(BaseModule $module): void
    {
        $routesPath = sprintf('%s/Routes/%s-routes.php', $module->path, Str::kebab($module->name));

        if (is_file($routesPath)) {
            $this->loadRoutesFrom($routesPath);
        }
    }

    /**
     * Load module helpers
     */
    public function loadModuleHelpers(BaseModule $module): void
    {
        $moduleName = Str::kebab($module->name);

        $helpersPath = sprintf('%s/%s-helpers.php', $module->path, $moduleName);

        if (is_file($helpersPath)) {
            require_once $helpersPath;
        }
    }

    /**
     * Load module translations
     */
    public function loadModuleTranslations(BaseModule $module): void
    {
        $translationsPath = sprintf('%s/Resources/Lang', $module->path);

        if (is_dir($translationsPath)) {
            $this->loadTranslationsFrom($translationsPath, 'module.'.Str::kebab($module->name));
        }
    }

    /**
     * Load module commands
     */
    public function loadModuleCommands(): void
    {
        if (! $this->app->runningInConsole()) {
            return;
        }

        $commands = [];
        $commandFiles = glob(modules_path('Core/Commands/').'*.php');

        if (! $commandFiles) {
            return;
        }

        foreach ($commandFiles as $commandFile) {
            $commandClass = 'App\\Modules\\Core\\Commands\\'.pathinfo($commandFile, PATHINFO_FILENAME);

            if (class_exists($commandClass)) {
                $commands[] = $commandClass;
            }
        }

        $this->commands($commands);
    }
}
