<?php

namespace Kylin\Modules;

use Illuminate\Container\Container;
use Illuminate\Support\Str;
use Kylin\Modules\Exceptions\InvalidModuleException;
use Illuminate\Support\helpers;

/**
 * 模块类
 *
 * 负责模块的初始化、验证、启动和管理
 */
class Module
{
    /**
     * 模块名称
     */
    private string $name;

    /**
     * 模块composer.json数据
     */
    private array $composer;

    /**
     * 模块状态
     */
    private bool $enabled = true;

    /**
     * 创建一个新的模块实例
     *
     * @param  Container  $app  应用容器
     * @param  string  $path  模块路径
     *
     * @throws InvalidModuleException 如果模块路径不存在或缺少composer.json文件
     */
    public function __construct(
        private readonly Container $app,
        private string $path
    ) {
        $this->path = realpath($path);

        $this->validate();
        $this->initialize();
    }

    /**
     * 验证模块
     *
     * @throws InvalidModuleException 如果模块路径不存在或缺少composer.json文件
     */
    protected function validate(): void
    {
        if (!is_dir($this->path)) {
            throw new InvalidModuleException(
                "模块路径 [{$this->path}] 不存在。"
            );
        }

        if (!file_exists($this->getComposerPath())) {
            // 使用模块路径而不是模块名称，因为此时名称还未初始化
            throw new InvalidModuleException(
                "模块路径 [{$this->path}] 缺少 composer.json 文件。"
            );
        }
    }

    /**
     * 初始化模块
     */
    protected function initialize(): void
    {
        $this->composer = json_decode(
            file_get_contents($this->getComposerPath()), true
        ) ?? [];

        $this->name = $this->getComposerName();
        $this->enabled = $this->isEnabledByConfig();
    }

    /**
     * 启动模块
     */
    public function boot(): void
    {
        if (!$this->isEnabled()) {
            return;
        }

        $this->registerProviders();
        $this->registerRoutes();
        $this->registerViews();
        $this->registerTranslations();
        $this->registerConfig();
    }

    /**
     * 注册模块提供者
     */
    protected function registerProviders(): void
    {
        $providers = $this->get('extra.laravel.providers', []);

        foreach ($providers as $provider) {
            if (class_exists($provider)) {
                $this->app->register($provider);
            }
        }
    }

    /**
     * 注册模块路由
     */
    protected function registerRoutes(): void
    {
        $routesPath = $this->getPath('routes');

        if (is_dir($routesPath)) {
            $routes = glob($routesPath . '/*.php');

            foreach ($routes as $route) {
                require $route;
            }
        }
    }

    /**
     * 注册模块视图
     */
    protected function registerViews(): void
    {
        $viewsPath = $this->getPath('resources/views');

        if (is_dir($viewsPath)) {
            $this->app['view']->addNamespace(
                $this->getName(), $viewsPath
            );
        }
    }

    /**
     * 注册模块翻译
     */
    protected function registerTranslations(): void
    {
        $translationsPath = $this->getPath('resources/lang');

        if (is_dir($translationsPath)) {
            $this->app['translator']->addNamespace(
                $this->getName(), $translationsPath
            );
        }
    }

    /**
     * 注册模块配置
     */
    protected function registerConfig(): void
    {
        $configPath = $this->getPath('config');

        if (is_dir($configPath)) {
            $configs = glob($configPath . '/*.php');

            foreach ($configs as $config) {
                $key = basename($config, '.php');
                $this->app['config']->set(
                    "{$this->getName()}.{$key}", require $config
                );
            }
        }
    }

    /**
     * 获取模块名称
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * 获取模块驼峰名称
     */
    public function getStudlyName(): string
    {
        return Str::studly($this->name);
    }

    /**
     * 获取模块蛇形名称
     */
    public function getSnakeName(): string
    {
        return Str::snake($this->name);
    }

    /**
     * 获取模块路径
     *
     * @param  string  $path  相对路径
     */
    public function getPath(string $path = ''): string
    {
        return $this->path . ($path ? DIRECTORY_SEPARATOR . $path : '');
    }

    /**
     * 获取模块额外路径
     *
     * @param  string  $path  相对路径
     */
    public function getExtraPath(string $path = ''): string
    {
        return $this->getPath($path);
    }

    /**
     * 获取composer.json路径
     */
    public function getComposerPath(): string
    {
        return $this->getPath('composer.json');
    }

    /**
     * 获取模块命名空间
     */
    public function getNamespace(): string
    {
        $psr4 = $this->get('autoload.psr-4', []);
        $moduleNamespace = $this->app['config']->get('modules.defaults.namespace', 'Kylin');
        return $psr4 ? key($psr4) : "{$moduleNamespace}\\{$this->getStudlyName()}";
    }

    /**
     * 获取模块状态
     */
    public function isEnabled(): bool
    {
        return $this->enabled;
    }

    /**
     * 启用模块
     */
    public function enable(): self
    {
        $this->enabled = true;

        return $this;
    }

    /**
     * 禁用模块
     */
    public function disable(): self
    {
        $this->enabled = false;

        return $this;
    }

    /**
     * 检查模块是否通过配置启用
     */
    protected function isEnabledByConfig(): bool
    {
        $enabled = $this->app['config']->get('modules.enabled', []);
        $disabled = $this->app['config']->get('modules.disabled', []);

        if (in_array($this->name, $disabled)) {
            return false;
        }

        if (!empty($enabled) && !in_array($this->name, $enabled)) {
            return false;
        }

        return true;
    }

    /**
     * 从composer.json获取模块名称
     */
    protected function getComposerName(): string
    {
        return basename($this->path);
    }

    /**
     * 从composer.json获取值
     *
     * @param  string  $key  键名
     * @param  mixed  $default  默认值
     */
    public function get(string $key, mixed $default = null): mixed
    {
        return data_get($this->composer, $key, $default);
    }

    /**
     * 获取模块composer数据
     */
    public function getComposer(): array
    {
        return $this->composer;
    }

    /**
     * 获取相对于基础路径的模块路径
     */
    public function getRelativePath(): string
    {
        return str_replace(base_path(), '', $this->path);
    }

    /**
     * 获取模块属性的魔术方法
     *
     * @param  string  $property  属性名
     */
    public function __get(string $property): mixed
    {
        if (method_exists($this, "get" . ucfirst($property))) {
            return $this->{"get" . ucfirst($property)}();
        }

        return $this->get($property);
    }

    /**
     * 检查属性是否存在的魔术方法
     *
     * @param  string  $property  属性名
     */
    public function __isset(string $property): bool
    {
        return method_exists($this, "get" . ucfirst($property)) || isset($this->composer[$property]);
    }
}