<?php
/**
 * FleekDash Labs Settings Controller
 *
 * Manages experimental and beta feature toggles.
 *
 * @package FleekDash\Controllers
 */

namespace FleekDash\Controllers;

use FleekDash\API\FeatureController;
use FleekDash\Services\LabsFeaturesService;
use FleekDash\Services\ResponseService;
use WP_Error;
use WP_REST_Request;

defined('ABSPATH') || exit;

/** Cache-Control value to prevent caching of Labs responses. */
const CACHE_CONTROL_NO_STORE = 'no-store, no-cache, must-revalidate, max-age=0';

class LabsSettingsController
{
    /**
     * Retrieve Labs settings (experimental/beta features).
     */
    public function getLabsSettings(WP_REST_Request $request)
    {
        $result = null;
        try {
            $service = LabsFeaturesService::init();

            // Check if force_refresh parameter is set (e.g., when accessing Labs page)
            $force_refresh = $request->get_param('force_refresh') === true ||
                            $request->get_param('force_refresh') === '1' ||
                            $request->get_param('force_refresh') === 1;

            $featuresData = $this->fetchFeaturesData($force_refresh);
            if ($featuresData instanceof WP_Error) {
                $result = ResponseService::error(
                    'Failed to load Labs features',
                    ['error' => $featuresData->get_error_message()],
                    500
                );
            } elseif (isset($featuresData['success']) && $featuresData['success'] === false) {
                // Return a REST response (not WP_Error) with blocking information
                $result = new \WP_REST_Response([
                    'success' => false,
                    'message' => $featuresData['message'] ?? 'Failed to load Labs features',
                    'blocking_reason' => $featuresData['blocking_reason'] ?? null,
                    'error_code' => $featuresData['error_code'] ?? null,
                    'data' => [
                        'features' => [],
                        'overrides' => [],
                        'blocking_reason' => $featuresData['blocking_reason'] ?? null,
                        'error_code' => $featuresData['error_code'] ?? null,
                    ],
                ], 200); // 200 because this is a valid response (not a server error)
                $result->header('Cache-Control', CACHE_CONTROL_NO_STORE);
                $result->header('Pragma', 'no-cache');
                $result->header('Expires', '0');
            } else {
                $featuresData = $service->enhanceFeaturesData($featuresData);
                $overrides = is_array($featuresData['labs_overrides'] ?? null)
                    ? $featuresData['labs_overrides']
                    : $service->getOverrides();
                $features = $service->buildLabsFeaturePayload($featuresData['features'] ?? [], $overrides);

                $result = ResponseService::success('Labs settings retrieved successfully', [
                    'features' => $features,
                    'overrides' => $overrides,
                    'app' => $featuresData['app_name'] ?? 'fleekdash',
                    'tier' => $featuresData['tier_id'] ?? null,
                ]);

                if ($result instanceof \WP_REST_Response) {
                    $result->header('Cache-Control', CACHE_CONTROL_NO_STORE);
                    $result->header('Pragma', 'no-cache');
                    $result->header('Expires', '0');
                }
            }
        } catch (\Throwable $e) {
            $result = ResponseService::error(
                'Failed to retrieve Labs settings',
                ['error' => $e->getMessage()],
                500
            );
        }

        return $result;
    }

    /**
     * Update Labs feature toggles.
     */
    public function updateLabsSettings(WP_REST_Request $request)
    {
        $result = null;
        try {
            $payload = $request->get_json_params();
            if (!$this->isValidLabsPayload($payload)) {
                $result = ResponseService::error(
                    'Invalid payload: "features" array is required',
                    null,
                    400
                );
            } else {
                $result = $this->executeLabsUpdate($payload);
            }
        } catch (\Throwable $e) {
            $result = ResponseService::error(
                'Failed to update Labs settings',
                ['error' => $e->getMessage()],
                500
            );
        }

        return $result;
    }

    /**
     * Merge incoming Labs toggles with stored overrides so concurrent or partial
     * requests do not drop keys that were omitted from the payload.
     *
     * @param array<string, bool> $existing
     * @param array<string, bool> $incoming
     * @param list<string>        $allowedKeys
     * @return array<string, bool>
     */
    private function mergeLabsOverridesWithExisting(
        array $existing,
        array $incoming,
        array $allowedKeys
    ): array {
        $allowedSet = array_flip($allowedKeys);
        $merged = array_intersect_key($existing, $allowedSet);

        foreach ($incoming as $key => $value) {
            if (isset($allowedSet[$key])) {
                $merged[$key] = (bool) $value;
            }
        }

        return $merged;
    }

    /** @param mixed $payload */
    private function isValidLabsPayload($payload): bool
    {
        return is_array($payload) && isset($payload['features']) && is_array($payload['features']);
    }

    /** @param array<string, mixed> $payload */
    private function executeLabsUpdate(array $payload)
    {
        $service = LabsFeaturesService::init();
        $featuresData = $this->fetchFeaturesData();
        if ($featuresData instanceof WP_Error) {
            return ResponseService::error(
                'Failed to validate Labs features',
                ['error' => $featuresData->get_error_message()],
                500
            );
        }

        $featuresData = $service->enhanceFeaturesData($featuresData);
        $features = $featuresData['features'] ?? [];
        $labsFeatures = $service->filterLabsFeatures($features);
        $allowedKeys = array_values(array_unique(array_map(
            static fn(array $feature) => \sanitize_key((string) ($feature['key'] ?? '')),
            $labsFeatures
        )));

        $incoming = $service->extractOverridesFromPayload($payload['features'], $allowedKeys);
        $mergedOverrides = $this->mergeLabsOverridesWithExisting(
            $service->getOverrides(),
            $incoming,
            $allowedKeys
        );
        $service->saveOverrides($mergedOverrides);

        $this->clearFeatureCache($featuresData['app_name'] ?? 'fleekdash', $featuresData['tier_id'] ?? null);

        $refreshed = $this->fetchFeaturesData();
        if (!$refreshed instanceof WP_Error) {
            $refreshed = $service->enhanceFeaturesData($refreshed);
            $refreshedOverrides = is_array($refreshed['labs_overrides'] ?? null)
                ? $refreshed['labs_overrides']
                : $service->getOverrides();
        } else {
            $refreshedOverrides = $service->getOverrides();
            $refreshed = [
                'features' => $service->applyOverridesToFeatures($features, $refreshedOverrides),
                'labs_overrides' => $refreshedOverrides,
                'app_name' => $featuresData['app_name'] ?? 'fleekdash',
                'tier_id' => $featuresData['tier_id'] ?? null,
            ];
        }

        $response = ResponseService::success('Labs settings updated successfully', [
            'features' => $service->buildLabsFeaturePayload($refreshed['features'] ?? $features, $refreshedOverrides),
            'overrides' => $refreshedOverrides,
            'app' => $refreshed['app_name'] ?? 'fleekdash',
            'tier' => $refreshed['tier_id'] ?? null,
        ]);
        $this->applyNoCacheHeaders($response);
        return $response;
    }

    /** @param \WP_REST_Response|\WP_Error $response */
    private function applyNoCacheHeaders($response): void
    {
        if ($response instanceof \WP_REST_Response) {
            $response->header('Cache-Control', CACHE_CONTROL_NO_STORE);
            $response->header('Pragma', 'no-cache');
            $response->header('Expires', '0');
        }
    }

    /**
     * Fetch feature data using FeatureController.
     *
     * @param bool $force_refresh Whether to bypass cache and fetch fresh data
     * @return array|WP_Error
     */
    private function fetchFeaturesData(bool $force_refresh = false)
    {
        $controller = FeatureController::getInstance();

        $request = new \WP_REST_Request('GET', '/fleekdash/v1/features/user');
        $request->set_param('app_name', 'fleekdash');
        if ($force_refresh) {
            $request->set_param('force_refresh', true);
        }

        $response = $controller->getUserFeatures($request);
        if ($response instanceof WP_Error) {
            return $response;
        }

        return $response->get_data();
    }

    /**
     * Clear feature cache after overrides are updated.
     */
    private function clearFeatureCache(string $appName, ?string $tierId): void
    {
        $controller = FeatureController::getInstance();

        if ($tierId) {
            $controller->clearFeaturesCacheForApp($appName, $tierId);
            return;
        }

        $controller->clearFeaturesCache();
    }
}
