<?php

namespace Devurai\AcceptbluePaymentPro\Api;

use Devurai\AcceptbluePaymentPro\Settings\Settings;
use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

class Client
{
    const METHOD_GET = 'GET';
    const METHOD_POST = 'POST';
    private string $live_endpoint = 'https://api.accept.blue/api/v2/';
    private string $test_endpoint = 'https://api.develop.accept.blue/api/v2/';
    private HttpClient $client;

    protected Settings $settings;

    private static array $instances = [];

    public function __construct(Settings $settings)
    {
        $this->settings = $settings;
        $params = array();
        if($this->settings->is_logging()){
            $params['handler'] = $this->get_request_logger();
        }
        $this->client = new HttpClient($params);
    }

    protected function __clone() { }

    /**
     * @throws \Exception
     */
    public function __wakeup()
    {
        throw new \Exception("Cannot unserialize a singleton.");
    }

    public static function getInstance(Settings $settings)
    {
        $cls = static::class;
        if (!isset(self::$instances[$cls])) {
            self::$instances[$cls] = new static($settings);
        }

        return self::$instances[$cls];
    }

    protected function request(string $method, string $endpoint, array $params = [])
    {
        try {
            $result = $this->client->request($method, $endpoint, $params);
            $content = $result->getBody()->__toString();
            return json_decode($content, true);
        } catch (GuzzleException $e) {
            if ($e->hasResponse()) {
                $response = $e->getResponse();
                $error_body = $response->getBody()->__toString();
                try{
                    $error_json = json_decode($error_body, true);
                    $message = trim($error_json['error_message'] ?? 'Unknown error from api');
                    $details = $error_json['error_details'] ?? '';
                    $details = is_array($details) ? $details['source'] : '';
                    $details = is_array($details)? $details[0] : $details;
                    if(!str_ends_with($message, '.')) $message .= '.';
                    $new_message = $message . ' ' . $details;
                    $exceptionClass = get_class($e);
                    return new $exceptionClass(
                        $new_message,
                        $e->getRequest(),
                        $e->getResponse(),
                        $e->getPrevious(),
                        $e->getHandlerContext()
                    );
                }catch (\Exception $e){
                    $exceptionClass = get_class($e);
                    return new $exceptionClass(
                        $error_body,
                        $e->getRequest(),
                        $e->getResponse(),
                        $e->getPrevious(),
                        $e->getHandlerContext()
                    );
                }
            }

            return $e;
        }
    }

    public function is_error($response): bool
    {
        $result = $response instanceof GuzzleException;
        return $result;
    }

    protected function get_aceptblue_endpoint($path): string
    {
        if($this->settings->is_test_mode()){
            return $this->test_endpoint . $path;
        }else{
            return $this->live_endpoint . $path;
        }
    }

    public function dump_result($result): void
    {
        echo '<pre>';
        var_dump($result);
        echo '</pre>';
    }

    public function get_request_logger(): HandlerStack
    {
        $logger = new Logger('guzzle_logger');
        $logger->pushHandler(new StreamHandler(pgabfw_pro_get_root() . '/guzzle.log', Logger::DEBUG));

        $formatter = new MessageFormatter("Request:\n{req_headers}\n{req_body}\n\nResponse:\n{res_headers}\n{res_body}\n");

        $logMiddleware = Middleware::log($logger, $formatter);

        $stack = HandlerStack::create();
        $stack->push($logMiddleware, 'log');
        return $stack;
    }

}