<?php

namespace classes;

use Exception;
use gatewayapi\gatewayapi_c;
use interfaces\gatewayapi_i;

require_once WD . '/modules/gatewayapi/gatewayapi_c.php';

class gatewayapi implements gatewayapi_i
{
    /**
     * Configuration of the gatewayapi module
     * @var gatewayapi_c $config
     */
    public gatewayapi_c $config;

    /**
     * API URL
     * @var string
     */
    private string $api_url = 'https://gatewayapi.eu';

    public function __construct()
    {
        $this->config = new gatewayapi_c();
    }

    /**
     * @inheritDoc
     */
    public function send(array $to, string $message, bool $ignore_empty = false): bool
    {
        $this->requireEnabled();
        // If there are no recipients, throw an exception
        if (empty($to)) {
            if ($ignore_empty) {
                return false;
            }
            throw new Exception('No recipients provided');
        }
        $data = [
            "recipients" => array_map(function ($recipient) {
                return [
                    'msisdn' => $recipient,
                ];
            }, $to),
            'message' => $message,
            'sender' => $this->config->sender->getVariableValue(),
        ];
        try {
            self::sendRequest(
                '/rest/mtsms',
                'POST',
                $data,
            );
        } catch (Exception $exception) {
            throw new Exception('Failed to send SMS: ' . $exception->getMessage());
        }
        return true;
    }

    /**
     * @inheritDoc
     */
    public function requireEnabled(): void
    {
        if (!$this->isEnabled()) {
            throw new Exception('GatewayAPI is not enabled');
        }
    }

    /**
     * @inheritDoc
     * @throws Exception If the enabled property is not set
     */
    public function isEnabled(): bool
    {
        try {
            return $this->config->enabled->isTrue();
        } catch (Exception $exception) {
            return false;
        }
    }

    /**
     * @inheritDoc
     * @throws Exception If the module is not enabled
     * @throws Exception If the API key is not set
     * @throws Exception If the sender is not set
     * @throws Exception If the API URL is not set
     * @throws Exception If the request fails
     * @throws Exception If the response is not valid JSON
     */
    public function sendRequest(string $url, string $method, array $data = [], array $headers = []): array
    {
        self::requireEnabled();
        $json_encode = json_encode($data);
        if ($json_encode === false) {
            throw new Exception('Failed to encode data to JSON: ' . json_last_error_msg());
        }
        if (empty($url) || empty($method)) {
            throw new Exception('URL and method are required');
        }
        // Add the token to the headers
        return self::curlRequest(
            $url,
            $method,
            $json_encode,
            [
                ...$headers,
                ...self::getDefaultRequestHeaders(),
            ]
        );
    }

    /**
     * @param string $endpoint The URL endpoint to send the request to (e.g. /rest/mtsms)
     * @param string $method The HTTP method to use (GET, POST, PUT, DELETE)
     * @param string $json_encode The JSON-encoded data to send
     * @param array $headers The headers to send with the request
     * @param bool $allow_fail If true, the response will be returned even if the HTTP code is not 200, otherwise an exception will be thrown
     * @throws Exception If the request fails
     * @throws Exception If the response is not valid JSON
     */
    private function curlRequest(string $endpoint, string $method, string $json_encode, array $headers, bool $allow_fail = false): array
    {
        $curl = curl_init();
        curl_setopt_array($curl, [
            CURLOPT_URL => $this->api_url . $endpoint,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_POSTFIELDS => $json_encode,
            CURLOPT_HTTPHEADER => $headers,
        ]);
        $response = curl_exec($curl);
        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);
        if ($http_code !== 200 && !$allow_fail) {
            throw new Exception('Request failed with HTTP code ' . $http_code . ': ' . $response);
        }
        if (empty($response)) {
            throw new Exception('Empty response from API');
        }
        $response = json_decode($response, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception('Failed to decode JSON response: ' . json_last_error_msg());
        }
        if (!is_array($response)) {
            throw new Exception('Invalid response format: ' . gettype($response));
        }
        // Check if the response is an array
        return $response;
    }

    /**
     * @inheritDoc
     */
    public function getDefaultRequestHeaders(): array
    {
        return [
            'Authorization: Token ' . $this->config->api_token->getVariableValue(),
            'Content-Type: application/json',
        ];
    }
}