<?php
// components/ModuleLoader.php
// Provides a single source of truth for module definitions used across the UI

namespace Components;

class ModuleLoader
{
    /**
     * Return a list of module-specific extensions providing additional features.
     * This is simple static data for now; in a real app this could come from an API or config.
     *
     * @return array<int, array{key:string,title:string,desc:string,installed:bool,url?:string}>
     */
    public static function extensions(string $module): array
    {
        // Delegate to the active data provider (mock/live)
        return \Components\Data\DataRegistry::modules()->extensions($module);
    }
    /**
     * Returns campaign modules grouped by e-commerce touchpoints.
     * Each module uses the common format returned by self::common().
     *
     * @return array<string, array<int, array<string, mixed>>> group => [modules]
     */
    public static function campaignsByGroup(): array
    {
        // Delegate to the active data provider (mock/live)
        return \Components\Data\DataRegistry::modules()->triggersByGroup();
    }

    /**
     * Returns service modules grouped by category, same structure as campaignsByGroup().
     * @return array<string, array<int, array<string, mixed>>>
     */
    public static function serviceModulesByGroup(): array
    {
        return \Components\Data\DataRegistry::modules()->serviceModulesByGroup();
    }

    /**
     * Returns filter modules grouped by category, same structure as campaignsByGroup().
     * @return array<string, array<int, array<string, mixed>>>
     */
    public static function filterModulesByGroup(): array
    {
        return \Components\Data\DataRegistry::modules()->filterModulesByGroup();
    }

    /**
     * Returns configuration modules as a flat list using the common format.
     *
     * @return array<int, array<string, mixed>>
     */
    public static function configuration(): array
    {
        // Build a flat list from the provider's grouped configuration modules
        $provider = \Components\Data\DataRegistry::modules();
        $groups = $provider->configModulesByGroup();
        $out = [];
        foreach ($groups as $group => $mods) {
            foreach ($mods as $m) {
                // Ensure minimal required fields exist
                if (!isset($m['key']) || !isset($m['title'])) { continue; }
                $out[] = $m;
            }
        }
        return $out;
    }

    /**
     * Grouped configuration modules for the configuration sidebar.
     * Structure: [ groupLabel => [ module(common), ... ] ]
     *
     * @return array<string, array<int, array<string, mixed>>>
     */
    public static function configurationSections(): array
    {
        // Delegate to the active data provider (mock/live)
        return \Components\Data\DataRegistry::modules()->configModulesByGroup();
    }

    /**
     * Returns all modules in an associative map keyed by module key.
     *
     * @return array<string, array<string, mixed>> key => module
     */
    public static function all(): array
    {
        // Delegate to provider which aggregates campaigns, services, filters, and configuration
        return \Components\Data\DataRegistry::modules()->all();
    }

    /**
     * Returns title for a given key or null if unknown.
     */
    public static function title(string $key): ?string
    {
        return \Components\Data\DataRegistry::modules()->title($key);
    }

    /**
     * Whether a module exists (campaign or configuration).
     */
    public static function exists(string $key): bool
    {
        return \Components\Data\DataRegistry::modules()->exists($key);
    }

    /**
     * Resolve the correct blueprint path for a given module.
     * Falls back to the application default blueprint when a module-specific
     * blueprint is not defined or not accessible.
     */
    public static function blueprintPath(string $module): string
    {
        $default = (\defined('BEEZUI_ROOT') ? BEEZUI_ROOT : \dirname(__DIR__)) . '/blueprint.yaml';
        try {
            $all = self::all();
            if (isset($all[$module])) {
                $bp = $all[$module]['blueprint_path'] ?? null;
                if (\is_string($bp) && $bp !== '' && \file_exists($bp)) {
                    return $bp;
                }
            }
            // Fallback: try to resolve via legacy module class from config directory even if not listed in all()
            $legacy = self::legacyBlueprintPath($module);
            if ($legacy !== null && is_string($legacy) && $legacy !== '' && file_exists($legacy)) {
                return $legacy;
            }
        } catch (\Throwable $e) {
            // ignore and use fallback
        }
        return $default;
    }

    /**
     * Return alias module keys for a given configuration module.
     * @return array<int,string>
     */
    public static function aliasModules(string $module): array
    {
        try {
            $all = self::all();
            $am = $all[$module]['alias_modules'] ?? [];
            if (is_string($am) && $am !== '') {
                $am = array_filter(array_map('trim', explode(',', $am)), fn($s) => $s !== '');
            }
            if (is_array($am)) {
                $out = [];
                foreach ($am as $k) { $k = (string)$k; if ($k !== '') { $out[] = $k; } }
                return $out;
            }
        } catch (\Throwable $e) { /* ignore */ }
        return [];
    }

    /**
     * Return merged blueprint paths for a module including its alias modules (if any).
     * The main module blueprint is first, followed by aliases in declared order.
     * @return array<int,string>
     */
    public static function mergedBlueprintPaths(string $module): array
    {
        $paths = [];
        $main = self::blueprintPath($module);
        if (is_string($main) && $main !== '' && file_exists($main)) { $paths[] = $main; }
        foreach (self::aliasModules($module) as $alias) {
            $bp = self::blueprintPath($alias);
            if (is_string($bp) && $bp !== '' && file_exists($bp)) {
                $paths[] = $bp;
            }
        }
        if (empty($paths)) { $paths[] = (\defined('BEEZUI_ROOT') ? BEEZUI_ROOT : \dirname(__DIR__)) . '/blueprint.yaml'; }
        return $paths;
    }

    /** Try to resolve blueprint path by loading legacy class from MH_DIR_CONFIG. */
    protected static function legacyBlueprintPath(string $module): ?string
    {
        try {
            if (!defined('MH_DIR_CONFIG')) { return null; }
            $baseDir = rtrim((string)MH_DIR_CONFIG, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
            $file = $baseDir . $module . (defined('MH_FILE_EXTENSION') ? MH_FILE_EXTENSION : '.php');
            if (!is_file($file)) { return null; }
            // Include class file
            require_once $file;
            if (!class_exists($module)) { return null; }
            $obj = new $module();
            if (method_exists($obj, 'blueprintPath')) {
                $bp = $obj->blueprintPath();
                return is_string($bp) ? $bp : null;
            }
        } catch (\Throwable $e) { /* ignore */ }
        return null;
    }

    /**
     * Returns modules grouped by their group label. Can include:
     * - type = 'campaign' only
     * - type = 'configuration' only
     * - both (default)
     *
     * @param array<string> $types Allowed types filter
     * @return array<string, array<int, array<string, mixed>>> group => [modules]
     */
    public static function byGroup(array $types = ['campaign', 'service', 'filter', 'configuration']): array
    {
        $out = [];
        $all = \Components\Data\DataRegistry::modules()->all();
        foreach ($all as $m) {
            if (!in_array($m['type'], $types, true)) {
                continue;
            }
            $out[$m['group']][] = $m;
        }
        return $out;
    }

    /**
     * Build a normalized module definition.
     *
     * @return array<string, mixed>
     */
    public static function common(string $key, string $title, string $desc = '', string $group = '', string $type = ''): array
    {
        return [
            'key' => $key,
            'title' => $title,
            'desc' => $desc,
            'group' => $group,
            'type' => $type,
        ];
    }

}
