<?php

/**
 * LolaCore — Remote Ability Allow List
 *
 * @package LolaCore
 * @license GPL-2.0-or-later
 */

declare(strict_types=1);

namespace LolaCore\Hub;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Single source of truth for abilities a remote caller (MCP or LolaHub master) may use.
 *
 * Shared by McpServerRegistrar and the sprint C1 remote execution channel so the
 * two surfaces never diverge (PRD-MASTER §6.4 / D10).
 */
class RemoteAbilityAllowList
{
    /**
     * @return array<string>
     */
    public function getAllowedAbilities(): array
    {
        $registry           = new \LolaCore\Actions\AbilitiesRegistry();
        $webChatAbilities   = $registry->getRegisteredAbilities();
        $disabledDomains    = $this->getDisabledDomains();
        $disabledCategories = $this->expandDisabledCategories($disabledDomains);
        $addonDomains       = apply_filters('lolacore_addon_domains', []);

        foreach ($addonDomains as $addonSlug => $addonData) {
            $hasLicense = !empty($addonData['license_active']);
            if (!$hasLicense || in_array($addonSlug, $disabledDomains, true)) {
                $category = $addonData['category'] ?? $addonSlug;
                if (!in_array($category, $disabledCategories, true)) {
                    $disabledCategories[] = $category;
                }
            }
        }

        $allowed = [];

        foreach ($webChatAbilities as $toolName => $tool) {
            if (!is_string($toolName) || $toolName === '') {
                continue;
            }

            $category = $tool['category'] ?? 'uncategorized';
            if (in_array($category, $disabledCategories, true)) {
                continue;
            }

            $actionId  = strtolower(preg_replace('/([a-z0-9])([A-Z])/', '$1_$2', $toolName));
            $allowed[] = 'lolacore/' . str_replace('_', '-', $actionId);
        }

        $allowed[] = 'mcp-adapter/discover-abilities';
        $allowed[] = 'mcp-adapter/get-ability-info';
        $allowed[] = 'mcp-adapter/execute-ability';

        return array_values(array_unique(array_merge(
            $allowed,
            $this->collectRegisteredCoreAbilities(),
            $this->collectRegisteredAddonAbilities($disabledDomains, $addonDomains)
        )));
    }

    public function isAllowed(string $abilityName): bool
    {
        return in_array($abilityName, $this->getAllowedAbilities(), true);
    }

    /**
     * @return string[]
     */
    private function getDisabledDomains(): array
    {
        $disabledDomainsJson = \LolaCore\Admin\Settings::getConfig('disabled_domains', '[]');
        $disabledDomains     = json_decode((string) $disabledDomainsJson, true);

        return is_array($disabledDomains) ? $disabledDomains : [];
    }

    /**
     * @param string[] $disabledDomains
     * @return string[]
     */
    private function expandDisabledCategories(array $disabledDomains): array
    {
        $disabledCategories = [];

        foreach ($disabledDomains as $domainSlug) {
            if (isset(\LolaCore\Admin\Settings::DOMAIN_MAP[$domainSlug])) {
                $disabledCategories = array_merge(
                    $disabledCategories,
                    \LolaCore\Admin\Settings::DOMAIN_MAP[$domainSlug]
                );
            }
        }

        return $disabledCategories;
    }

    /**
     * @return array<string, array{meta: array<string, mixed>}>
     */
    private function getRegisteredAbilityEntries(): array
    {
        if (function_exists('wp_get_abilities')) {
            $entries = [];

            foreach (wp_get_abilities() as $slug => $ability) {
                if (!is_string($slug) || !$ability instanceof \WP_Ability) {
                    continue;
                }

                $entries[$slug] = [
                    'meta' => $ability->get_meta(),
                ];
            }

            return $entries;
        }

        if (function_exists('wp_get_registered_abilities')) {
            $entries = [];

            foreach (wp_get_registered_abilities() as $slug => $config) {
                if (!is_string($slug) || !is_array($config)) {
                    continue;
                }

                $entries[$slug] = [
                    'meta' => is_array($config['meta'] ?? null) ? $config['meta'] : [],
                ];
            }

            return $entries;
        }

        return [];
    }

    /**
     * @return string[]
     */
    private function collectRegisteredCoreAbilities(): array
    {
        $allowed = [];

        foreach ($this->getRegisteredAbilityEntries() as $slug => $config) {
            if (!str_starts_with($slug, 'lolacore/')) {
                continue;
            }

            $meta = $config['meta'] ?? [];
            if (empty($meta['mcp']['public'])) {
                continue;
            }

            $allowed[] = $slug;
        }

        return $allowed;
    }

    /**
     * @param string[]                            $disabledDomains
     * @param array<string, array<string, mixed>> $addonDomains
     * @return string[]
     */
    private function collectRegisteredAddonAbilities(array $disabledDomains, array $addonDomains): array
    {
        $namespaceToDomain = [
            'lolacore-fse'        => 'fse',
            'lolacore-statistics' => 'statistics',
            'lolacore-yoast'      => 'seo',
            'lolacore-wpforms'    => 'wpforms',
        ];

        $allowed = [];

        foreach ($this->getRegisteredAbilityEntries() as $slug => $config) {
            if (!str_contains($slug, '/')) {
                continue;
            }

            [$namespace] = explode('/', $slug, 2);
            if (!str_starts_with($namespace, 'lolacore-')) {
                continue;
            }

            $domainSlug = $namespaceToDomain[$namespace] ?? '';
            if ($domainSlug !== '' && in_array($domainSlug, $disabledDomains, true)) {
                continue;
            }

            if ($domainSlug !== '' && isset($addonDomains[$domainSlug])) {
                $addonData  = $addonDomains[$domainSlug];
                $hasLicense = !empty($addonData['license_active']);
                if (!$hasLicense) {
                    continue;
                }
            }

            $meta = $config['meta'] ?? [];
            if (empty($meta['mcp']['public'])) {
                continue;
            }

            $allowed[] = $slug;
        }

        return $allowed;
    }
}
