<?php

namespace App\Module\Core\Service;

use App\Shared\Interface\ModuleInterface;
use App\Service\PlanFeatureService;
use App\Service\TenantContext;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\Tenant\ModuleConfig;

class ModuleRegistry
{
    /** @var array<string, ModuleInterface> */
    private array $modules = [];

    /** @var array<string, bool>|null */
    private ?array $activeModules = null;

    public function __construct(
        iterable $modules,
        private readonly EntityManagerInterface $entityManager,
        private readonly TenantContext $tenantContext,
        private readonly PlanFeatureService $planFeatureService,
    ) {
        foreach ($modules as $module) {
            $this->modules[$module::getModuleSlug()] = $module;
        }
    }

    public function getModule(string $slug): ?ModuleInterface
    {
        return $this->modules[$slug] ?? null;
    }

    /** @return array<string, ModuleInterface> */
    public function getAllModules(): array
    {
        return $this->modules;
    }

    /** @return array<string, ModuleInterface> */
    public function getActiveModules(): array
    {
        $activeMap = $this->getActiveModuleMap();

        return array_filter(
            $this->modules,
            fn(ModuleInterface $m) => $m::isCore() || ($activeMap[$m::getModuleSlug()] ?? false)
        );
    }

    /**
     * Get active modules filtered by the current tenant's plan.
     * Modules that require a higher plan are excluded from the menu.
     *
     * @return array<string, ModuleInterface>
     */
    public function getActiveModulesForPlan(): array
    {
        $activeModules = $this->getActiveModules();

        if (!$this->tenantContext->isResolved()) {
            return $activeModules;
        }

        $tenantPlan = $this->tenantContext->getPlan();

        return array_filter(
            $activeModules,
            function (ModuleInterface $m) use ($tenantPlan) {
                // Core modules always available
                if ($m::isCore()) {
                    return true;
                }

                return $this->planFeatureService->isModuleAllowed(
                    $m::getModuleSlug(),
                    $tenantPlan
                );
            }
        );
    }

    public function isModuleActive(string $slug): bool
    {
        $module = $this->modules[$slug] ?? null;
        if ($module === null) {
            return false;
        }
        if ($module::isCore()) {
            return true;
        }

        return $this->getActiveModuleMap()[$slug] ?? false;
    }

    /**
     * Check if a module is allowed by the tenant's plan.
     */
    public function isModuleAllowedByPlan(string $slug): bool
    {
        if (!$this->tenantContext->isResolved()) {
            return true;
        }

        $module = $this->modules[$slug] ?? null;
        if ($module === null) {
            return false;
        }

        if ($module::isCore()) {
            return true;
        }

        return $this->planFeatureService->isModuleAllowed(
            $slug,
            $this->tenantContext->getPlan()
        );
    }

    /**
     * Invalidate the cached active modules map.
     * Call this after activating/deactivating a module.
     */
    public function invalidateCache(): void
    {
        $this->activeModules = null;
    }

    /**
     * Get admin menu items filtered by both module activation and plan.
     */
    public function getAdminMenuItems(): array
    {
        $menuItems = [];
        foreach ($this->getActiveModulesForPlan() as $module) {
            foreach ($module::getAdminMenuItems() as $item) {
                $menuItems[] = $item;
            }
        }

        usort($menuItems, fn($a, $b) => ($a['priority'] ?? 50) <=> ($b['priority'] ?? 50));

        return $menuItems;
    }

    private function getActiveModuleMap(): array
    {
        if ($this->activeModules !== null) {
            return $this->activeModules;
        }

        $this->activeModules = [];

        try {
            $configs = $this->entityManager
                ->getRepository(ModuleConfig::class)
                ->findAll();

            foreach ($configs as $config) {
                $this->activeModules[$config->getModuleSlug()] = $config->isActive();
            }
        } catch (\Exception) {
            // DB might not be ready yet
        }

        return $this->activeModules;
    }
}
