<?php

namespace inpost_shipping;

use GuzzleHttp;

class InpostApi
{
    protected const LIVE_BASE_URI = 'https://api-shipx-pl.easypack24.net';
    protected const SBX_BASE_URI = 'https://sandbox-api-shipx-pl.easypack24.net';
    protected const LIVE_GEOWIDGET_URI = 'https://geowidget.inpost.pl';
    protected const SBX_GEOWIDGET_URI = 'https://sandbox-easy-geowidget-sdk.easypack24.net';

    protected $baseUri;
    protected $geoWidgetBaseUri;

    protected $apiKey;
    protected $idOrganization;
    protected $geoWidgetKey;

    protected $guzzleClient;
    protected $inpostFactory;

    /**
     * @param array $params
     */
    public function __construct(array $params = [])
    {
        $this->inpostFactory = new InpostFactory();
        $this->baseUri = isset($params['sandboxMode']) && $params['sandboxMode'] ? static::SBX_BASE_URI : static::LIVE_BASE_URI;
        $this->apiKey = $params['apiKey'];
        $this->idOrganization = $params['idOrganization'];
        $this->geoWidgetKey = $params['geoWidgetKey'];
        $this->geoWidgetBaseUri = isset($params['sandboxMode']) && $params['sandboxMode'] ? static::SBX_GEOWIDGET_URI : static::LIVE_GEOWIDGET_URI;
        $this->guzzleClient = new GuzzleHttp\Client([
            'headers' => [
                'Authorization' => 'Bearer ' . $this->apiKey,
                'Content-Type' => 'application/json',
                'Accept' => '*/*',
            ]
        ]);
    }

    /**
     * @param $params
     *
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function validateApiKeys($params): bool
    {
        if (!empty($params['sandbox']['key']) && !empty($params['sandbox']['idOrganization'])) {
            try {
                $response = $this->guzzleClient->request(
                    'GET',
                    static::SBX_BASE_URI . '/v1/organizations/' . $params['sandbox']['idOrganization'],
                    [
                        'headers' =>
                            [
                                'Authorization' => 'Bearer ' . $params['sandbox']['key'],
                                'Content-Type' => 'application/json',
                                'Accept' => '*/*',
                            ],
                    ]
                );
            } catch (GuzzleHttp\Exception\BadResponseException $badResponseException) {
                return false;
            }
        }

        if (!empty($params['live']['key']) && !empty($params['live']['idOrganization'])) {
            try {
                $response = $this->guzzleClient->request(
                    'GET',
                    static::LIVE_BASE_URI . '/v1/organizations/' . $params['live']['idOrganization'],
                    [
                        'headers' =>
                            [
                                'Authorization' => 'Bearer ' . $params['live']['key'],
                                'Content-Type' => 'application/json',
                                'Accept' => '*/*',
                            ],
                    ]
                );
            } catch (GuzzleHttp\Exception\BadResponseException $badResponseException) {
                return false;
            }
        }

        return true;
    }

    /**
     * @return array
     *
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getOrganizationDetails(): array
    {
        $response = $this->guzzleClient->request(
            'GET',
            $this->baseUri . '/v1/organizations/' . $this->idOrganization,
            [
                'headers' =>
                    [
                        'Authorization' => 'Bearer ' . $this->apiKey,
                        'Content-Type' => 'application/json',
                        'Accept' => '*/*',
                    ],
            ]
        );

        return $this->convertJsonToArray($response->getBody());
    }

    /**
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getServices(): array
    {
        $response = $this->guzzleClient->request(
            'GET',
            $this->baseUri . '/v1/services/',
            [
                'headers' =>
                    [
                        'Authorization' => 'Bearer ' . $this->apiKey,
                        'Content-Type' => 'application/json',
                        'Accept' => '*/*',
                    ],
            ]
        );

        $deliveryServicesResponse = $this->convertJsonToArray($response->getBody());

        $deliveryServices = [];
        foreach ($deliveryServicesResponse as $deliveryService) {
            if (in_array($deliveryService['id'], InpostConfig::DISABLED_SERVICES)) {
                continue;
            }

            $deliveryServices[$deliveryService['id']] = $deliveryService;
        }

        return $this->inpostFactory
            ->createInpostShippingMethodExpander()
            ->expandDeliveryServices($deliveryServices);
    }

    public function getShippingMethods(): array
    {
        $response = $this->guzzleClient->request(
            'GET',
            $this->baseUri . '/v1/sending_methods/',
            [
                'headers' =>
                    [
                        'Authorization' => 'Bearer ' . $this->apiKey,
                        'Content-Type' => 'application/json',
                        'Accept' => '*/*',
                    ],
            ]
        );

        $shippingMethodsResponse = $this->convertJsonToArray($response->getBody());

        $shippingMethods = [];
        foreach ($shippingMethodsResponse['items'] as $shippingMethod) {
            $shippingMethods[$shippingMethod['id']] = $shippingMethod;
        }

        return $shippingMethods;
    }

    /**
     * @return string
     */
    public function getGeoWidgetScript(): string
    {
        return $this->geoWidgetBaseUri . '/inpost-geowidget.js';
    }

    /**
     * @return string
     */
    public function getGeoWidgetStylesheet(): string
    {
        return $this->geoWidgetBaseUri . '/inpost-geowidget.css';
    }

    /**
     * @param string $json
     *
     * @return array
     */
    protected function convertJsonToArray(string $json): array
    {
        return json_decode($json, 1);
    }

    /**
     * @param array $address
     * @param $ocRegistry
     *
     * @return array
     */
    public function getQuotes(array $address, $ocRegistry): array
    {
        return $this->inpostFactory
            ->createQuoteHandler()
            ->getQuotes($address, $ocRegistry);
    }
//                echo "<pre>"; print_r($quote_data); echo "</pre>";
//                echo "<pre>"; print_r($quotes); echo "</pre>";
    /**
     * @param array $request
     *
     * @return array
     */
    public function transformShippingMethodRequest(array $request): array
    {
        return $this->inpostFactory
            ->createInpostShippingMethodExpander()
            ->transformRequest($request);
    }

    /**
     * @param array $request
     *
     * @return array
     */
    public function transformDeliveryServiceRequest(array $request): array
    {
        return $this->inpostFactory
            ->createInpostDeliveryServiceExpander()
            ->transformRequest($request);
    }

    /**
     * @return array
     */
    public function createShipment(array $request, $ocRegistry): array
    {
        return $this->inpostFactory
            ->createInpostShipmentHandler($this->guzzleClient, $this->idOrganization, $this->baseUri, $this->apiKey, $ocRegistry)
            ->createShipment($request, $ocRegistry);
    }

    /**
     * @param array $request
     * @param $ocRegistry
     *
     * @return string
     */
    public function validateShippingMethodSaveRequest(array $request, $ocRegistry): string
    {
        return $this->inpostFactory
            ->createInpostRequestValidator()
            ->validateShippingMethodSaveRequest($request, $ocRegistry);
    }

    /**
     * @param array $labelParams
     *
     * @return array
     */
    public function printLabel(array $labelParams): array
    {
        return $this->inpostFactory
            ->createInpostPrinter($this->guzzleClient, $this->idOrganization, $this->baseUri, $this->apiKey)
            ->print($labelParams);
    }

    /**
     * @param string $idDispatch
     *
     * @return array
     */
    public function printDispatchLabel(string $idDispatch): array
    {
        return $this->inpostFactory
            ->createInpostPrinter($this->guzzleClient, $this->idOrganization, $this->baseUri, $this->apiKey)
            ->printDispatch($idDispatch);
    }

    /**
     * @param string $idDispatch
     *
     * @return array
     */
    public function printBulkDispatchLabel(array $idsShipment): array
    {
        return $this->inpostFactory
            ->createInpostPrinter($this->guzzleClient, $this->idOrganization, $this->baseUri, $this->apiKey)
            ->printBulkDispatch($idsShipment);
    }

    /**
     * @param array $request
     * @param $ocRegistry
     *
     * @return array
     */
    public function dispatchOrder(array $request, $ocRegistry): array
    {
        return $this->inpostFactory
            ->createInpostDispatchHandler($this->guzzleClient, $this->idOrganization, $this->baseUri, $this->apiKey, $ocRegistry)
            ->dispatchOrder($request);
    }

    /**
     * @return array
     */
    public function getStatuses(): array
    {
        $response = $this->guzzleClient->request(
            'GET',
            $this->baseUri . '/v1/statuses',
        );

        return $this->convertJsonToArray($response->getBody());
    }

    /**
     * @param array $data
     * @param $ocRegistry
     */
    public function updateShipmentStatus(array $data, $ocRegistry): void
    {
        $ocShipmentId = $ocRegistry->db->query("SELECT * FROM `".DB_PREFIX."inpost_shipping_shipments` WHERE idInpost = '".$ocRegistry->db->escape($data['payload']['shipment_id'])."'")->row['id_shipment'];

        $sql = "UPDATE `".DB_PREFIX."inpost_shipping_shipments` SET 
        `status` = '".str_replace('shipment_', '', $data['event'])."',
        `updated_at` = '".date('Y-m-d H:i:s', strtotime($data['event_ts']))."',
        `tracking_number` = '".$ocRegistry->db->escape($data['payload']['tracking_number'])."' 
        WHERE id_shipment = '".(int)$ocShipmentId."'";

        $ocRegistry->db->query($sql);
    }

    public function syncPickupPoints($ocRegistry): array
    {
        return $this->inpostFactory
            ->createInpostPickupPoints($this->guzzleClient, $this->idOrganization, $this->baseUri, $this->apiKey, $ocRegistry)
            ->syncPickupPoints();
    }

    /**
     * @param string $id
     * @param $ocRegistry
     * @param int $retryCount
     *
     * @return array
     */
    public function syncTrackingNumber(string $id, $ocRegistry, int $retryCount): array
    {
        return $this->inpostFactory
            ->createInpostShipmentHandler($this->guzzleClient, $this->idOrganization, $this->baseUri, $this->apiKey, $ocRegistry)
            ->syncTrackingNumber($id, $retryCount);
    }

    /**
     * @param int $sbxMode
     * @param $ocRegistry
     *
     * @return array
     */
    public function syncShipmentStatuses(int $sbxMode, $ocRegistry): array
    {
        return $this->inpostFactory
            ->createInpostShipmentHandler($this->guzzleClient, $this->idOrganization, $this->baseUri, $this->apiKey, $ocRegistry)
            ->syncShipmentStatuses($sbxMode);
    }
}