<?php

namespace inpost_shipping\Model\Shipment;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use inpost_shipping\InpostConfig;
use inpost_shipping\Model\Helper\InpostHelper;


class InpostShipmentHandler
{
    /**
     * @var \GuzzleHttp\Client $guzzleClient
     */
    protected $guzzleClient;

    /**
     * @var string
     */
    protected $idOrganization;

    /**
     * @var string
     */
    protected $baseUri;

    /**
     * @var string
     */
    protected $apiKey;

    /**
     * @var \stdClass
     */
    protected $ocRegistry;

    public function __construct(
        Client $guzzleClient,
        string $idOrganization,
        string $baseUri,
        string $apiKey,
        $ocRegistry
    ) {
        $this->guzzleClient = $guzzleClient;
        $this->idOrganization = $idOrganization;
        $this->baseUri = $baseUri;
        $this->apiKey = $apiKey;
        $this->ocRegistry = $ocRegistry;
    }

    /**
     * @param array $request
     * @param $ocRegistry
     *
     * @return array
     */
    public function createShipment(array $request, $ocRegistry): array
    {
        $orderDetails = json_decode(base64_decode($request['orderInfo']), 1);

        $orderDetails['telephone'] = $request['phone'];
        $orderDetails['email'] = $request['email'];

        $shipmentRequest = [
            'service' => $request['delivery_service'],
            'receiver' => $this->prepareReceiverInformation($orderDetails, $request['delivery_service'], $ocRegistry),
            'parcels' => [],
            'reference' => $orderDetails['order_id'],
            'comments' => '',
            'external_customer_id' => 'opencart',
        ];

        if ((float)$request['insurance'] >= 1.0000) {
            $shipmentRequest['insurance'] = [
                'amount' => $request['insurance'],
                'currency' => 'PLN',
            ];
        }

        $shipmentRequest['parcels'][] = $this->prepareParcels($request);

        if (!empty($request['cod'])) {
            $shipmentRequest['cod'] = [
                'amount' => $request['total'],
                'currency' => 'PLN',
            ];
        }

        if (!empty($request['weekendDelivery'])) {
            $shipmentRequest['end_of_week_collection'] = true;
        }

        $shipmentRequest['custom_attributes'] = [
            'sending_method' => $request['shipping_method'],
            'dropoff_point' => $request['dropoff_point'] ?? '',
            'target_point' => $request['parcelLocker'],
        ];

        try {
            $response = $this->guzzleClient
                ->request(
                    'POST',
                    $this->baseUri . '/v1/organizations/' . $this->idOrganization . '/shipments',
                    [
                        'json' => $shipmentRequest
                    ],
                );
        } catch (ClientException $exception) {
            return InpostHelper::decodeExceptionToArray($exception->getResponse());
        }

        $responseData = json_decode($response->getBody()->getContents(), 1);

        return $this->postCreateShipment($responseData);
    }

    /**
     * @param array $orderDetails
     * @param string $deliveryService
     * @param $ocRegistry
     *
     * @return array|null
     */
    public function prepareReceiverInformation(array $orderDetails, string $deliveryService, $ocRegistry): ?array
    {
        if (InpostConfig::SENDING_METHOD_SERVICES[$deliveryService]['parcel_locker'] && $deliveryService !== 'inpost_courier_c2c') {
            return [
                'phone' => $orderDetails['telephone'],
                'email' => $orderDetails['email'],
            ];
        }

        if (!InpostConfig::SENDING_METHOD_SERVICES[$deliveryService]['parcel_locker'] || $deliveryService === 'inpost_courier_c2c') {
            $countryCode = $ocRegistry->db->query("SELECT * FROM `" . DB_PREFIX . "country` WHERE country_id = '" . (int)$orderDetails['shipping_country_id'] . "'")->row['iso_code_2'];
            return [
                'phone' => $orderDetails['telephone'],
                'email' => $orderDetails['email'],
                'company_name' => $orderDetails['shipping_company'],
                'first_name' => $orderDetails['shipping_firstname'],
                'last_name' => $orderDetails['shipping_lastname'],
                'address' => [
                    'street' => $orderDetails['shipping_address_1'],
                    'building_number' => $orderDetails['shipping_address_2'],
                    'city' => $orderDetails['shipping_city'],
                    'post_code' => $orderDetails['shipping_postcode'],
                    'country_code' => $countryCode,
                ],
            ];
        }

        return null;
    }

    /**
     * @param array $request
     *
     * @return array|null
     */
    public function prepareParcels(array $request): ?array
    {
        $deliveryService = $request['delivery_service'];

        if ($deliveryService === 'inpost_locker_standard' ||
            $deliveryService === 'inpost_locker_allegro' ||
            $deliveryService === 'inpost_locker_pass_thru'
        ) {
            if (isset($request['predefinedTemplate'])) {
                $template = $request['template'];
                if (isset($request['predefinedTemplate'])) {
                    return [
                        'template' => InpostConfig::TEMPLATE_TRANSFORM[$template],
                    ];
                }
                return [
                    'dimensions' => [
                        'length' => InpostConfig::SIZES_LOCKER[$template]['length'],
                        'width' => InpostConfig::SIZES_LOCKER[$template]['width'],
                        'height' => InpostConfig::SIZES_LOCKER[$template]['height'],
                        'unit' => 'mm',
                    ],
                    'weight' => [
                        'amount' => InpostConfig::SIZES_LOCKER[$template]['weight'],
                        'unit' => 'kg',
                    ],
                ];
            }
        }

        if ($deliveryService === 'inpost_courier_c2c') {
            if (isset($request['predefinedTemplate'])) {
                $template = $request['template'];
                return [
                    'template' => InpostConfig::TEMPLATE_TRANSFORM[$template],
                ];
            }
//            else {
//                return [
//                    'dimensions' => [
//                        'length' => InpostConfig::SIZES_LOCKER_C2C[$template]['length'],
//                        'width' => InpostConfig::SIZES_LOCKER_C2C[$template]['width'],
//                        'height' => InpostConfig::SIZES_LOCKER_C2C[$template]['height'],
//                        'unit' => 'mm',
//                    ],
//                    'weight' => [
//                        'amount' => InpostConfig::SIZES_LOCKER_C2C[$template]['weight'],
//                        'unit' => 'kg',
//                    ],
//                ];
//            }
        }

        return [
            'dimensions' => [
                'length' => $request['length'],
                'width' => $request['width'],
                'height' => $request['height'],
                'unit' => 'mm',
            ],
            'weight' => [
                'amount' => $request['weight'],
                'unit' => 'kg',
            ],
        ];
    }

    /**
     * @param array $response
     *
     * @return array
     */
    protected function postCreateShipment(array $response): array
    {
        $response = $this->enrichResponseWithTrackingNumber($response);

        return $response;
    }

    /**
     * @param array $response
     *
     * @return array
     */
    protected function enrichResponseWithTrackingNumber(array $response): array
    {
        $subResponse = $this->syncTrackingNumber($response['id']);

        if (!empty($subResponse['error'])) {
            return $subResponse;
        }

        $response['tracking_number'] = !empty($subResponse['tracking_number']) ? $subResponse['tracking_number'] : '';

        return $response;
    }

    /**
     * @param string $id
     * @param int $retryCount
     *
     * @return array
     */
    public function syncTrackingNumber(string $id, int $retryCount = InpostConfig::TN_REQUEST_RETRY_COUNT): array
    {
        $response = [];
        $retryCounter = 0;
        for ($i = 0; $i < $retryCount; $i++) {
            usleep(500000);
            try {
                $subResponse = $this->guzzleClient
                    ->request(
                        'GET',
                        $this->baseUri . '/v1/organizations/' . $this->idOrganization . '/shipments?id=' . $id,
                        [],
                    );
            } catch (ClientException $exception) {
                return InpostHelper::decodeExceptionToArray($exception->getResponse());
            }

            $data = json_decode($subResponse->getBody()->getContents(), 1);

            if (empty($data['items'])) {
                return [
                    'error' => 'Synchronization error',
                    'error_details' => 'Synchronization error. Please, ensure that the shipment related to correct live/sandbox mode.',
                ];
            }

            $trackingNumber = $data['items'][0]['tracking_number'];

            if (!empty($trackingNumber)) {
                $response['tracking_number'] = $trackingNumber;
                break;
            }

            $retryCounter++;
        }

        $response['retryCounter'] = $retryCounter;

        return $response;
    }

    public function syncShipmentStatuses(int $sbxMode): array
    {
        $sql = "SELECT * FROM `".DB_PREFIX."inpost_shipping_shipments` sh WHERE sbxMode = '".$sbxMode."' AND (".strtotime(date('Y-m-d H:i:s'))." - sh.lastSync) > 300 ORDER BY sh.lastSync ASC LIMIT 0, 50";
        $shipments = $this->ocRegistry->db->query($sql)->rows;

        if (empty($shipments)) {
            return [];
        }

        return $this->getShipmentsStatuses($shipments);
    }

    /**
     * @param array $shipments
     *
     * @return array
     */
    public function getShipmentsStatuses(array $shipments = []): array
    {
        if (empty($shipments)) {
            return [];
        }

        $shipmentsToCheck = [];
        foreach ($shipments as $shipment) {
            $shipmentsToCheck[] = $shipment['idInpost'];
        }

        $ids = implode(',', $shipmentsToCheck);

        try {
            $response = $this->guzzleClient
                ->request(
                    'GET',
                    $this->baseUri . '/v1/organizations/' . $this->idOrganization . '/shipments?id=' . $ids,
                );
        } catch (ClientException $exception) {
            return InpostHelper::decodeExceptionToArray($exception->getResponse());
        }

        return json_decode($response->getBody()->getContents(), 1);
    }
}