<?php

declare(strict_types=1);

namespace ReallySimplePlugins\RSS\Core\Features\Vulnerability\Clients;

use ReallySimplePlugins\RSS\Core\Features\Vulnerability\Support\Helpers\VulnerabilityConfig;
use ReallySimplePlugins\RSS\Core\Support\Helpers\Storages\EnvironmentConfig;

/**
 * HTTP client for retrieving vulnerability data from the
 * Really Simple Plugins – Vulnerability API.
 *
 * Responsible only for making HTTP requests and normalizing API responses.
 */
final class VulnerabilityClient
{
    private VulnerabilityConfig $vulnerabilityConfig;
    private EnvironmentConfig $env;
    private string $baseUrl;

    public function __construct(
        VulnerabilityConfig $vulnerabilityConfig,
        EnvironmentConfig $environmentConfig
    ) {
        $this->vulnerabilityConfig = $vulnerabilityConfig;
        $this->env = $environmentConfig;
        $this->baseUrl = $this->resolveBaseUrl();
    }

    /**
     * @param string $component Slug of the component (e.g. "wordpress", "contact-form-7").
     * @param string $name Human-readable component name (e.g. "WordPress", "Contact Form 7").
     * @param string $type Component type: "core", "plugin", or "theme".
     * @param string|null $version Optional version for core components; ignored for plugins/themes.
     *
     * @return array{
     *      success: bool,
     *      status: int,
     *      data: array<array-key, mixed>,
     *      message?: string
     *  }
     */
    public function fetchVulnerabilities(
        string $component,
        string $name,
        string $type,
        ?string $version = null
    ): array {
        $url = $this->baseUrl;
        $query = $this->buildQuery($component, $name, $type, $version);

        $url = add_query_arg($query, $url);

        $response = wp_safe_remote_get($url, [
            'timeout' => $this->vulnerabilityConfig->getInt('client.timeout', 10),
            'headers' => [
                'Accept' => 'application/json',
            ],
        ]);

        if (is_wp_error($response)) {
            return [
                'success' => false,
                'status' => 0,
                'data' => [],
                'message' => $response->get_error_message(),
            ];
        }

        $statusCode = (int)wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);

        return $this->normalizeResponse($statusCode, $body);
    }

    /**
     * @return array{success: bool, status: int, data: array<array-key, mixed>, message?: string}
     */
    public function fetchCore(string $version): array
    {
        return $this->fetchVulnerabilities(
            'wordpress',
            'WordPress',
            'core',
            $version
        );
    }

    /**
     * @return array{success: bool, status: int, data: array<array-key, mixed>, message?: string}
     */
    public function fetchPlugin(string $slug, string $name): array
    {
        return $this->fetchVulnerabilities(
            $slug,
            $name,
            'plugin'
        );
    }

    /**
     * @return array{success: bool, status: int, data: array<array-key, mixed>, message?: string}
     */
    public function fetchTheme(string $slug, string $name): array
    {
        return $this->fetchVulnerabilities(
            $slug,
            $name,
            'theme'
        );
    }

    /**
     * Builds the query array for a vulnerability request based on component
     * data and an optional version for core components.
     *
     * @param string $component Component slug.
     * @param string $name Component name.
     * @param string $type Component type.
     * @param string|null $version Optional version for core components.
     *
     * @return array<string, string>
     */
    private function buildQuery(string $component, string $name, string $type, ?string $version): array
    {
        $query = [
            'component[eq]' => $component,
            'name[eq]' => $name,
            'type[eq]' => $type,
        ];

        if ($type === 'core' && $version !== null && $version !== '') {
            $query['version[eq]'] = $version;
        }

        return $query;
    }

    /**
     * Resolves and caches the base URL for vulnerability requests.
     *
     * Priority:
     * 1) client config (base_uri + namespace/version/endpoint)
     * 2) environment config (plugin.url + http.namespace/http.version + client endpoint)
     */
    private function resolveBaseUrl(): string
    {
        $base = rtrim($this->vulnerabilityConfig->getString('client.base_uri'), '/');
        $namespace = trim($this->vulnerabilityConfig->getString('client.namespace'), '/');
        $version = trim($this->vulnerabilityConfig->getString('client.version'), '/');
        $endpoint = trim($this->vulnerabilityConfig->getString('client.endpoint'), '/');

        if ($base !== '' && $namespace !== '' && $version !== '' && $endpoint !== '') {
            return sprintf(
                '%s/wp-json/%s/%s/%s',
                $base,
                $namespace,
                $version,
                $endpoint
            );
        }

        $envBase = rtrim($this->env->getString('plugin.url'), '/');
        $envNamespace = trim($this->env->getString('http.namespace'), '/');
        $envVersion = trim($this->env->getString('http.version'), '/');

        if ($endpoint === '') {
            $endpoint = 'vulnerabilities';
        }

        return sprintf(
            '%s/wp-json/%s/%s/%s',
            $envBase,
            $envNamespace,
            $envVersion,
            $endpoint
        );
    }

    /**
     * Normalizes the decoded API response into a consistent shape.
     *
     * @param int $statusCode HTTP status code from the response.
     * @param string $body Raw response body.
     *
     * @return array{
     *     success: bool,
     *     status: int,
     *     data: array<array-key, mixed>,
     *     message?: string
     * }
     */
    private function normalizeResponse(int $statusCode, string $body): array
    {
        $decoded = json_decode($body, true);

        if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) {
            return [
                'success' => false,
                'status' => $statusCode,
                'data' => [],
                'message' => 'Invalid JSON response from vulnerability API.',
            ];
        }

        if (isset($decoded['data']) && is_array($decoded['data'])) {
            $apiData = $decoded['data'];
            $message = isset($decoded['message']) ? (string)$decoded['message'] : null;
            $apiStatus = isset($decoded['status']) ? (string)$decoded['status'] : '';

            return [
                'success' => $apiStatus === 'success',
                'status' => $statusCode,
                'data' => $apiData, // In a correct search we only have an array of one.
                'message' => $message,
            ];
        }

        return [
            'success' => false,
            'status' => $statusCode,
            'data' => [],
            'message' => 'Unexpected response format from vulnerability API.',
        ];
    }
}
