<?php

namespace Lcmigrator\LcMigrator\Backend;

use Lcmigrator\LcMigrator\Encryption\Data_Encryption;
use Lcmigrator\LcMigrator\Logger\Logger;
use Lcmigrator\LcMigrator\OptionManager\OptionManager;
use Lcmigrator\LcMigrator\Constants\Constants;
use Lcmigrator\LcMigrator\Event\EventUpdater;

class LMRequest
{
    private string $developerVersion = 'lc-migrator-v2';
    private string $version = '2021-04-15';
    private array $methods = ['GET', 'POST'];

    private OptionManager $optionManager;
    private Logger $logger;
    private Data_Encryption $dataEncryption;
    private string $oauthEndpoint = 'wordpress/lc-plugin/migrator/oauth/';
    const maxAllowedAttempts = 2;

    /**
     * Key Names Space
     */
    const oauth_authorization_code = "oauth_authorization_code";
    const oauth_access_token = Constants::oauth_access_token;
    const oauth_refresh_token = Constants::oauth_refresh_token;
    const location_id = Constants::location_id;


    public function __construct($accessToken = null)
    {
        $this->optionManager = OptionManager::getInstance();
        $this->dataEncryption = new Data_Encryption();
        $this->logger = Logger::getInstance();
        $this->developerVersion = defined('LC_MIGRATOR_DEVELOPER_VERSION') ? \LC_MIGRATOR_DEVELOPER_VERSION : 'lc-migrator';
    }

    private function encryptString($string)
    {
        return $this->dataEncryption->encrypt($string);
    }

    private function decryptString($string)
    {
        return $this->dataEncryption->decrypt($string);
    }

    private function performOauthFunctions(string $endpoint = "validate", $code = null)
    {
        $finalEndpoint = $this->oauthEndpoint . $endpoint;
        $body = array();
        if ($endpoint == "validate") {
            $body['code'] = $code;
        } else {
            $body['refresh_token'] = $this->decryptString($code);
        }

        $args = array(
            'timeout' => 60,
            'body' => json_encode($body),
            'headers' => array(
                'Content-Type' => 'application/json'
            ),
        );

        if($this->developerVersion != ''){
            $args['headers']['developer_version'] = $this->developerVersion;
        }

        $url = LC_MIGRATOR_SERVICES_BASE_URL . $finalEndpoint;
        $url = add_query_arg('lcMigratorVersion', LC_MIGRATOR_VERSION, $url);
        
        $response = wp_remote_post($url, $args);
        $http_code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);

        if ($http_code === 200 || $http_code === 201) {
            return json_decode($body, false);

        } else {
            return (object) (array(
                'error' => true,
                'http_code' => $http_code,
                'body' => $body,
            ));
        }
    }

    public function validateAndSaveOauthToken(string $code){
        if($code == null){
            return array(
                "success" => false,
                "error" => "Code is required",
            );
        }

        $isOptionsSaved = false;
        $previousOptions = $this->optionManager->all();

        $tokenResponse = $this->performOauthFunctions("validate", $code);
        if(!isset($tokenResponse->response->error)){
            if ($tokenResponse->access_token && $tokenResponse->refresh_token) {
                $this->optionManager->set(self::oauth_access_token, $this->encryptString($tokenResponse->access_token));
                $this->optionManager->set(self::oauth_refresh_token, $this->encryptString($tokenResponse->refresh_token));
                if(isset($tokenResponse->userType) && $tokenResponse->userType == 'Location'){
                    $this->optionManager->set(self::location_id, $tokenResponse->locationId);
                }
            }
            
            $isOptionsSaved = true;
            $isOptionsSaved = $this->optionManager->all();
            // Send Pendo Event
            $event = new EventUpdater('LC_MIGRATOR_OAUTH_CONNECTED', [
                'authorization_status' => 'connected',
                'user_id' => get_current_user_id(),
                'string_1' => get_site_url(),
                'string_2' => wp_get_current_user()->roles[0]
            ]);
            $event->send();
        }
        return array(
            "success" => true,
            "previous_options" => $previousOptions,
            "options_saved" => $isOptionsSaved,
        );
    }

    public function regenerateAccessToken()
    {
        $refresh_token = $this->optionManager->get(self::oauth_refresh_token);
        $refreshResponse = $this->performOauthFunctions('refresh_token', $refresh_token);
        if (!isset($refreshResponse->response->error)) {
            $this->optionManager->setMultiple(array(
                self::oauth_access_token => $this->encryptString($refreshResponse->access_token),
                self::oauth_refresh_token => $this->encryptString($refreshResponse->refresh_token)
            ));
            if (isset($refreshResponse->userType) && $refreshResponse->userType == 'Location') {
                $this->optionManager->set(self::location_id, $refreshResponse->locationId);
            }
        }
        return array(
            "success" => true,
            "token" => $refreshResponse,
        );
    }

    private function makeHeaders($additionalHeaders = array())
    {
        $headers = array(
            'version' => $this->version,
            'Accept' => 'application/json',
            'channel' => 'OAUTH',
            'content-type' => 'application/json',
            
        );

        if ($this->developerVersion != '') {
            $headers['developer_version'] = $this->developerVersion;
        }

        // Inject auth tokens
        $oauthAccessToken = $this->optionManager->get(self::oauth_access_token);
        $oauthAccessToken = $this->decryptString($oauthAccessToken);
        if ($oauthAccessToken) {
            $headers['Authorization'] = "Bearer " . $oauthAccessToken;
        }

        return array_merge($headers, $additionalHeaders); // Merges with additionalHeaders taking precedence
    }

    public function request($endpoint, $method, $params = array(), $headers = array(), $baseHost = LC_MIGRATOR_SERVICES_BASE_URL)
    {
        if (!in_array($method, $this->methods)) {
            throw new \Exception('Invalid method');
        }

        $url = $baseHost . $endpoint;
        $url = add_query_arg('lcMigratorVersion', LC_MIGRATOR_VERSION, $url);
        
        $http_code = 100;
        $attempt = 1;
        $all_attempts = array();
        $args = array();

        if ($method == 'POST') {
            $args = array(
                'timeout' => 60,
                'body' => json_encode($params),
                'headers' => $this->makeHeaders($headers),
            );
        } else {
            $args = array(
                'timeout' => 60,
                'headers' => $this->makeHeaders($headers),
            );
        }
        $this->logger->info("Headers : " . json_encode($args['headers']));

        while ($attempt <= self::maxAllowedAttempts) {
            $this->logger->info("URL: " . $url);
            if ($method === 'GET') {
                $response = wp_remote_get($url, $args);
            } else if ($method === 'POST') {
                $response = wp_remote_post($url, $args);
            } else {
                $response = wp_remote_request($url, $args);
            }
            $http_code = wp_remote_retrieve_response_code($response);
            $body = wp_remote_retrieve_body($response);

            array_push($all_attempts, array(
                'http_code' => $http_code,
                'body' => $body,
                'method' => $method,
            ));
            $this->logger->info("Response: " . $body);
            if ($http_code === 200 || $http_code === 201) {
                $response = json_decode($body, false);
                return $response;
            } else if ($http_code == 401) {
                $tokenRegenerated = $this->performOauthFunctions('refresh', $this->optionManager->get(self::oauth_refresh_token));
                if (!isset($tokenRegenerated->error) && isset($tokenRegenerated->token)) {
                    $args['headers']['Authorization'] = "Bearer " . $tokenRegenerated->token;
                } else {
                    $this->logger->error("Failed to regenerate access token");
                }
            } else {
                $this->logger->error("Entire Request Failed for URL: " . $url . " with method: " . $method);
                return (object) (array(
                    'error' => true,
                    'http_code' => $http_code,
                    'body' => $body,
                    'from' => 'request',
                ));
            }

            $attempt++;
        }
    }
}
