<?php

namespace App\Core\Modules\Services;

use App\Core\Auth\Services\AuthService;
use App\Core\Modules\Models\ModuleModel;
use CodeIgniter\Router\RouteCollection;
use Config\Database;

class ModuleManager
{
    public function path(): string
    {
        return APPPATH . 'Modules' . DIRECTORY_SEPARATOR;
    }

    public function discover(): array
    {
        $modules = [];

        foreach (glob($this->path() . '*', GLOB_ONLYDIR) ?: [] as $directory) {
            $file = $directory . DIRECTORY_SEPARATOR . 'module.json';
            if (! is_file($file)) {
                continue;
            }

            $config = json_decode((string) file_get_contents($file), true);
            if (is_array($config)) {
                $config['_path'] = $directory;
                $modules[$config['name']] = $config;
            }
        }

        return $modules;
    }

    public function sync(): void
    {
        if (! Database::connect()->tableExists('core_modules')) {
            return;
        }

        $model = new ModuleModel();
        $modules = $this->discover();
        $discoveredNames = array_keys($modules);

        if ($discoveredNames === []) {
            $model->builder()->emptyTable();
        } else {
            $model->whereNotIn('name', $discoveredNames)->delete();
        }

        foreach ($modules as $module) {
            $existing = $model->where('name', $module['name'])->first();
            $payload = [
                'name' => $module['name'],
                'label' => $module['label'] ?? $module['name'],
                'version' => $module['version'] ?? '0.1.0',
                'enabled' => ! empty($module['enabled']) ? 1 : 0,
                'config_json' => json_encode($module, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            ];

            $existing ? $model->update($existing['id'], $payload) : $model->insert($payload);
        }
    }

    public function isEnabled(string $name): bool
    {
        if (! Database::connect()->tableExists('core_modules')) {
            $module = $this->discover()[$name] ?? null;

            return $module ? ! empty($module['enabled']) : false;
        }

        $row = (new ModuleModel())->where('name', $name)->first();
        if (! $row) {
            $module = $this->discover()[$name] ?? null;

            return $module ? ! empty($module['enabled']) : false;
        }

        return (bool) $row['enabled'];
    }

    public function setEnabled(string $name, bool $enabled): void
    {
        $model = new ModuleModel();
        $row = $model->where('name', $name)->first();

        if ($row) {
            $model->update($row['id'], ['enabled' => $enabled ? 1 : 0]);
        }
    }

    public function menus(): array
    {
        $auth = new AuthService();
        $menus = [];

        foreach ($this->discover() as $module) {
            if (! $this->isEnabled($module['name'])) {
                continue;
            }

            foreach ($module['menus'] ?? [] as $menu) {
                $permission = $menu['permission'] ?? null;
                if ($permission && ! $auth->can($permission)) {
                    continue;
                }

                $menus[] = $menu + ['module' => $module['name']];
            }
        }

        return $menus;
    }

    public function registerRoutes(RouteCollection $routes): void
    {
        foreach ($this->discover() as $module) {
            if (! $this->isEnabled($module['name'])) {
                continue;
            }

            $this->registerModuleFilters($module);

            $file = $module['_path'] . DIRECTORY_SEPARATOR . 'Routes.php';
            if (is_file($file)) {
                require $file;
            }
        }
    }

    private function registerModuleFilters(array $module): void
    {
        $filters = config('Filters');

        if (! empty($module['filter_alias']) && ! empty($module['filter_class'])) {
            $filters->aliases[$module['filter_alias']] = $module['filter_class'];
        }

        foreach ($module['filters'] ?? [] as $alias => $class) {
            if (is_string($alias) && is_string($class) && $alias !== '' && $class !== '') {
                $filters->aliases[$alias] = $class;
            }
        }
    }
}
