<?php

namespace Autogedal\Retragere\Controller\Index;

use Magento\Framework\App\Action\Context;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Framework\Controller\Result\Redirect;
use Magento\Framework\DataObject;
use Magento\Framework\Stdlib\DateTime\DateTime;
use Magento\Framework\Validator\EmailAddress as EmailAddressValidator;
use Magento\Sales\Model\ResourceModel\Order\CollectionFactory as OrderCollectionFactory;
use Magento\Store\Model\StoreManagerInterface;
use Autogedal\Retragere\Model\MailInterface;
use Autogedal\Retragere\Model\Retragere;
use Autogedal\Retragere\Model\RetragereFactory;
use Autogedal\Retragere\Model\ResourceModel\Retragere\CollectionFactory as RetragereCollectionFactory;
use Psr\Log\LoggerInterface;

class Post extends \Magento\Framework\App\Action\Action implements HttpPostActionInterface
{
    private const DATA_PERSISTOR_KEY = 'autogedal_retragere';


    /**
     * @var RetragereFactory
     */
    private $retragereFactory;

    /**
     * @var DataPersistorInterface
     */
    private $dataPersistor;

    /**
     * @var MailInterface
     */
    private $mail;

    /**
     * @var EmailAddressValidator
     */
    private $emailValidator;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var DateTime
     */
    private $dateTime;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var OrderCollectionFactory
     */
    private $orderCollectionFactory;

    /**
     * @var RetragereCollectionFactory
     */
    private $retragereCollectionFactory;

    public function __construct(
        Context $context,
        RetragereFactory $retragereFactory,
        DataPersistorInterface $dataPersistor,
        MailInterface $mail,
        EmailAddressValidator $emailValidator,
        StoreManagerInterface $storeManager,
        DateTime $dateTime,
        LoggerInterface $logger,
        OrderCollectionFactory $orderCollectionFactory,
        RetragereCollectionFactory $retragereCollectionFactory
    ) {
        parent::__construct($context);
        $this->retragereFactory = $retragereFactory;
        $this->dataPersistor = $dataPersistor;
        $this->mail = $mail;
        $this->emailValidator = $emailValidator;
        $this->storeManager = $storeManager;
        $this->dateTime = $dateTime;
        $this->logger = $logger;
        $this->orderCollectionFactory = $orderCollectionFactory;
        $this->retragereCollectionFactory = $retragereCollectionFactory;
    }

    /**
     * @return Redirect
     */
    public function execute()
    {
        $resultRedirect = $this->resultRedirectFactory->create();

        if (!$this->getRequest()->isPost()) {
            return $resultRedirect->setPath('retragere');
        }

        $data = $this->getRequest()->getParams();

        $errors = $this->validate($data);

        if (!empty($errors)) {
            foreach ($errors as $error) {
                $this->messageManager->addErrorMessage($error);
            }
            $this->dataPersistor->set(self::DATA_PERSISTOR_KEY, $data);
            return $resultRedirect->setPath('retragere');
        }

        $order = $this->findOrder(trim((string) $data['order_number']));

        if ($order === null) {
            $this->messageManager->addErrorMessage(
                __('Comanda introdusa nu a fost gasita. Verificati numarul comenzii si incercati din nou.')
            );
            $this->dataPersistor->set(self::DATA_PERSISTOR_KEY, $data);
            return $resultRedirect->setPath('retragere');
        }

        if ($this->isWithdrawalPeriodExpired($order)) {
            $this->messageManager->addErrorMessage(
                __('Au trecut mai mult de 14 zile de la livrarea comenzii, asa ca aceasta nu mai este eligibila pentru exercitarea dreptului de retragere.')
            );
            $this->dataPersistor->set(self::DATA_PERSISTOR_KEY, $data);
            return $resultRedirect->setPath('retragere');
        }

        if ($this->hasExistingRequest(trim((string) $data['order_number']))) {
            $this->messageManager->addErrorMessage(
                __('Comanda introdusa nu este eligibila pentru exercitarea dreptului de retragere. Puteti folosi formularul de retur.')
            );
            $this->dataPersistor->set(self::DATA_PERSISTOR_KEY, $data);
            return $resultRedirect->setPath('retragere');
        }

        try {
            $storeId = (int) $this->storeManager->getStore()->getId();
            $createdAt = $this->dateTime->gmtDate();

            /** @var Retragere $retragere */
            $retragere = $this->retragereFactory->create();
            $retragere->setData([
                'store_id' => $storeId,
                'firstname' => trim((string) $data['firstname']),
                'lastname' => trim((string) $data['lastname']),
                'email' => trim((string) $data['email']),
                'order_number' => trim((string) $data['order_number']),
                'telephone' => trim((string) ($data['telephone'] ?? '')),
                'message' => trim((string) ($data['message'] ?? '')),
                'status' => Retragere::STATUS_NEW,
                'created_at' => $createdAt,
            ]);
            $retragere->save();

            $this->mail->send($retragere->getEmail(), [
                'data' => new DataObject([
                    'store_id' => $storeId,
                    'firstname' => $retragere->getFirstname(),
                    'lastname' => $retragere->getLastname(),
                    'order_number' => $retragere->getOrderNumber(),
                    'created_at' => $createdAt,
                ]),
            ]);

            $this->dataPersistor->clear(self::DATA_PERSISTOR_KEY);
        } catch (\Exception $e) {
            $this->logger->critical($e);
            $this->messageManager->addErrorMessage(
                __('A aparut o eroare la procesarea cererii. Te rugam sa incerci din nou.')
            );
            $this->dataPersistor->set(self::DATA_PERSISTOR_KEY, $data);
            return $resultRedirect->setPath('retragere');
        }

        return $resultRedirect->setPath('retragere/index/success');
    }

    /**
     * @param string $orderNumber
     * @return \Magento\Sales\Model\Order|null
     */
    private function findOrder(string $orderNumber): ?\Magento\Sales\Model\Order
    {
        $order = $this->orderCollectionFactory->create()
            ->addFieldToFilter('user_increment_id', $orderNumber)
            ->getFirstItem();

        return $order->getId() ? $order : null;
    }

    /**
     * Returns true if the order was ever completed AND more than 14 days have
     * passed since it reached "complete" status. If the order was never completed
     * the countdown has not started yet, so returns false.
     *
     * @param \Magento\Sales\Model\Order $order
     * @return bool
     */
    private function isWithdrawalPeriodExpired(\Magento\Sales\Model\Order $order): bool
    {
        $completedAt = $this->getOrderCompletedAt($order);

        if ($completedAt === null) {
            return false;
        }

        return (new \DateTime())->diff($completedAt)->days > 14;
    }

    /**
     * Returns the date when the order first reached "complete" status, or null
     * if it never did.
     *
     * @param \Magento\Sales\Model\Order $order
     * @return \DateTime|null
     */
    private function getOrderCompletedAt(\Magento\Sales\Model\Order $order): ?\DateTime
    {
        $completedStatuses = ['complete', 'custom_finalizatamanual'];

        foreach ($order->getStatusHistories() as $history) {
            if (in_array($history->getStatus(), $completedStatuses, true)) {
                return new \DateTime($history->getCreatedAt());
            }
        }

        return null;
    }

    /**
     * Check if a withdrawal request was already registered for this order
     *
     * @param string $orderNumber
     * @return bool
     */
    private function hasExistingRequest(string $orderNumber): bool
    {
        $collection = $this->retragereCollectionFactory->create()
            ->addFieldToFilter('order_number', $orderNumber);

        return count($collection) > 0;
    }

    /**
     * Validate required fields
     *
     * @param array $data
     * @return array
     */
    private function validate(array $data): array
    {
        $errors = [];

        if (empty(trim((string) ($data['firstname'] ?? '')))) {
            $errors[] = __('Numele este obligatoriu.');
        }

        if (empty(trim((string) ($data['lastname'] ?? '')))) {
            $errors[] = __('Prenumele este obligatoriu.');
        }

        $email = trim((string) ($data['email'] ?? ''));
        if (empty($email)) {
            $errors[] = __('Adresa de email este obligatorie.');
        } elseif (!$this->emailValidator->isValid($email)) {
            $errors[] = __('Adresa de email nu este valida.');
        }

        if (empty(trim((string) ($data['order_number'] ?? '')))) {
            $errors[] = __('Numarul comenzii este obligatoriu.');
        }

        return $errors;
    }
}
