<?php
/**
 * Credit API Client - HTTP client for remote credit API
 *
 * Handles all HTTP communication with the remote credit system.
 * Includes retry logic, circuit breaker, and rate limiting.
 *
 * @package AIPT\Core\CreditSystem
 */

namespace AIPT\Core\CreditSystem;

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Credit API Client
 */
class CreditApiClient
{
    private const API_BASE = 'https://api.aiforwoo.com/api/v2/';
    private const TIMEOUT = 30;
    private const MAX_RETRIES = 3;

    private const CB_FAILURES_KEY = 'aipt_api_cb_failures';
    private const CB_LAST_FAILURE_KEY = 'aipt_api_cb_last_failure';
    private const CB_TTL = 600; 

    public const ERROR_NOT_REGISTERED = 'NOT_REGISTERED';

	/**
	 * Check if site has valid API credentials
	 */
	public function hasCredentials(): bool
	{
		return CreditCredentials::isRegistered();
	}

    /**
     * Sync with remote API
     *
     * @param array $usageSummary Local usage to report
     * @return array API response
     */
    public function sync(array $usageSummary = []): array
    {
        if (!$this->hasCredentials()) {
            return [
                'success' => false,
                'error' => 'Site not registered with credit system',
                'error_code' => self::ERROR_NOT_REGISTERED
            ];
        }

        $params = [
            'action' => 'sync',
            'version' => defined('AIPT_VERSION') ? AIPT_VERSION : '1.0.0',
            'current_plan' => $this->getCurrentPlan()
        ];

        if (!empty($usageSummary)) {
            $params['usage_summary'] = wp_json_encode($usageSummary);
        }

        return $this->request('ctrl', $params);
    }

    /**
     * Check balance without syncing usage
     *
     * @return array API response
     */
    public function check(): array
    {
        if (!$this->hasCredentials()) {
            return [
                'success' => false,
                'error' => 'Site not registered with credit system',
                'error_code' => self::ERROR_NOT_REGISTERED
            ];
        }

        return $this->request('ctrl', [
            'action' => 'check',
            'version' => defined('AIPT_VERSION') ? AIPT_VERSION : '1.0.0',
            'current_plan' => $this->getCurrentPlan()
        ]);
    }

    /**
     * Error code for invalid plan name
     */
    public const ERROR_INVALID_PLAN = 'invalid_plan_name';

    /**
     * Allowed plan names whitelist
     */
    private const ALLOWED_PLANS = ['free', 'pro', 'business'];

    /**
     * Immediate plan update from local Freemius SDK detection
     *
     * @param string $planName Plan name (free, pro, business)
     * @return array API response with updated balance
     */
    public function updatePlan(string $planName): array
    {
        if (!$this->hasCredentials()) {
            return [
                'success' => false,
                'error' => 'Site not registered with credit system',
                'error_code' => self::ERROR_NOT_REGISTERED
            ];
        }

        $planName = trim($planName);
        $planName = preg_replace('/[\x00-\x1F\x7F]/', '', $planName); 
        $planName = strtolower($planName);

        if (!in_array($planName, self::ALLOWED_PLANS, true)) {
            return [
                'success' => false,
                'error' => 'Invalid plan name provided',
                'error_code' => self::ERROR_INVALID_PLAN
            ];
        }

        return $this->request('ctrl', [
            'action' => 'update_plan',
            'plan_name' => $planName,
            'version' => defined('AIPT_VERSION') ? AIPT_VERSION : '1.0.0'
        ]);
    }

    /**
     * Register site with remote API
     *
     * @param array $data Site data
     * @return array Registration result
     */
    public function register(array $data): array
    {
        $data['site_url'] = \get_site_url();
        $data['site_name'] = \get_bloginfo('name');
        $data['admin_email'] = \get_option('admin_email');
        $data['version'] = defined('AIPT_VERSION') ? AIPT_VERSION : '1.0.0';

        $response = $this->request('register', $data, false);

        if (!empty($response['success'])) {
            if (isset($response['api_key'])) {
                \update_option('aipt_credit_api_key', $response['api_key'], false);
            }
            if (isset($response['secret_key'])) {
                \update_option('aipt_credit_secret_key', $response['secret_key'], false);
            }
            \update_option('aipt_credit_registered', true, false);

            $this->maybeUpdateSecretFromResponse($response);

		}

        return $response;
    }

    public function troubleshoot(string $action, array $data = []): array
    {
        $params = array_merge($data, [
            'action' => $action
        ]);

        if ($action === 'repair') {
            $params['secret_key'] = get_option('aipt_credit_secret_key', '');
        }

        return $this->request('troubleshoot', $params);
    }

    public function optOut(): array
    {
        return $this->request('opt-out', []);
    }

    /**
     * Make HTTP request with retry logic
     *
     * @param string $endpoint API endpoint
     * @param array $params Request parameters
     * @param bool $signed Whether to add authentication
     * @return array Response data
     */
    private function request(string $endpoint, array $params, bool $signed = true): array
    {

        if ($this->isCircuitOpen()) {
            return [
                'success' => false,
                'error' => 'API temporarily unavailable',
                'error_code' => 'CIRCUIT_OPEN'
            ];
        }

        if (!$this->checkRateLimit($endpoint)) {
            return [
                'success' => false,
                'error' => 'Rate limit exceeded',
                'error_code' => 'RATE_LIMITED'
            ];
        }

        if ($signed) {
            $params = $this->addAuth($params);
        }

        $lastError = 'Unknown error';
        for ($i = 0; $i < self::MAX_RETRIES; $i++) {
            $response = $this->doRequest($endpoint, $params);

            if (!empty($response['success'])) {
                $this->recordSuccess();
                $this->maybeUpdateSecretFromResponse($response);
                return $response;
            }

            $lastError = $response['error'] ?? 'Unknown error';

            if (isset($response['status']) && $response['status'] < 500) {
                break;
            }

            if ($i < self::MAX_RETRIES - 1) {
                $wait = min(pow(2, $i) * 100000, 2000000); 
                usleep($wait);
            }
        }

        $this->recordFailure();
        return [
            'success' => false,
            'error' => $lastError,
            'error_code' => 'REQUEST_FAILED'
        ];
    }

    /**
     * If server provides a new derived secret key (dual-accept migration),
     * persist it so subsequent requests use the derived auth path.
     *
     * @param array $response Parsed API response.
     * @return void
     */
    private function maybeUpdateSecretFromResponse(array $response): void
    {
        if (empty($response['new_secret_key']) || !is_string($response['new_secret_key'])) {
            return;
        }

        $new_secret_key = sanitize_text_field($response['new_secret_key']);
        if (empty($new_secret_key)) {
            return;
        }

        update_option('aipt_credit_secret_key', $new_secret_key, false);

        if (!empty($response['auth_version'])) {
            update_option('aipt_credit_auth_version', sanitize_text_field((string) $response['auth_version']), false);
        }
    }

    /**
     * Execute actual HTTP request
     *
     * @param string $endpoint API endpoint
     * @param array $params Request body
     * @return array Response
     */
    private function doRequest(string $endpoint, array $params): array
    {
        $url = self::API_BASE . $endpoint;

        $response = wp_remote_post($url, [
            'timeout' => self::TIMEOUT,
            'headers' => [
                'Content-Type' => 'application/json',
                'Accept' => 'application/json'
            ],
            'body' => wp_json_encode($params),
            'sslverify' => true
        ]);

        if (is_wp_error($response)) {
            return [
                'success' => false,
                'error' => $response->get_error_message(),
                'error_code' => 'WP_ERROR'
            ];
        }

        $code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);

        if ($code >= 400) {
            return [
                'success' => false,
                'error' => "HTTP $code",
                'status' => $code,
                'error_code' => 'HTTP_ERROR'
            ];
        }

        $data = json_decode($body, true);
        if (!is_array($data)) {
            return [
                'success' => false,
                'error' => 'Invalid JSON response',
                'error_code' => 'INVALID_JSON'
            ];
        }

        return $data;
    }

    /**
     * Add authentication to request
     *
     * @param array $params Request parameters
     * @return array Parameters with auth added
     */
    private function addAuth(array $params): array
    {
        $apiKey = get_option('aipt_credit_api_key', '');
        $secretKey = get_option('aipt_credit_secret_key', '');
        $timestamp = time();

        $params['site_url'] = \get_site_url();
        $params['api_key'] = $apiKey;
        $params['timestamp'] = $timestamp;
        $params['signature'] = hash_hmac('sha256', "$apiKey:$timestamp", $secretKey);

        return $params;
    }

    /**
     * Get current plan from Freemius SDK
     */
    private function getCurrentPlan(): string
    {
        if (function_exists('aipt_fs') && class_exists(\AIPT\Core\FsManager::class)) {
            $fsManager = \AIPT\Core\FsManager::getInstance();
            return $fsManager->getPlanType();
        }
        return 'free';
    }

    /**
     * Check if circuit breaker is open
     *
     * @return bool
     */
    private function isCircuitOpen(): bool
    {
        $failures = (int) get_transient(self::CB_FAILURES_KEY);

        if ($failures < 5) {
            return false;
        }

        $lastFailure = (int) get_transient(self::CB_LAST_FAILURE_KEY);

        if (time() - $lastFailure > 300) {
            $this->resetCircuitBreaker();
            return false;
        }

        return true;
    }

    /**
     * Record successful request - reset circuit breaker
     */
    private function recordSuccess(): void
    {
        $this->resetCircuitBreaker();
    }

    /**
     * Record failed request
     */
    private function recordFailure(): void
    {
        $failures = (int) get_transient(self::CB_FAILURES_KEY);
        set_transient(self::CB_FAILURES_KEY, $failures + 1, self::CB_TTL);
        set_transient(self::CB_LAST_FAILURE_KEY, time(), self::CB_TTL);
    }

    /**
     * Reset circuit breaker state
     */
    private function resetCircuitBreaker(): void
    {
        delete_transient(self::CB_FAILURES_KEY);
        delete_transient(self::CB_LAST_FAILURE_KEY);
    }

    /**
     * Check rate limit for endpoint
     *
     * Increments first then checks to reduce race condition window.
     *
     * @param string $endpoint Endpoint name
     * @return bool True if within limit
     */
    private function checkRateLimit(string $endpoint): bool
    {
        $key = 'aipt_rate_' . md5($endpoint);

        $newCount = ((int) get_transient($key)) + 1;
        set_transient($key, $newCount, 60);

        if ($newCount > 30) {
            return false;
        }

        return true;
    }
}
