<?php

namespace Alsendo\AlsendoWrapper\Api\Ecolet;

use Alsendo\AlsendoWrapper\Api\Ecolet\Model\EcoletServiceStructure;
use Alsendo\AlsendoWrapper\Api\Ecolet\Model\Order\Created\EcoletOrderCreatedResponse;
use Alsendo\AlsendoWrapper\Api\Ecolet\Model\Order\EcoletOrderResponse;
use Alsendo\AlsendoWrapper\Api\Ecolet\Model\Order\EcoletOrderSendResponse;
use Alsendo\AlsendoWrapper\Api\Ecolet\Model\Order\EcoletOrderValuation;
use Alsendo\AlsendoWrapper\Api\Ecolet\Wrapper\OrderRequestWrapper;
use Alsendo\AlsendoWrapper\Api\Ecolet\Wrapper\OrderResponseWrapper;
use Alsendo\AlsendoWrapper\ApiClient;
use Alsendo\AlsendoWrapper\ApiClientInterface;
use Alsendo\AlsendoWrapper\Exception\UnsupportedEndpointException;
use Alsendo\AlsendoWrapper\Exception\ValidationException;
use Alsendo\AlsendoWrapper\Json;
use Alsendo\AlsendoWrapper\Model\Location\LocationQueryRequest;
use Alsendo\AlsendoWrapper\Model\Order\OrderInterface;
use Alsendo\AlsendoWrapper\Model\Order\OrderRequest;
use Alsendo\AlsendoWrapper\Model\Order\OrderResponse;
use Alsendo\AlsendoWrapper\Validator;
use GuzzleHttp\Exception\GuzzleException;
use JsonException;
use ReflectionException;

class ApiEcoletClient extends ApiClient implements ApiClientInterface
{
    public const API_URL = 'https://panel.ecolet.ro/';
    public const API_URL_TEST = 'https://staging.ecolet.ro/';

    public const CONFIG_REQUIRES = [
        'token',
    ];

    public function __construct(array $config, bool $test = false)
    {
        parent::__construct(($test) ? self::API_URL_TEST : self::API_URL, $config);
        $this->authorization();
    }

    private function authorization(): void
    {
        $this->options = [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->config['token'],
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
            ],
            'debug' => false
        ];
    }

    /**
     * Retrieves the current authenticated user's information.
     *
     * Makes a GET request to the '/api/v1/me' endpoint to fetch the user details.
     *
     * @return array The user data as an associative array
     *
     * @throws GuzzleException If the HTTP request fails
     * @throws JsonException If the response JSON cannot be parsed
     */
    public function getMe()
    {
        $endpoint = '/api/v1/me';

        $response = $this->makeRequest('GET', $endpoint);

        return $this->parseResponse($response);
    }

    /**
     * Retrieves the service structure from the API endpoint.
     *
     * @return EcoletServiceStructure The parsed service structure data from the API response
     * @throws GuzzleException
     * @throws JsonException
     */
    public function getServiceStructure()
    {
        $endpoint = '/api/v1/services';

        $response = $this->makeRequest('GET', $endpoint);

        $response = $this->parseResponse($response);

        return Json::mapArrayToObject($response, EcoletServiceStructure::class);
    }

    /**
     * Retrieves the valuation details for an order based on available service slugs and their associated pricing
     * and status information.
     *
     * @param OrderRequest $order The order request containing the necessary data to determine valuation.
     * @return array An associative array where each key is a service slug and each value is an instance of
     * EcoletOrderValuation representing the valuation for that service.
     * @throws ValidationException If the order request fails validation.
     * @throws ReflectionException If there is an issue with reflection operations during processing.
     * @throws GuzzleException If the HTTP request to the valuation endpoint fails.
     * @throws JsonException If there is an error during JSON parsing or mapping.
     */
    public function getOrderValuation(OrderRequest $order): array
    {
        $endpoint = '/api/v2/add-parcel/reload-form';

        $validator = new Validator(ValidationRules::ORDER_CREATE);
        $validator->validate($order);

        if ($validator->hasErrors()) {
            throw new ValidationException($validator->getErrors());
        }

        $ecoletOrderRequest = OrderRequestWrapper::wrap($order);

        $json = $ecoletOrderRequest->toArray();
        $debug_place =  __METHOD__ . ': ' . __LINE__;
		$this->ecolet_log_for_woocommerce( $debug_place, $json );

        $response = $this->makeRequest('POST', $endpoint, [
            'json' => $json,
        ]);

        $response = $this->parseResponse($response);

        $debug_place =  __METHOD__ . ': ' . __LINE__;
        $this->ecolet_log_for_woocommerce( $debug_place, $response );

        $form = $response['form'];


        $slugs = array_keys($form['statuses'] ?? []);

        $orderValuations = [];

        foreach ($slugs as $slug) {
            $mappedData = [
                'service_id' => $slug,
                'status' => $form['statuses'][$slug] ?? false,
                'additional_services' => $form['additional_services'][$slug] ?? [],
                'pickup_dates' => $form['pickup_dates'][$slug] ?? [],
                'is_standard' => $form['is_standard'][$slug] ?? false,
                'price' => [
                    'net' => $form['prices_net'][$slug] ?? '0,00',
                    'gross' => $form['prices_gross'][$slug] ?? '0,00'
                ],
                'fees' => $form['fees'][$slug] ?? [],
                'vat' => $form['vat'] ?? null,
                'info' => $form['info'][$slug] ?? [],
                'errors' => $form['errors'][$slug] ?? [],
            ];
            $orderValuations[$slug] = Json::mapArrayToObject($mappedData, EcoletOrderValuation::class);
        }

        return $orderValuations;
    }

    /**
     * Sends an order to the Ecolet system and returns the created order response.
     *
     * This method validates the provided order request, sends it to the Ecolet API,
     * waits for the order to be processed, and returns the resulting order response.
     *
     * @param OrderRequest $order The order requests to be sent to the Ecolet system.
     * @return EcoletOrderCreatedResponse The response object representing the created order.
     * @throws GuzzleException
     * @throws JsonException
     * @throws ReflectionException
     * @throws ValidationException
     */
    public function sendOrder(OrderRequest $order)
    {
        $endpoint = '/api/v2/add-parcel/send-order';

        $validator = new Validator(ValidationRules::ORDER_CREATE);
        $validator->validate($order);

        if ($validator->hasErrors()) {
            throw new ValidationException($validator->getErrors());
        }

        $ecoletOrderRequest = OrderRequestWrapper::wrap($order);

        $json = $ecoletOrderRequest->toArray();
        $debug_place =  __METHOD__ . ': ' . __LINE__;
        $this->ecolet_log_for_woocommerce( $debug_place, $json );

        $response = $this->makeRequest('POST', $endpoint, [
            'json' => $json,
        ]);

        $response = $this->parseResponse($response);

        $debug_place =  __METHOD__ . ': ' . __LINE__;
        $this->ecolet_log_for_woocommerce( $debug_place, $response );

        $orderToSendId = $response['order_to_send_id'];

        sleep(6);

        $endpoint = '/api/v1/order-to-send/' . $orderToSendId;

        $response = $this->makeRequest('GET', $endpoint);

        $response = $this->parseResponse($response);

        $debug_place =  __METHOD__ . ': ' . __LINE__;
        $this->ecolet_log_for_woocommerce( $debug_place, $response );

        $decodedJson = $response['order_to_send'];

        $parsedOrder = OrderResponseWrapper::wrap($decodedJson);

        $error_details = '';
        if( ! empty( $response['order_to_send']['error'] ) ) {
            $error_details = $response['order_to_send']['error'];
        }


        $result = Json::mapArrayToObject($parsedOrder, EcoletOrderCreatedResponse::class);

        if( ! empty( $error_details ) ) {
            $result->api_error = $error_details;
        }

        return $result;

    }

    /**
     * Retrieves an order by its unique identifier from the system.
     *
     * This method constructs a GET request to fetch an order using the provided order ID,
     * parses the response, and returns an order response object.
     *
     * @param string $orderId The unique identifier of the order to retrieve.
     * @return OrderInterface The order interface object representing the retrieved order.
     * @throws GuzzleException
     * @throws JsonException
     */
    public function getOrder($orderId): OrderInterface
    {
        $endpoint = '/api/v1/order/' . $orderId;

        $response = $this->makeRequest('GET', $endpoint );

        $response = $this->parseResponse($response);

        $response = Json::mapArrayToObject( $response['data'], EcoletOrderResponse::class );

        $debug_place =  __METHOD__ . ': ' . __LINE__;
        $this->ecolet_log_for_woocommerce( $debug_place, $response );

        return $response;
    }

    /**
     * Retrieves the waybill for a given order ID.
     *
     * This method constructs the endpoint for downloading a waybill based on the provided order ID
     * and makes a GET request to fetch the waybill data.
     *
     * @param string $orderId The unique identifier of the order for which to retrieve the waybill.
     * @return string The waybill data as a string.
     * @throws GuzzleException If the HTTP request fails or the response is invalid.
     */
    public function getWayBill($orderId)
    {
        $endpoint = '/api/v1/order/' . $orderId . '/download-waybill';

        return $this->makeRequest('GET', $endpoint);
    }

    /**
     * Cancels an order by its ID using the Ecolet API.
     *
     * This method sends a DELETE request to the specified order endpoint to cancel the order.
     *
     * @param string $orderId The unique identifier of the order to be canceled.
     * @return bool True if the cancellation request was successfully sent, false otherwise.
     * @throws GuzzleException
     */
    public function cancelOrder($orderId)
    {
        $endpoint = '/api/v1/order/' . $orderId;

        $response = $this->makeRequest('DELETE', $endpoint . '/');
        return $this->parseResponse($response);
    }

    /**
     * Retrieves a list of turn-in records for the provided order IDs.
     *
     * This method constructs a request to fetch turn-in data for the specified order IDs.
     * It currently throws an exception as the endpoint is not supported.
     *
     * @param array $orderIds A list of order IDs for which turn-in records are requested.
     * @throws UnsupportedEndpointException When attempting to access the turn_in_list endpoint.
     */
    public function getTurnInList(array $orderIds)
    {
        throw new UnsupportedEndpointException('turn_in_list');
    }

    /**
     * Retrieves a list of contacts from the system.
     *
     * This method is not supported and will throw an UnsupportedEndpointException.
     *
     * @throws UnsupportedEndpointException When attempting to access the contact list endpoint.
     */
    public function getContactList()
    {
        throw new UnsupportedEndpointException('contact_list');
    }

    /**
     * Parses a JSON response string into a PHP array.
     *
     * Converts a JSON-formatted response string into a associative array using JSON decoding.
     * The decoding process throws an exception if the JSON is invalid or malformed.
     *
     * @param string $response The JSON-formatted response string to parse.
     * @return array The parsed response as an associative array.
     * @throws JsonException If the JSON string is invalid or cannot be decoded.
     */
    private function parseResponse(string $response): array
    {
        return json_decode($response, true, 10, JSON_THROW_ON_ERROR);
    }

    /**
     * Retrieves order details for the specified order IDs.
     *
     * This method constructs a request to fetch order information from the system
     * using the provided order IDs. It returns an array of order response objects
     * corresponding to the requested orders.
     *
     * @param array $orderIds A list of order IDs to retrieve details for.
     * @return array An array of order response objects representing the fetched orders.
     * @throws UnsupportedEndpointException When the endpoint for retrieving orders is not supported.
     */
    public function getOrders(array $orderIds): array
    {
        throw new UnsupportedEndpointException('get_orders');
    }

    /**
     * Retrieves a list of locations based on a country code and city.
     *
     * This method constructs a GET request to the '/locations/{country_code}/localities/{city}' endpoint
     * to fetch a list of locations based on the provided country code and city.
     *
     * @param LocationQueryRequest $locationQueryRequest The request object containing the country code and city.
     * @return array A list of locations as an associative array.
     * @throws GuzzleException
     * @throws JsonException
     * @throws ReflectionException
     * @throws ValidationException If the request object fails validation.
     */
    public function queryLocation(LocationQueryRequest $locationQueryRequest): array
    {
        $validator = new Validator(ValidationRules::LOCATION_QUERY);
        $validator->validate($locationQueryRequest);

        if ($validator->hasErrors()) {
            throw new ValidationException($validator->getErrors());
        }

        $endpoint = '/api/v1/locations/'.$locationQueryRequest->countryCode.'/localities/'.$locationQueryRequest->city;

        $response = $this->makeRequest('GET', $endpoint );

        return $this->parseResponse($response);
    }


    public function ecolet_log_for_woocommerce( $debug_place, $data )
    {

        if ( function_exists( 'wc_get_logger' ) ) {

            $order_id = ! empty( $_POST['order_id'] ) && is_numeric( $_POST['order_id'] ) ? sanitize_text_field(wp_unslash($_POST['order_id'])) : '';

            $logger = wc_get_logger();
            $logger->log(
                'debug',
                'ECOLET: for order #: ' . $order_id,
                array(
                    'source'            => 'alsendo-connect-order-' . $order_id,
                    'order_id'          => $order_id,
                    'debug_place'  => $debug_place,
                    'data' => $data,
                )
            );
        }
    }
}