<?php
/**
 * Feature Supabase Service
 *
 * Fetches user features and limits from Supabase via parallel HTTP requests.
 * Extracted from FeatureController to satisfy Sonar maintainability rules.
 *
 * @package FleekDash\Services
 */

namespace FleekDash\Services;

use WP_Error;

defined('ABSPATH') || exit();

class FeatureSupabaseService
{
    private const ACCEPT_JSON = 'application/json';
    private const REST_PATH = '/rest/';

    private string $supabaseUrl;
    private string $supabaseKey;
    private string $apiVersion = 'v1';

    public function __construct(string $supabaseUrl, string $supabaseKey)
    {
        $this->supabaseUrl = $supabaseUrl;
        $this->supabaseKey = $supabaseKey;
    }

    /**
     * Fetch user features and limits from Supabase (parallel requests)
     *
     * @param string $tierId
     * @param string $appName
     * @return array{features: array, limits: array}|WP_Error
     */
    public function fetchUserFeatures(string $tierId, string $appName)
    {
        $configError = $this->validateConfig();
        if ($configError !== null) {
            return $configError;
        }

        try {
            $requests = $this->buildParallelRequests($tierId, $appName);
            $responses = $this->requestMultipleParallel($requests);

            $tierFeatures = $this->parseTierFeaturesResponse($responses);
            $limits = $this->parseLimitsResponse($responses);

            if ($tierFeatures instanceof WP_Error) {
                $result = $tierFeatures;
            } elseif ($limits instanceof WP_Error) {
                $result = $limits;
            } else {
                $labsFeatures = $this->parseLabsFeaturesResponse($responses, $tierId);
                $mergedFeatures = $this->mergeLabsFeatures($tierFeatures, $labsFeatures);
                $result = [
                    'features' => $mergedFeatures,
                    'limits' => $limits,
                ];
            }
            return $result;
        } catch (\Exception $e) {
            return new WP_Error(
                'features_fetch_exception',
                'Exception fetching features: ' . $e->getMessage(),
                ['status' => 500]
            );
        }
    }

    private function validateConfig(): ?WP_Error
    {
        if (empty($this->supabaseUrl) || empty($this->supabaseKey)) {
            return new WP_Error(
                'supabase_config_missing',
                'Supabase configuration is missing',
                ['status' => 500]
            );
        }
        return null;
    }

    /**
     * @return array<string, array{url: string, headers: array, type: string}>
     */
    private function buildParallelRequests(string $tierId, string $appName): array
    {
        $baseUrl = $this->supabaseUrl . self::REST_PATH . $this->apiVersion;
        $headers = [
            'apikey' => $this->supabaseKey,
            'Authorization' => 'Bearer ' . $this->supabaseKey,
            'Accept' => self::ACCEPT_JSON,
        ];

        $tierQuery = "?tier_id=eq.{$tierId}&select=*,tool_available_features!inner(*)&tool_available_features.app_name=eq.{$appName}";

        return [
            'tier_features' => [
                'url' => $baseUrl . '/license_tier_features' . $tierQuery,
                'headers' => $headers,
                'type' => 'GET',
            ],
            'labs_features' => [
                'url' => $baseUrl . '/tool_available_features?app_name=eq.' . $appName . '&feature_type=in.(experimental,beta)&is_available=eq.true&select=*',
                'headers' => $headers,
                'type' => 'GET',
            ],
            'limits' => [
                'url' => $baseUrl . '/license_tier_feature_limits' . $tierQuery,
                'headers' => $headers,
                'type' => 'GET',
            ],
        ];
    }

    /**
     * Validates HTTP response status. Returns WP_Error if status is not 2xx.
     *
     * @param object|\Exception $response
     * @param string $contextKey For error message (e.g. 'tier_features')
     * @return WP_Error|null
     */
    private function validateHttpResponse($response, string $contextKey): ?WP_Error
    {
        if (!\is_object($response) || $response instanceof \Exception) {
            return null; // Caller handles Exception case separately
        }
        $code = isset($response->status_code) ? (int) $response->status_code : 0;
        if ($code < 200 || $code >= 300) {
            return new WP_Error(
                'supabase_http_error',
                \sprintf('Supabase returned HTTP %d for %s', $code ?: 0, $contextKey),
                ['status' => $code ?: 500]
            );
        }
        return null;
    }

    /**
     * @param array<string, object|\Exception> $responses
     * @return array|WP_Error
     */
    private function parseTierFeaturesResponse(array $responses)
    {
        if (!isset($responses['tier_features']) || $responses['tier_features'] instanceof \Exception) {
            return new WP_Error(
                'features_fetch_failed',
                'Failed to fetch tier features from Supabase',
                ['status' => 500]
            );
        }
        $resp = $responses['tier_features'];
        $statusError = $this->validateHttpResponse($resp, 'tier_features');
        if ($statusError !== null) {
            return $statusError;
        }
        $body = \is_object($resp) ? $resp->body : '';
        return $this->formatFeatures((array) \json_decode($body, true) ?: []);
    }

    /**
     * @param array<string, object|\Exception> $responses
     * @return array
     */
    private function parseLabsFeaturesResponse(array $responses, string $tierId): array
    {
        $labsFeatures = [];
        $resp = $responses['labs_features'] ?? null;
        $canParse = $resp !== null
            && !($resp instanceof \Exception)
            && $this->validateHttpResponse($resp, 'labs_features') === null;

        if ($canParse) {
            $body = \is_object($resp) ? $resp->body : '';
            $labsBody = (array) \json_decode($body, true);
            if (\is_array($labsBody)) {
                foreach ($labsBody as $labsFeature) {
                    $labsFeatures[] = [
                        'tier_id' => $tierId,
                        'is_enabled' => false,
                        'tool_available_features' => \is_array($labsFeature) ? $labsFeature : [],
                    ];
                }
            }
        }
        return $labsFeatures;
    }

    /**
     * @param array<string, object|\Exception> $responses
     * @return array|WP_Error
     */
    private function parseLimitsResponse(array $responses)
    {
        if (!isset($responses['limits']) || $responses['limits'] instanceof \Exception) {
            return new WP_Error(
                'limits_fetch_failed',
                'Failed to fetch limits from Supabase',
                ['status' => 500]
            );
        }
        $resp = $responses['limits'];
        $statusError = $this->validateHttpResponse($resp, 'limits');
        if ($statusError !== null) {
            return $statusError;
        }
        $body = \is_object($resp) ? $resp->body : '';
        return $this->formatLimits((array) \json_decode($body, true) ?: []);
    }

    /**
     * @param array $tierFeatures
     * @param array $labsFeatures
     * @return array
     */
    private function mergeLabsFeatures(array $tierFeatures, array $labsFeatures): array
    {
        $tierFeatureKeys = \array_column($tierFeatures, 'key');
        $merged = $tierFeatures;

        foreach ($labsFeatures as $labsFeature) {
            $labsKey = $labsFeature['tool_available_features']['feature_key'] ?? null;
            if ($labsKey === null || \in_array($labsKey, $tierFeatureKeys, true)) {
                continue;
            }
            $fd = $labsFeature['tool_available_features'];
            $merged[] = [
                'id' => $fd['id'] ?? null,
                'key' => $labsKey,
                'name' => $fd['feature_name'] ?? '',
                'category' => $fd['feature_category'] ?? '',
                'type' => $fd['feature_type'] ?? 'experimental',
                'description' => $fd['description'] ?? '',
                'icon' => $fd['icon'] ?? null,
                'label' => $fd['label'] ?? null,
                'url' => $fd['url'] ?? null,
                'media_associations' => \is_array($fd['media_associations'] ?? null) ? $fd['media_associations'] : [],
                'is_enabled' => false,
                'override_icon' => null,
                'override_label' => null,
                'override_url' => null,
            ];
        }
        return $merged;
    }

    /**
     * @param array<string, array{url: string, headers: array, type?: string}> $requests
     * @return array<string, object|\Exception>
     */
    private function requestMultipleParallel(array $requests): array
    {
        if (\class_exists(\WpOrg\Requests\Requests::class)) {
            return \WpOrg\Requests\Requests::request_multiple($requests, ['timeout' => 15]);
        }

        if (\class_exists('Requests')) {
            return \Requests::request_multiple($requests, ['timeout' => 15]);
        }

        $responses = [];
        foreach ($requests as $key => $config) {
            $url = $config['url'] ?? '';
            $headers = $config['headers'] ?? [];
            $response = \wp_remote_get($url, [
                'headers' => $headers,
                'timeout' => 15,
                'sslverify' => false,
            ]);
            if (\is_wp_error($response)) {
                $responses[$key] = new \Exception($response->get_error_message());
            } else {
                $code = (int) \wp_remote_retrieve_response_code($response);
                $responses[$key] = (object) [
                    'body' => \wp_remote_retrieve_body($response),
                    'status_code' => $code,
                ];
            }
        }
        return $responses;
    }

    /**
     * @param array $rawFeatures
     * @return array
     */
    private function formatFeatures(array $rawFeatures): array
    {
        $features = [];
        foreach ($rawFeatures as $item) {
            if (isset($item['tool_available_features'])) {
                $feature = $item['tool_available_features'];
                $features[] = [
                    'id' => $feature['id'],
                    'key' => $feature['feature_key'],
                    'name' => $feature['feature_name'],
                    'category' => $feature['feature_category'],
                    'type' => $feature['feature_type'],
                    'description' => $feature['description'],
                    'icon' => $feature['icon'],
                    'label' => $feature['label'],
                    'url' => $feature['url'],
                    'media_associations' => $feature['media_associations'],
                    'is_enabled' => $item['is_enabled'],
                    'override_icon' => $item['override_icon'],
                    'override_label' => $item['override_label'],
                    'override_url' => $item['override_url'],
                ];
            } elseif (isset($item['key'])) {
                $features[] = [
                    'id' => $item['id'] ?? null,
                    'key' => $item['key'],
                    'name' => $item['name'] ?? '',
                    'category' => $item['category'] ?? 'general',
                    'type' => $item['type'] ?? 'beta',
                    'description' => $item['description'] ?? '',
                    'icon' => $item['icon'] ?? null,
                    'label' => $item['label'] ?? null,
                    'url' => $item['url'] ?? null,
                    'media_associations' => \is_array($item['media_associations'] ?? null)
                        ? $item['media_associations']
                        : [],
                    'is_enabled' => (bool) ($item['is_enabled'] ?? false),
                    'override_icon' => $item['override_icon'] ?? null,
                    'override_label' => $item['override_label'] ?? null,
                    'override_url' => $item['override_url'] ?? null,
                ];
            }
        }
        return $features;
    }

    /**
     * @param array $rawLimits
     * @return array
     */
    private function formatLimits(array $rawLimits): array
    {
        $limits = [];
        foreach ($rawLimits as $item) {
            if (isset($item['tool_available_features'])) {
                $feature = $item['tool_available_features'];
                $limits[] = [
                    'feature_id' => $feature['id'],
                    'feature_key' => $feature['feature_key'],
                    'feature_name' => $feature['feature_name'],
                    'limit_key' => $item['limit_key'],
                    'limit_value' => $item['limit_value'],
                    'is_unlimited' => $item['is_unlimited'],
                    'limit_type' => $item['limit_type'],
                    'limit_unit' => $item['limit_unit'],
                    'description' => $item['description'],
                    'display_value' => $this->formatDisplayValue($item),
                ];
            }
        }
        return $limits;
    }

    private function formatDisplayValue(array $limitData): string
    {
        if ($limitData['is_unlimited']) {
            return 'Unlimited';
        }
        return "{$limitData['limit_value']} {$limitData['limit_unit']}";
    }
}
