<?php

namespace OtomaticAi\Utils;

use Exception;
use OtomaticAi\Api\OtomaticAi\Client;
use OtomaticAi\Api\OtomaticAi\Exceptions\UnauthorizedException;
use OtomaticAi\Vendors\Carbon\Carbon;
use OtomaticAi\Vendors\Illuminate\Support\Arr;
use OtomaticAi\Vendors\Illuminate\Support\Str;
use WP_REST_Request;

class Auth
{
    static private $token = null;

    const TOKEN_OPTION_NAME = "otomatic_ai_token";
    const TOKEN_LAST_CHECK_OPTION_NAME = "otomatic_ai_token_last_check";
    const TOKEN_CHECK_INTERVAL = 5 * MINUTE_IN_SECONDS;

    static public function login($email, $password)
    {
        $api = new Client;
        $response = $api->login($email, $password, get_site_url(), defined('OTOMATIC_AI_VERSION') ? OTOMATIC_AI_VERSION : null);

        $token = Arr::get($response, "token", null);
        if (!empty($token) && function_exists('update_option') && update_option(self::TOKEN_OPTION_NAME, $token, false)) {

            // update the last check
            update_option(self::TOKEN_LAST_CHECK_OPTION_NAME, time(), false);

            // sync the data
            DataSync::updateData();

            return true;
        }

        throw new Exception("Unable to store the auth token.");
    }

    static public function loginWithAccessToken($accessToken)
    {
        $api = new Client;
        $response = $api->loginWithAccessToken($accessToken, get_site_url(), defined('OTOMATIC_AI_VERSION') ? OTOMATIC_AI_VERSION : null);

        $token = Arr::get($response, "token", null);
        if (!empty($token) && function_exists('update_option') && update_option(self::TOKEN_OPTION_NAME, $token, false)) {

            // update the last check
            update_option(self::TOKEN_LAST_CHECK_OPTION_NAME, time(), false);

            // sync the data
            DataSync::updateData();

            return true;
        }

        throw new Exception("Unable to store the auth token.");
    }

    static public function logout()
    {
        // delete the token
        delete_option(self::TOKEN_OPTION_NAME);

        // delete the last check
        delete_option(self::TOKEN_LAST_CHECK_OPTION_NAME);

        // remove the data
        DataSync::removeData();
    }

    static public function token()
    {
        if (self::$token === null && function_exists('get_option')) {
            self::$token = get_option(self::TOKEN_OPTION_NAME, null);

            if (empty(self::$token)) {
                return null;
            }

            if (self::shouldRecheckToken()) {
                if (!self::verifyToken(self::$token)) {

                    self::logout();

                    return null;
                }
            }
        }

        return self::$token;
    }

    private static function shouldRecheckToken()
    {
        $lastCheck = get_option(self::TOKEN_LAST_CHECK_OPTION_NAME);
        return !$lastCheck || (time() - $lastCheck) > self::TOKEN_CHECK_INTERVAL;
    }

    private static function verifyToken($token)
    {
        try {
            $api = new Client;
            $response = $api->validate($token, get_site_url(), defined('OTOMATIC_AI_VERSION') ? OTOMATIC_AI_VERSION : null);

            // verify if the token is valid and if it's different from the current token
            $newToken = Arr::get($response, "token", null);
            if (!empty($newToken) && $token !== $newToken && function_exists('update_option')) {

                // update the token
                update_option(self::TOKEN_OPTION_NAME, $newToken, false);
                self::$token = $newToken;

                // sync the data
                DataSync::updateData();
            }

            update_option(self::TOKEN_LAST_CHECK_OPTION_NAME, time());
            return true;
        } catch (UnauthorizedException $e) {

            update_option(self::TOKEN_LAST_CHECK_OPTION_NAME, time());

            return false;
        } catch (Exception $e) {
        }

        return true;
    }

    static public function check()
    {
        return self::token() !== null;
    }

    static public function domain()
    {
        return DataSync::getData("domain");
    }

    static public function user()
    {
        return DataSync::getData("user");
    }

    static public function validateOpenAISignature(string $key, bool $force = false)
    {
        $key = Str::substr($key, -16);
        $api = new Client;
        $response = $api->validateOpenAISignature($key, $force);

        return $response;
    }

    static public function isPremium(): bool
    {
        try {
            $user = self::user();
            $hasSubscription = Arr::get($user, "has_subscription", true);
            return $hasSubscription && Arr::get(self::domain(), "is_premium", false);
        } catch (Exception $e) {
            return false;
        }
    }

    static public function isTrial(): bool
    {
        return false;
    }

    static public function buildAuthorizeUrl(): string
    {
        $client_id    = defined('OTOMATIC_AI_OAUTH_CLIENT_ID') ? OTOMATIC_AI_OAUTH_CLIENT_ID : '';
        $url = defined('OTOMATIC_AI_OAUTH_AUTHORIZE_URI') ? OTOMATIC_AI_OAUTH_AUTHORIZE_URI : '#';
        $redirect_uri = defined('OTOMATIC_AI_OAUTH_REDIRECT_URI') ? OTOMATIC_AI_OAUTH_REDIRECT_URI : '#';
        $scope        = '*'; // à définir côté SaaS
        $state        = Support::randomBase64url(24);
        $verifier     = Support::randomBase64url(64);
        $challenge    = Support::pkceChallenge($verifier);

        set_transient("otomatic_pkce_$state", [
            'code_verifier' => $verifier,
            'created_at'    => time(),
        ], 10 * MINUTE_IN_SECONDS);

        $query = http_build_query([
            'response_type'  => 'code',
            'client_id'      => $client_id,
            'redirect_uri'   => $redirect_uri,
            'scope'          => $scope,
            'state'          => $state,
            'code_challenge' => $challenge,
            'code_challenge_method' => 'S256',
            'prompt' => 'consent', // consent
        ]);

        return $url . "?$query";
    }

    static public function authorizeRestApi(WP_REST_Request $request): bool
    {
        $timestamp    = $request->get_header('X-TIMESTAMP');
        $receivedHash = $request->get_header('X-BODY-HASH');
        $signature    = $request->get_header('X-SIGNATURE');

        // 1. Check that the timestamp is not too old (anti-replay)
        if (!$timestamp || abs(time() - strtotime($timestamp)) > 300) {
            return false;
        }

        // 2. Get the body of the request
        $rawBody = $request->get_body();

        // 3. If a hash is provided, we must check the body
        if (!empty($receivedHash)) {
            $calculatedHash = hash('sha256', $rawBody);

            if ($receivedHash !== $calculatedHash) {
                return false;
            }

            $message = $timestamp . $receivedHash;
        } else {
            // No hash provided → the body must be empty
            if (strlen(trim($rawBody)) > 0) {
                return false;
            }

            $message = $timestamp;
        }

        // 4. Get the public key of the SaaS
        $publicKey = DataSync::getData('domain.rsa_key', null);
        if (!$publicKey) {
            return false;
        }

        // 5. Check the RSA signature
        $decodedSignature = base64_decode($signature);
        $pubKeyRes = openssl_pkey_get_public($publicKey);

        if (!$pubKeyRes) {
            return false;
        }

        $verified = openssl_verify($message, $decodedSignature, $pubKeyRes, OPENSSL_ALGO_SHA256);
        openssl_free_key($pubKeyRes);

        return $verified === 1;
    }
}
