<?php
/**
 * Unified integration merger.
 *
 * @package FleekDash\Services
 */

namespace FleekDash\Services;

use FleekDash\Controllers\License\LicenseHelpers;
use FleekDash\Core\IntegrationManager;
use FleekDash\Integration\Registry;
use WP_REST_Request;

defined('ABSPATH') || exit;

class IntegrationMergerService
{
    /**
     * Request-scoped caches only. Feature access and role filtering are user-specific.
     *
     * @var array<string, array<int, array<string, mixed>>>
     */
    private static array $mergedCache = [];

    /**
     * @var array<string, array<string, mixed>>|null
     */
    private static ?array $featureMapCache = null;

    /**
     * Get a unified, role-filtered integration list.
     *
     * @return array<int, array<string, mixed>>
     */
    public static function getAll(bool $activeOnly = false, bool $includeCatalog = true): array
    {
        $cacheKey = self::buildRequestCacheKey($activeOnly, $includeCatalog);
        if (isset(self::$mergedCache[$cacheKey])) {
            return self::$mergedCache[$cacheKey];
        }

        $merged = [];

        foreach (self::getInternalIntegrations() as $id => $integration) {
            $normalized = IntegrationNormalizerService::internal((string) $id, $integration);
            if (!$activeOnly || !empty($normalized['is_active'])) {
                $merged[$normalized['integration_id']] = $normalized;
            }
        }

        foreach (self::getRegistryIntegrations($activeOnly) as $integration) {
            $normalized = IntegrationNormalizerService::registry($integration);
            $id = $normalized['integration_id'];
            $merged[$id] = isset($merged[$id])
                ? IntegrationNormalizerService::merge($merged[$id], $normalized)
                : $normalized;
        }

        if ($includeCatalog && !$activeOnly) {
            foreach (PremiumExtensionCatalogService::getAll() as $integration) {
                $normalized = IntegrationNormalizerService::catalog($integration);
                $merged[$normalized['integration_id']] ??= $normalized;
            }
        }

        $merged = self::enrichWithFeatureAccess($merged);
        $merged = self::filterByRole($merged);
        uasort($merged, [self::class, 'sortIntegrations']);

        self::$mergedCache[$cacheKey] = array_values($merged);

        return self::$mergedCache[$cacheKey];
    }

    public static function clearCache(): void
    {
        self::$mergedCache = [];
        self::$featureMapCache = null;
    }

    public static function get(string $integrationId): ?array
    {
        $integrationId = sanitize_key($integrationId);

        foreach (self::getAll(false, true) as $integration) {
            if (($integration['integration_id'] ?? '') === $integrationId || ($integration['id'] ?? '') === $integrationId) {
                return $integration;
            }
        }

        return null;
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private static function getInternalIntegrations(): array
    {
        if (!class_exists(IntegrationManager::class)) {
            return [];
        }

        try {
            return IntegrationManager::getInstance()->discover();
        } catch (\Throwable $e) {
            if (function_exists('fleekdash_log_error')) {
                fleekdash_log_error('[IntegrationMergerService] Failed to discover internal integrations: ' . $e->getMessage());
            }
            return [];
        }
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private static function getRegistryIntegrations(bool $activeOnly): array
    {
        try {
            return Registry::getAll($activeOnly);
        } catch (\Throwable $e) {
            if (function_exists('fleekdash_log_error')) {
                fleekdash_log_error('[IntegrationMergerService] Failed to load registry integrations: ' . $e->getMessage());
            }
            return [];
        }
    }

    /**
     * @param array<string, array<string, mixed>> $integrations
     * @return array<string, array<string, mixed>>
     */
    private static function enrichWithFeatureAccess(array $integrations): array
    {
        $features = self::getFeatureMap();
        $raw_license = \get_option('fleekdash_v2_license_data', []);
        $licenseData = LicenseHelpers::getCanonicalLicenseRecord(\is_array($raw_license) ? $raw_license : null)
            ?? (\is_array($raw_license) ? $raw_license : []);
        $tierData = \get_option('fleekdash_v2_tier_data', []);
        $userTier = self::resolveTierName(is_array($tierData) ? $tierData : [], \is_array($licenseData) ? $licenseData : []);

        foreach ($integrations as &$integration) {
            $featureKey = (string) ($integration['feature_key'] ?? '');
            $feature = $featureKey !== '' ? ($features[$featureKey] ?? null) : null;
            $licenseRequired = (bool) ($integration['license_required'] ?? false);

            if ($feature) {
                $enabled = (bool) ($feature['enabled'] ?? $feature['is_enabled'] ?? $feature['resolved_enabled'] ?? false);
                $integration['license_required'] = $licenseRequired || !$enabled;
                $integration['has_feature_access'] = $enabled;
                $integration['required_tier'] = $integration['required_tier'] ?? ($feature['plan_label'] ?? $feature['required_tier'] ?? null);
            } else {
                $integration['has_feature_access'] = !$licenseRequired;
            }

            $integration['user_tier'] = $userTier;
        }
        unset($integration);

        return $integrations;
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private static function getFeatureMap(): array
    {
        if (self::$featureMapCache !== null) {
            return self::$featureMapCache;
        }

        $map = [];

        if (!class_exists('\FleekDash\API\FeatureController')) {
            self::$featureMapCache = $map;
            return $map;
        }

        try {
            $request = new WP_REST_Request('GET', '/fleekdash/v1/features/user');
            $request->set_param('app_name', 'fleekdash');
            $response = \FleekDash\API\FeatureController::getInstance()->getUserFeatures($request);

            if (!\is_wp_error($response)) {
                foreach (($response->get_data()['features'] ?? []) as $feature) {
                    if (is_array($feature)) {
                        $key = (string) ($feature['feature_key'] ?? $feature['key'] ?? '');
                        if ($key !== '') {
                            $map[$key] = $feature;
                        }
                    }
                }
            }
        } catch (\Throwable $e) {
            if (function_exists('fleekdash_log_debug')) {
                fleekdash_log_debug('[IntegrationMergerService] Feature enrichment skipped: ' . $e->getMessage());
            }
        }

        self::$featureMapCache = $map;

        return self::$featureMapCache;
    }

    private static function buildRequestCacheKey(bool $activeOnly, bool $includeCatalog): string
    {
        $user = \wp_get_current_user();
        $roles = is_array($user->roles ?? null) ? array_values($user->roles) : [];
        sort($roles);

        $encoded = \wp_json_encode([
            'active_only' => $activeOnly,
            'include_catalog' => $includeCatalog,
            'user_id' => \get_current_user_id(),
            'roles' => $roles,
            'can_manage_options' => \current_user_can('manage_options'),
        ]);

        return md5(is_string($encoded) ? $encoded : '');
    }

    /**
     * @param array<string, array<string, mixed>> $integrations
     * @return array<string, array<string, mixed>>
     */
    private static function filterByRole(array $integrations): array
    {
        if (\current_user_can('manage_options')) {
            return $integrations;
        }

        $user = \wp_get_current_user();
        $roles = is_array($user->roles ?? null) ? $user->roles : [];

        return array_filter($integrations, static function (array $integration) use ($roles): bool {
            $allowedRoles = $integration['allowed_roles'] ?? null;
            return empty($allowedRoles) || !is_array($allowedRoles) || !empty(array_intersect($roles, $allowedRoles));
        });
    }

    /**
     * @param array<string, mixed> $a
     * @param array<string, mixed> $b
     */
    private static function sortIntegrations(array $a, array $b): int
    {
        $priorityA = (int) ($a['priority'] ?? 100);
        $priorityB = (int) ($b['priority'] ?? 100);

        if ($priorityA !== $priorityB) {
            return $priorityA <=> $priorityB;
        }

        return strcasecmp((string) $a['name'], (string) $b['name']);
    }

    /**
     * @param array<string, mixed> $tierData
     * @param array<string, mixed> $licenseData
     */
    private static function resolveTierName(array $tierData, array $licenseData): ?string
    {
        foreach (['name', 'tier_name', 'deal_title', 'title'] as $key) {
            if (!empty($tierData[$key])) {
                return (string) $tierData[$key];
            }
        }

        return !empty($licenseData['deal_tier_id']) ? (string) $licenseData['deal_tier_id'] : null;
    }
}
