<?php
/**
 * _             _       _
 * | |____      _| | __ _| |__
 * | '_ \ \ /\ / / |/ _` | '_ \
 * | |_) \ V  V /| | (_| | |_) |
 * |_.__/ \_/\_/ |_|\__,_|_.__/.
 *
 * @author    Bwlab di Luigi Massa <info@bwlab.it>
 * @copyright since 2014 Bwlab
 * @license   proprietary
 */

declare(strict_types=1);

namespace Bwlab\Withdrawal\Service;

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


use Bwlab\Withdrawal\Entity\WithdrawalRequest;
use Bwlab\Withdrawal\Repository\WithdrawalRequestRepository;
use Doctrine\ORM\EntityManagerInterface;

class WithdrawalRequestService
{
    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly WithdrawalRequestRepository $requestRepository,
        private readonly WithdrawalDeadlineCalculator $deadlineCalculator,
        private readonly WithdrawalMailService $mailService,
    ) {
    }

    /**
     * @param array<int, array{id_category_default: int}> $orderProducts
     *
     * @throws \RuntimeException if order is not eligible or request already exists
     */
    public function submit(
        int $orderId,
        int $customerId,
        int $shopId,
        string $customerName,
        string $customerEmail,
        string $orderReference,
        array $orderProducts,
        \DateTimeInterface $referenceDate,
        string $userAgent,
        string $languageIsoCode = 'it',
    ): WithdrawalRequest {
        if ($this->requestRepository->findOneByOrderAndCustomer($orderId, $customerId) !== null) {
            throw new \RuntimeException('Withdrawal request already submitted for this order.');
        }

        $days = $this->deadlineCalculator->calculateDays($orderProducts, $shopId);
        if ($days === null) {
            throw new \RuntimeException('This order is not eligible for withdrawal.');
        }

        $deadline = $this->deadlineCalculator->calculateDeadline($referenceDate, $days);
        if (new \DateTime() > $deadline) {
            throw new \RuntimeException('The withdrawal deadline has passed.');
        }

        $request = (new WithdrawalRequest())
            ->setIdOrder($orderId)
            ->setIdCustomer($customerId)
            ->setIdShop($shopId)
            ->setCustomerName($customerName)
            ->setCustomerEmail($customerEmail)
            ->setOrderReference($orderReference)
            ->setWithdrawalDeadline($deadline)
            ->setUserAgent($userAgent);

        $this->entityManager->persist($request);
        $this->entityManager->flush();

        $this->mailService->sendConfirmation($request, $languageIsoCode);
        $this->mailService->sendMerchantNotification($request, $languageIsoCode);

        return $request;
    }

    public function updateStatus(int $requestId, string $status, ?string $note, ?string $customerNote, int $employeeId): void
    {
        $request = $this->requestRepository->find($requestId);
        if ($request === null) {
            throw new \RuntimeException("WithdrawalRequest #{$requestId} not found.");
        }

        $request->setStatus($status)->setIdEmployee($employeeId);
        if ($note !== null && $note !== '') {
            $request->appendNote($note);
        }
        if ($customerNote !== null && trim($customerNote) !== '') {
            // keep a trace of what was communicated to the customer in the internal log
            $request->appendNote('→ Cliente: ' . trim($customerNote));
        }

        $this->entityManager->flush();

        $langIso = \Language::getIsoById((int) \Configuration::get('PS_LANG_DEFAULT'));
        $this->mailService->sendStatusUpdate($request, $customerNote, $langIso);
    }
}
