<?php

namespace WPSN\Modules\AI;

use WPSN\Core\Http\HttpClient;
use WPSN\Core\Logger;
use WPSN\Core\Settings;
use WPSN\Modules\SaaSSync\SaaSSyncService;

defined('ABSPATH') || exit;

final class SaaSAICreditsClient
{
    private SaaSSyncService $saas;
    private Logger $logger;
    private Settings $settings;
    private SaaSAIRequestContract $contract;

    public function __construct(SaaSSyncService $saas, Logger $logger, Settings $settings)
    {
        $this->saas = $saas;
        $this->logger = $logger;
        $this->settings = $settings;
        $this->contract = new SaaSAIRequestContract($settings, $saas);
    }

    /**
     * @return array{ok:bool,balance:float,currency:string,error:string,http_status:int,retry_after:int}
     */
    public function fetch(): array
    {
        $token = $this->contract->authToken();
        $licenseKey = $this->contract->licenseKey();
        if ($token === '' && $licenseKey === '') {
            return [
                'ok' => false,
                'balance' => 0.0,
                'currency' => 'credits',
                'error' => 'SaaS auth token is missing.',
                'http_status' => 401,
                'retry_after' => 0,
            ];
        }

        $endpoint = $this->saas->endpoint(
            'ai_credits',
            (string) $this->settings->get('saas_sync.ai_credits_path', '/wp-seo-next/ai/credits')
        );

        $timeout = (int) $this->settings->get('saas_sync.ai_credits_timeout', 20);
        $timeout = max(3, min(60, $timeout));

        $post = $this->fetchViaContractPost($endpoint, $timeout);
        if (!empty($post['ok'])) {
            return $post;
        }

        if (in_array((int) ($post['http_status'] ?? 0), [404, 405, 501], true)) {
            return $this->fetchViaLegacyGet($endpoint, $timeout);
        }

        return $post;
    }

    /**
     * @return array{ok:bool,balance:float,currency:string,error:string,http_status:int,retry_after:int}
     */
    private function fetchViaContractPost(string $endpoint, int $timeout): array
    {
        $payload = $this->contract->buildPayload('ai_credits', [
            'client' => [
                'plugin' => 'wp-seo-next',
                'version' => defined('WPSN_VERSION') ? (string) WPSN_VERSION : 'dev',
                'wp' => function_exists('get_bloginfo') ? (string) get_bloginfo('version') : '',
            ],
        ]);

        $resp = HttpClient::post($endpoint, [
            'headers' => $this->contract->buildHeaders('ai_credits', $payload),
            'timeout' => $timeout,
            'user-agent' => 'WPSN/' . (defined('WPSN_VERSION') ? WPSN_VERSION : 'dev'),
            'body' => $this->contract->jsonEncode($payload),
        ], [
            'service' => 'saas_ai_proxy',
            'context' => 'ai_credits',
            'retries' => 1,
        ]);

        return $this->parseResponse($resp, 'Invalid JSON response from SaaS credits endpoint.');
    }

    /**
     * Legacy fallback for older SaaS implementations that exposed GET /ai/credits.
     *
     * @return array{ok:bool,balance:float,currency:string,error:string,http_status:int,retry_after:int}
     */
    private function fetchViaLegacyGet(string $endpoint, int $timeout): array
    {
        $resp = HttpClient::get($endpoint, [
            'headers' => [
                'Accept' => 'application/json',
                'Authorization' => 'Bearer ' . $this->contract->authToken(),
                'X-WPSN-Service' => 'saas_ai_proxy',
                'X-WPSN-Context' => 'ai_credits',
            ],
            'timeout' => $timeout,
            'user-agent' => 'WPSN/' . (defined('WPSN_VERSION') ? WPSN_VERSION : 'dev'),
        ], [
            'service' => 'saas_ai_proxy',
            'context' => 'ai_credits',
            'retries' => 1,
        ]);

        return $this->parseResponse($resp, 'Invalid JSON response from SaaS credits endpoint.');
    }

    /**
     * @param array|\WP_Error $resp
     * @return array{ok:bool,balance:float,currency:string,error:string,http_status:int,retry_after:int}
     */
    private function parseResponse($resp, string $invalidJsonMessage): array
    {
        if (is_wp_error($resp)) {
            $retryAfter = 0;
            $data = $resp->get_error_data();
            if (is_array($data) && isset($data['retry_after'])) {
                $retryAfter = (int) $data['retry_after'];
            }
            return [
                'ok' => false,
                'balance' => 0.0,
                'currency' => 'credits',
                'error' => $resp->get_error_message(),
                'http_status' => 0,
                'retry_after' => $retryAfter,
            ];
        }

        $code = (int) wp_remote_retrieve_response_code($resp);
        $body = (string) wp_remote_retrieve_body($resp);
        $retryAfter = $this->extractRetryAfter($resp);

        $json = json_decode($body, true);
        if (!is_array($json)) {
            return [
                'ok' => false,
                'balance' => 0.0,
                'currency' => 'credits',
                'error' => $invalidJsonMessage,
                'http_status' => $code,
                'retry_after' => $retryAfter,
            ];
        }

        if ($retryAfter <= 0 && isset($json['retry_after'])) {
            $retryAfter = (int) $json['retry_after'];
        }

        if ($code >= 400 || (!empty($json['ok']) && $json['ok'] === false)) {
            $msg = '';
            if (isset($json['error']) && is_string($json['error'])) {
                $msg = $json['error'];
            } elseif (isset($json['message']) && is_string($json['message'])) {
                $msg = $json['message'];
            }
            if ($msg === '') {
                $msg = 'SaaS credits endpoint error (HTTP ' . $code . ').';
            }
            return [
                'ok' => false,
                'balance' => 0.0,
                'currency' => 'credits',
                'error' => $msg,
                'http_status' => $code,
                'retry_after' => $retryAfter,
            ];
        }

        $balance = 0.0;
        $currency = 'credits';
        $credits = [];

        if (isset($json['credits']) && is_array($json['credits'])) {
            $credits = $json['credits'];
        }

        if (!empty($credits)) {
            if (isset($credits['balance'])) {
                $balance = (float) $credits['balance'];
            } elseif (isset($credits['remaining'])) {
                $balance = (float) $credits['remaining'];
            }
            if (isset($credits['currency'])) {
                $currency = sanitize_text_field((string) $credits['currency']);
            }
        } else {
            if (isset($json['balance'])) {
                $balance = (float) $json['balance'];
            } elseif (isset($json['remaining'])) {
                $balance = (float) $json['remaining'];
            }
            if (isset($json['currency'])) {
                $currency = sanitize_text_field((string) $json['currency']);
            }
        }

        return [
            'ok' => true,
            'balance' => $balance,
            'currency' => $currency !== '' ? $currency : 'credits',
            'error' => '',
            'http_status' => $code,
            'retry_after' => $retryAfter,
        ];
    }

    /**
     * @param array<string,mixed> $resp
     */
    private function extractRetryAfter(array $resp): int
    {
        $retryAfter = 0;
        $respHeaders = wp_remote_retrieve_headers($resp);
        if (is_array($respHeaders)) {
            foreach ($respHeaders as $hk => $hv) {
                if (strtolower((string) $hk) === 'retry-after') {
                    if (is_array($hv)) {
                        $hv = reset($hv);
                    }
                    $retryAfter = (int) $hv;
                    break;
                }
            }
        }

        return $retryAfter;
    }
}
