<?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);

namespace Paysera\Delivery\Service;

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

class DeliveryOrderMessageGenerator
{
    private const ORDER_CREATED_NOTE = 'Order created - %s';
    private const ORDER_NOT_CREATED_NOTE = 'Something went wrong, you should create delivery order manually in Paysera account';
    private const ORDER_TERMINAL_LOCATION_NOTE = 'Paysera Delivery: Locker address: %s, %s, %s, %s, Order created: %s';
    private const ORDER_TERMINAL_LOCATION_CHANGE_NOTE = 'Paysera Delivery: Locker address has been changed: %s, %s, %s, %s';

    private const RECEIVER_DATA_TEMPLATES = [
        'shipping_phone' => 'Shipping phone: %s',
        'shipping_street' => 'Street: %s',
        'shipping_postcode' => 'Postal code: %s',
        'shipping_country' => 'Country: %s',
        'shipping_city' => 'City: %s',
    ];

    /**
     * @var int
     */
    private $shopId;
    /**
     * @var int
     */
    private $orderId;
    /**
     * @var int
     */
    private $languageId;
    /**
     * @var int
     */
    private $orderCustomerId;
    /**
     * @var callable
     */
    private $translationCallback;

    public function __construct(
        int $shopId,
        int $languageId,
        int $orderId,
        int $orderCustomerId,
        callable $translationCallback
    ) {
        $this->shopId = $shopId;
        $this->orderId = $orderId;
        $this->languageId = $languageId;
        $this->orderCustomerId = $orderCustomerId;
        $this->translationCallback = $translationCallback;
    }

    public function generateOrderCreatedMessage(string $deliveryOrderNumber): bool
    {
        /** @var string $messageText */
        $messageText = call_user_func(
            $this->translationCallback,
            self::ORDER_CREATED_NOTE,
            'DeliveryOrderHistoryGenerator'
        );
        $messageText = sprintf($messageText, $deliveryOrderNumber);
        $messageThreadId = $this->createMessageThread();

        return $messageThreadId !== null
            ? $this->createMessage($messageThreadId, $messageText)
            : false;
    }

    public function generateOrderNotCreatedMessage(): bool
    {
        /** @var string $messageText */
        $messageText = call_user_func(
            $this->translationCallback,
            self::ORDER_NOT_CREATED_NOTE,
            'DeliveryOrderHistoryGenerator'
        );
        $messageThreadId = $this->createMessageThread();

        return $messageThreadId !== null
            ? $this->createMessage($messageThreadId, $messageText)
            : false;
    }

    public function generateTerminalDeliveryOrderMessage(
        string $deliveryOrderNumber,
        string $shipmentGateway,
        string $shipmentMethod,
        string $parcelMachineStreet,
        string $parcelMachineLocation
    ): bool {
        /** @var string $messageText */
        $messageText = call_user_func(
            $this->translationCallback,
            self::ORDER_TERMINAL_LOCATION_NOTE,
            'DeliveryOrderHistoryGenerator'
        );
        $messageText = sprintf(
            $messageText,
            $shipmentGateway,
            $shipmentMethod,
            $parcelMachineStreet,
            $parcelMachineLocation,
            $deliveryOrderNumber
        );

        $messageThreadId = $this->createMessageThread();

        return $messageThreadId !== null
            ? $this->createMessage($messageThreadId, $messageText)
            : false;
    }

    public function generateTerminalDeliveryOrderChangedMessage(
        string $shipmentGateway,
        string $shipmentMethod,
        string $parcelMachineStreet,
        string $parcelMachineLocation
    ): bool {
        /** @var string $messageText */
        $messageText = call_user_func(
            $this->translationCallback,
            self::ORDER_TERMINAL_LOCATION_CHANGE_NOTE,
            'DeliveryOrderHistoryGenerator'
        );
        $messageText = sprintf(
            $messageText,
            $shipmentGateway,
            $shipmentMethod,
            $parcelMachineStreet,
            $parcelMachineLocation
        );

        $messageThreadId = $this->createMessageThread();

        return $messageThreadId !== null
            ? $this->createMessage($messageThreadId, $messageText)
            : false;
    }

    public function generateShippingAddressChangedMessage(array $previousData, array $currentData): bool
    {
        $messageText = sprintf(
            implode(
                "\n",
                [
                    'Receiver data changed',
                    'Previous: %s',
                    'Current: %s',
                ]
            ),
            $this->prepareRecipientDataInfoString($previousData),
            $this->prepareRecipientDataInfoString($currentData)
        );
        $messageThreadId = $this->createMessageThread();

        return $messageThreadId !== null && $this->createMessage($messageThreadId, $messageText);
    }

    public function generateShippingMethodNotFoundMessage(string $deliveryOrderNumber): bool
    {
        $messageText = sprintf(
            'Shipping method of delivery order %s has been changed, '
            . 'but it doesn\'t match with any active shipping method.',
            $deliveryOrderNumber
        );
        $messageThreadId = $this->createMessageThread();

        return $messageThreadId !== null && $this->createMessage($messageThreadId, $messageText);
    }

    public function generateShippingMethodIsChangedMessage(string $shippingMethodName): bool
    {
        $messageText = sprintf(
            'Shipping method has been changed to %s',
            $shippingMethodName
        );
        $messageThreadId = $this->createMessageThread();

        return $messageThreadId !== null && $this->createMessage($messageThreadId, $messageText);
    }

    private function prepareRecipientDataInfoString(array $recipientData): string
    {
        $newline = "\n\t";
        $messageData = [];

        foreach ($recipientData as $field => $value) {
            $messageData[] = isset(self::RECEIVER_DATA_TEMPLATES[$field])
                ? sprintf(self::RECEIVER_DATA_TEMPLATES[$field], (string)$value)
                : $field . ':' . $value;
        }

        return $newline . implode($newline, $messageData);
    }

    private function createMessage(int $customerThreadId, string $message): bool
    {
        $customerMessage = new \CustomerMessage();
        $customerMessage->id_customer_thread = $customerThreadId;
        $customerMessage->id_employee = 1;
        $customerMessage->message = $message;
        $customerMessage->private = true;
        $customerMessage->add();

        return true;
    }

    private function createMessageThread(): ?int
    {
        $customerThread = new \CustomerThread();
        $customerThread->id_contact = 0;
        $customerThread->id_customer = $this->orderCustomerId;
        $customerThread->id_shop = $this->shopId;
        $customerThread->id_order = $this->orderId;
        $customerThread->id_lang = $this->languageId;
        $customerThread->email = (new \Customer($this->orderCustomerId))->email;
        $customerThread->status = 'open';
        $customerThread->token = \Tools::passwdGen(12);
        $customerThread->add();

        return $customerThread->id === null ? null : (int) $customerThread->id;
    }
}
