<?php
/**
 * 2018 Paysera
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * @author Paysera <plugins@paysera.com>
 * @copyright 2018 Paysera
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 * International Registered Trademark & Property of Paysera
 */
declare(strict_types=1);

if (!defined('_PS_VERSION_')) {
    exit;
}

use GuzzleHttp6\Exception\GuzzleException;
use Paysera\Delivery\PayseraDeliveryCarriers;
use Paysera\Delivery\PayseraDeliveryConfiguration;
use Paysera\Delivery\Service\DeliveryApiClient;

class PayseraDeliveryApiModuleFrontController extends ModuleFrontController
{
    const PARAM_CARRIER = 'carrier';
    const PARAM_METHOD = 'method';
    const PARAM_COUNTRY = 'country';
    const PARAM_CITY = 'city';
    const PARAM_TERMINAL = 'terminal';
    const PARAM_VALIDATE = 'validate';
    const PARAM_SAVE_TERMINAL = 'save_terminal';

    /**
     * @var DeliveryApiClient
     */
    private $deliveryApiClient;
    /**
     * @var array<string, int|string>|null
     */
    private $deliveryGateway;
    /**
     * @var string|bool
     */
    private $output;
    /**
     * @var Carrier
     */
    private $carrier;
    /**
     * @var bool
     */
    public $ajax;

    public function __construct()
    {
        parent::__construct();

        $this->deliveryApiClient = $this->module
            ->getServiceContainer()
            ->getService('paysera.payseradelivery.service.delivery_api_client')
        ;
    }

    public function initContent(): void
    {
        $this->ajax = true;

        $payseraConfiguration = new PayseraDeliveryConfiguration();

        $payseraCarriers = new PayseraDeliveryCarriers($this->module, $payseraConfiguration);

        $this->carrier = new Carrier((int) Tools::getValue(self::PARAM_CARRIER));

        $this->deliveryGateway = $payseraCarriers->getCarrierById((int) $this->carrier->id);

        $data = [];

        switch (Tools::getValue(self::PARAM_METHOD)) {
            case self::PARAM_COUNTRY:
                $data = $this->getCountries();
                break;
            case self::PARAM_CITY:
                $data = $this->getCities();
                break;
            case self::PARAM_TERMINAL:
                $data = $this->getTerminalLocations();
                break;
            case self::PARAM_VALIDATE:
                $data = $this->validate();
                break;
            case self::PARAM_SAVE_TERMINAL:
                $this->saveTerminal();
                break;
        }

        $this->output = json_encode($data);

        parent::initContent();
    }

    public function displayAjax(): void
    {
        echo $this->output;

        exit;
    }

    private function saveTerminal(): void
    {
        $this->context->cookie->__set('paysera_delivery_terminal', Tools::getValue(self::PARAM_TERMINAL));
        $this->context->cookie->__set('paysera_delivery_country', Tools::getValue(self::PARAM_COUNTRY));
        $this->context->cookie->__set('paysera_delivery_city', Tools::getValue(self::PARAM_CITY));
    }

    /**
     * @return array<string, string>
     *
     * @throws GuzzleException
     */
    private function getCountries(): array
    {
        $normalizedCountries = [];

        if (isset($this->deliveryGateway['code'])) {
            foreach ($this->deliveryApiClient->getCountries((string) $this->deliveryGateway['code']) as $country) {
                if (in_array($country->getCountryCode(), PayseraDeliveryConfiguration::DELIVERY_COUNTRIES)) {
                    $normalizedCountries[$country->getCountryCode()] = $country->getCountryName();
                }
            }
        }

        return array_merge(['default' => $this->l('select_country', 'api')], $normalizedCountries);
    }

    /**
     * @return array<string, string>
     *
     * @throws GuzzleException
     */
    private function getCities(): array
    {
        $cities = [];

        if (isset($this->deliveryGateway['code'])) {
            $cities = $this->deliveryApiClient->getCities((string) $this->deliveryGateway['code'], (string) Tools::getValue(self::PARAM_COUNTRY));
        }

        return array_merge(
            ['default' => $this->l('select_city', 'api')],
            $cities
        );
    }

    /**
     * @return array<string, string>
     *
     * @throws GuzzleException
     */
    private function getTerminalLocations(): array
    {
        $terminalLocations = [];

        if (isset($this->deliveryGateway['code'])) {
            $terminalLocations = $this->deliveryApiClient->getTerminalLocations(
                (string) $this->deliveryGateway['code'],
                (string) Tools::getValue(self::PARAM_COUNTRY),
                (string) Tools::getValue(self::PARAM_CITY)
            );
        }

        return array_merge(
            ['default' => $this->l('select_terminal', 'api')],
            $terminalLocations
        );
    }

    /**
     * @return array<string, mixed>
     *
     * @throws GuzzleException
     */
    private function validate(): array
    {
        $json = [
            'has_delivery_terminal' => 0,
            'selected_country' => 'default',
            'selected_city' => 'default',
        ];

        if ($this->deliveryGateway === null) {
            return $json;
        }

        if ($this->context->cart->getTotalWeight() > (int) $this->carrier->max_weight) {
            $json['error'] = sprintf(
                $this->l('error_max_weight', 'api'),
                $this->context->cart->getTotalWeight(),
                $this->carrier->max_weight,
                Configuration::get('PS_WEIGHT_UNIT')
            );
        }

        $json['has_delivery_terminal'] = $this->isReceiverTypeParcelMachine((string) $this->deliveryGateway['type']) ? 1 : 0;

        if ($json['has_delivery_terminal'] === 1) {
            $address = new Address($this->context->cart->id_address_delivery);

            if (
                count($this->deliveryApiClient->getCities(
                    (string) $this->deliveryGateway['code'],
                    Country::getIsoById($address->id_country)
                ))
                > 0
            ) {
                $json['selected_country'] = Country::getIsoById($address->id_country);

                if (
                    count($this->deliveryApiClient->getTerminalLocations(
                        (string) $this->deliveryGateway['code'],
                        Country::getIsoById($address->id_country),
                        $address->city
                    ))
                    > 0
                ) {
                    $json['selected_city'] = $address->city;
                }
            }
        }

        return $json;
    }

    /**
     * @param string $type
     *
     * @return bool
     */
    private function isReceiverTypeParcelMachine(string $type): bool
    {
        return $type === PayseraDeliveryConfiguration::TYPE_PARCEL_MACHINE;
    }
}
