<?php

namespace classes;

require_once WD . '/modules/virkdata/virkdata_c.php';
/** Actions */
require_once WD . '/modules/virkdata/actions/virkdata_search_a.php';

use Exception;
use modules\virkdata\helpers\virkdata_response;
use virkdata\actions\virkdata_search_a;
use virkdata\virkdata_c;
use interfaces\virkdata_i;

class virkdata implements virkdata_i
{
    /**
     * Configuration of the virkdata module
     * @var virkdata_c
     */
    public virkdata_c $config;

    /**
     * API URL
     * @var string
     */
    private string $api_url = 'https://api.virkdata.com/';

    /**
     * ACTION: VIRKDATA_SEARCH
     * @see virkdata_search_a
     * @notation This action is when a company search request is made, and logs the request in the database
     * @var virkdata_search_a $virkdata_search
     */
    private virkdata_search_a $virkdata_search;

    public function __construct()
    {
        $this->config = new virkdata_c();
        /** Actions */
        $this->virkdata_search = new virkdata_search_a();
    }

    /**
     * @inheritDoc
     * @throws Exception If the base or target currency is invalid
     * @throws Exception If the endpoint is invalid
     * @throws Exception If the response is invalid
     * @throws Exception If the response is not valid JSON
     * @throws Exception If the request fails
     */
    function getCompanyInformation(string $search, string $endpoint, array $data): \modules\virkdata\helpers\virkdata_response
    {

        // Send the request
        // Return the conversion rate
        return self::sendRequest($search, $endpoint, $data);
    }

    /**
     * @inheritDoc
     * @throws Exception If the module is not enabled, the license plate is invalid, the daily limit is exceeded, or the secret key is invalid
     */
    function sendRequest(
        string $search,
        string $endpoint,
        array  $data = [],
        string $method = 'GET'
    ): object
    {
        // Validate the module is enabled
        self::requireModuleEnabled();
        // Validate the secret key
        self::requireValidSecretKey();
        // Send the request
        $response = match ($method) {
            'GET' => self::sendGetRequest($search, $endpoint, $data),
            //'POST' => self::sendPostRequest($search, $endpoint, $data),
            //'PUT' => self::sendPutRequest($search, $endpoint, $data),
            //'DELETE' => self::sendDeleteRequest($search, $endpoint, $data),
            default => self::exception(
                $search,
                [
                    'method' => $method,
                    'endpoint' => $endpoint,
                    'data' => $data,
                    'search' => $search,
                    'response' => null,
                    'status_code' => 400,
                    'error' => 'Invalid request method',
                ],
                500
            ),
        };
        // Add the usage to the request log
        $this->virkdata_search->virkdata_search($search, $response, 200);
        // Return the response
        return $response;
    }

    /**
     * @inheritDoc
     */
    function requireModuleEnabled(): void
    {
        // Check if the module is enabled
        if (!$this->config->enabled->isTrue()) {
            throw new Exception('The virkdata module is not enabled');
        }
    }


    /**
     * @inheritDoc
     */
    function requireValidSecretKey(): void
    {
        // Check if the secret key is valid
        if ($this->config->secret_key->getVariableValue() === null) {
            self::exception(
                '',
                [
                    'status_code' => 500,
                    'error' => 'Invalid secret key defined in the config (virkdata_secret_key_c)',
                ],
                500
            );
        }
    }

    /**
     * @throws Exception
     */
    function exception(string $search, array $data, int $status_code = 500): object
    {
        // Check if the response is valid JSON
        if (isset($data['response'])) {
            if (!json_decode($data['response'])) {
                throw new Exception('Invalid response');
            }
        } else {
            isset($data['data']) ? $data['response'] = json_encode($data['data']) : $data['response'] = null;
        }
        // Add the request to the log
        $this->virkdata_search->virkdata_search($search, $data, $status_code);;
        return throw new Exception($data['error'] ?? 'An error occurred while processing the request, in ' . $this->config->getModuleName() . ' module');
    }

    /**
     * @inheritDoc
     * @throws Exception
     */
    function sendGetRequest(string $search, string $endpoint, array $data): virkdata_response
    {
        if (!empty($data)) {
            $query_string = '?' . http_build_query($data);
        } else {
            $query_string = '';
        }
        if (empty($query_string) && !empty($search)) {
            $query_string = '?search=' . urlencode($search);
        }

        $curl = curl_init();

        curl_setopt_array($curl, [
            CURLOPT_URL => "https://virkdata.dk/api/{$query_string}",
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => "GET",
            CURLOPT_HTTPHEADER => [
                "Authorization: {$this->config->secret_key->getVariableValue()}"
            ],
        ]);

        $response = curl_exec($curl);
        $err = curl_error($curl);

        curl_close($curl);

        if ($err) {
            echo "cURL Error #:" . $err;
        }

        // Get the status code
        $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);

        curl_close($curl);
        // Check if the response is valid JSON
        if (!json_decode($response)) {
            $exception_message = match ($status_code) {
                401 => 'Unauthorized',
                404 => 'Not found',
                429 => 'Too many requests',
                default => 'Invalid response',
            };
            self::exception(
                $search,
                [
                    'method' => 'GET',
                    'endpoint' => $endpoint,
                    'data' => $data,
                    'response' => null,
                    'status_code' => $status_code,
                    'error' => $exception_message,
                ],
                $status_code
            );
        }
        $decoded_response = json_decode($response, true);
        if (isset($decoded_response['error_code'])) {
            self::exception(
                $search,
                [
                    'method' => 'GET',
                    'endpoint' => $endpoint,
                    'data' => $data,
                    'response' => $response,
                    'status_code' => $status_code,
                    'error' => $response['error'] ?? 'An error occurred',
                ],
                $status_code
            );
        }
        return (new virkdata_response())->populate(json_decode($response, true));
    }

    /**
     * @throws Exception
     */
    public function searchCompanies(string $query): virkdata_response|array
    {
        return $this->sendRequest($query, 'search', [], 'GET');
    }
}