<?php
/**
 * @author 1902 Software
 * @copyright Copyright © 2023 1902 Software (https://1902software.com/magento/)
 * @package WriteTextAI_WriteTextAI
 */

namespace WriteTextAI\WriteTextAI\Model\Api;

use Magento\Framework\HTTP\Client\Curl as MagentoCurl;
use WriteTextAI\WriteTextAI\Exception\ApiException;
use WriteTextAI\WriteTextAI\Exception\UnauthorizedException;
use WriteTextAI\WriteTextAI\Logger\Logger;

class StatusCodeValidator
{
    public const UNAUTHORIZED = 401;

    /**
     * @var Logger
     */
    protected $logger;

    /**
     * StatusCodeValidator constructor.
     *
     * @param Logger $logger
     */
    public function __construct(
        Logger $logger
    ) {
        $this->logger = $logger;
    }

    /**
     * Validate the status code of the response
     *
     * @param MagentoCurl $curl
     * @param string $request
     * @param array $body
     *
     * @return bool
     */
    public function validate(MagentoCurl $curl, $request, $body = []): bool
    {
        if (!$this->isSuccess($curl->getStatus())
            && !$this->isContinue($curl->getStatus())
        ) {
            $errMsg = $this->getErrorMessage($curl->getBody());
            $status = $curl->getStatus();

            if ($status === self::UNAUTHORIZED) {
                $exceptionMessage = sprintf('[%s] %s', $status, $errMsg);
                $this->logger->logResponseErrors($exceptionMessage, $request, $body);
                throw new UnauthorizedException(__($errMsg));
            } elseif ($this->isClientError($status)) {
                if ($this->isPayloadTooLarge($status)) {
                    $exceptionMessage = __('You have exceeded the maximum allowed payload. This could be'.
                    ' due to the number of product data you have selected or the limited capacity of your'.
                    ' server. Please reduce the number of products for the bulk processing and try again.');
                } else {
                    $exceptionMessage = sprintf('[%s] Request issue: %s', $status, $errMsg);
                    $this->handleApiErrorCodes($curl->getBody(), $errMsg, $status);
                }
            } elseif ($this->isServerError($status)) {
                $exceptionMessage = sprintf('[%s] Server error: %s', $status, $errMsg);
            } else {
                $exceptionMessage = sprintf('[%s] There was an issue processing the request: %s', $status, $errMsg);
            }

            $this->logger->logResponseErrors($exceptionMessage, $request, $body);
            throw new ApiException(__($errMsg), null, $status);
        }

        return true;
    }

    /**
     * Handle API error codes
     *
     * @param string $curlBody
     * @param string $message
     * @param int $status
     *
     * @return bool
     */
    protected function handleApiErrorCodes($curlBody, $message, $status)
    {
        $body = json_decode($curlBody, true);

        if (!isset($body['code']) && !isset($body['status_code'])) {
            return;
        }

        $code = $body['code'] ?? $body['status_code'];

        switch ($code) {
            case 21:
                throw new \WriteTextAI\WriteTextAI\Exception\TermsException(__($message), null, $status);
            case 28:
                throw new \WriteTextAI\WriteTextAI\Exception\ImageDownloadFailed(__($message), null, $status);
            case 29:
                throw new \WriteTextAI\WriteTextAI\Exception\ImageUnsupported(__($message), null, $status);
            case 30:
                throw new \WriteTextAI\WriteTextAI\Exception\ImageInvalid(__($message), null, $status);
            case 40202:
            case 40209:
            case 50301:
            case 50401:
                throw new \WriteTextAI\WriteTextAI\Exception\TooManyRequests(
                    __('The server is currently handling a lot of requests. Please retry after a few minutes.'),
                    null,
                    $status
                );
            default:
                return;
        }
    }

    /**
     * Check if the response is ongoing
     *
     * @param int $code
     *
     * @return bool
     */
    protected function isContinue($code): bool
    {
        return (100 <= $code && 200 > $code);
    }
    /**
     * Check if the response is successful
     *
     * @param int $code
     *
     * @return bool
     */
    protected function isSuccess($code): bool
    {
        return (200 <= $code && 300 > $code);
    }

    /**
     * Check if the response is a client error
     *
     * @param int $code
     *
     * @return bool
     */
    protected function isClientError($code): bool
    {
        return ($code >= 400 && $code < 500);
    }

    /**
     * Check if the response is a server error
     *
     * @param int $code
     *
     * @return bool
     */
    protected function isServerError($code): bool
    {
        return $code >= 500;
    }

    /**
     * Check if error is payload too large
     *
     * @param int $code
     *
     * @return bool
     */
    protected function isPayloadTooLarge($code): bool
    {
        return $code === 413;
    }

    /**
     * Get the error message from the response
     *
     * @param string $curlBody
     *
     * @return string
     */
    protected function getErrorMessage($curlBody = '')
    {
        try {
            $unserializedMessage = json_decode($curlBody, true);
            $error = $unserializedMessage['errors'] ?? $unserializedMessage['error'] ?? $curlBody;

            if (is_array($error) || is_object($error)) {
                $error = json_encode($error);
            }

            return $error;
        } catch (\InvalidArgumentException $e) {
            return $curlBody;
        }
    }
}
