<?php

namespace App\Services;

use App\Models\ActivityLogModel;
use App\Models\SystemModuleModel;
use Config\ModuleRegistry;

/**
 * Manages which application modules are enabled for the company.
 */
class ModuleRegistryService
{
    private ?array $enabledCache = null;

    /** URI prefixes that are never blocked by module toggles. */
    private array $excludedPrefixes = [
        'login',
        'logout',
        'register',
        'install',
        'dashboard',
        'profile',
        'password',
        'account',
        'tpin',
    ];

    protected SystemModuleModel $modules;
    protected ActivityLogModel $activity;
    protected ModuleRegistry $config;

    public function __construct()
    {
        $this->modules  = new SystemModuleModel();
        $this->activity = new ActivityLogModel();
        $this->config   = config('ModuleRegistry');
    }

    /**
     * @return array<string, array{label: string, description: string, icon: string, is_core: bool, is_enabled: bool}>
     */
    public function allWithState(): array
    {
        $this->ensureSeeded();
        $enabledMap = $this->modules->enabledMap();
        $result     = [];

        foreach ($this->config->registry as $key => $meta) {
            $isCore = ! empty($meta['is_core']);
            $result[$key] = [
                'label'       => $meta['label'],
                'description' => $meta['description'],
                'icon'        => $meta['icon'],
                'is_core'     => $isCore,
                'is_enabled'  => $isCore || ($enabledMap[$key] ?? true),
            ];
        }

        return $result;
    }

    public function isEnabled(string $moduleKey): bool
    {
        $meta = $this->config->registry[$moduleKey] ?? null;
        if ($meta === null) {
            return true;
        }

        if (! empty($meta['is_core'])) {
            return true;
        }

        return $this->enabledState()[$moduleKey] ?? true;
    }

    /**
     * Resolve the module key for a URI path, or null if not module-scoped.
     */
    public function resolveModuleForPath(string $path): ?string
    {
        $path = trim(strtolower($path), '/');

        if ($path === '' || $this->isExcludedPath($path)) {
            return null;
        }

        $segment = explode('/', $path)[0];

        foreach ($this->config->registry as $key => $meta) {
            foreach ($meta['prefixes'] as $prefix) {
                if ($path === $prefix || str_starts_with($path, $prefix . '/')) {
                    return $key;
                }
            }
        }

        // Fallback: first segment matches a module prefix exactly.
        foreach ($this->config->registry as $key => $meta) {
            if (in_array($segment, $meta['prefixes'], true)) {
                return $key;
            }
        }

        return null;
    }

    public function pathIsAccessible(string $path): bool
    {
        $moduleKey = $this->resolveModuleForPath($path);

        if ($moduleKey === null) {
            return true;
        }

        return $this->isEnabled($moduleKey);
    }

    /**
     * @param list<string> $enabledKeys
     */
    public function updateEnabled(array $enabledKeys, ?int $actingUserId): array
    {
        $this->ensureSeeded();
        $enabledKeys = array_flip($enabledKeys);
        $now         = date('Y-m-d H:i:s');
        $changed     = [];

        foreach ($this->config->registry as $key => $meta) {
            if (! empty($meta['is_core'])) {
                continue;
            }

            $shouldEnable = isset($enabledKeys[$key]);
            $row          = $this->modules->where('module_key', $key)->first();

            if (! $row) {
                continue;
            }

            $currentlyEnabled = (bool) $row['is_enabled'];
            if ($currentlyEnabled === $shouldEnable) {
                continue;
            }

            $this->modules->update($row['id'], [
                'is_enabled' => $shouldEnable ? 1 : 0,
                'updated_by' => $actingUserId,
                'updated_at' => $now,
            ]);

            $changed[] = ($shouldEnable ? 'Enabled' : 'Disabled') . ' module: ' . $meta['label'];
        }

        $this->enabledCache = null;

        if ($changed !== []) {
            $this->activity->log(
                'modules',
                'updated',
                $actingUserId,
                implode('; ', $changed)
            );
        }

        return ['success' => true];
    }

    /**
     * Seed default rows for all non-core modules (all enabled).
     */
    public function ensureSeeded(): void
    {
        if ($this->modules->countAllResults() > 0) {
            return;
        }

        $now = date('Y-m-d H:i:s');

        foreach ($this->config->registry as $key => $meta) {
            if (! empty($meta['is_core'])) {
                continue;
            }

            $this->modules->insert([
                'module_key' => $key,
                'is_enabled' => 1,
                'updated_at' => $now,
            ]);
        }

        $this->enabledCache = null;
    }

    /**
     * @return array<string, bool>
     */
    private function enabledState(): array
    {
        if ($this->enabledCache !== null) {
            return $this->enabledCache;
        }

        $this->ensureSeeded();
        $this->enabledCache = $this->modules->enabledMap();

        return $this->enabledCache;
    }

    private function isExcludedPath(string $path): bool
    {
        foreach ($this->excludedPrefixes as $prefix) {
            if ($path === $prefix || str_starts_with($path, $prefix . '/')) {
                return true;
            }
        }

        return false;
    }
}
