<?php
declare(strict_types=1);

namespace Autogedal\BlackFriday\Model;

use Magento\Customer\Api\AccountManagementInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\Session as CustomerSession;
use Magento\Customer\Model\Url as CustomerUrl;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Validator\EmailAddress as EmailValidator;
use Magento\Newsletter\Model\Config as NewsletterConfig;
use Magento\Newsletter\Model\Subscriber;
use Magento\Newsletter\Model\SubscriberFactory;
use Magento\Newsletter\Model\SubscriptionManagerInterface;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;

/**
 * The same subscription as the newsletter form of the site (Magento\Newsletter\Controller\Subscriber\NewAction):
 * same validations, same SubscriptionManager, so TheMarketer gets the subscriber through the existing observer.
 * The only difference: an email already subscribed is not an error here.
 */
class NewsletterSubscriber
{
    public function __construct(
        private readonly SubscriptionManagerInterface $subscriptionManager,
        private readonly SubscriberFactory $subscriberFactory,
        private readonly StoreManagerInterface $storeManager,
        private readonly CustomerRepositoryInterface $customerRepository,
        private readonly AccountManagementInterface $accountManagement,
        private readonly CustomerSession $customerSession,
        private readonly CustomerUrl $customerUrl,
        private readonly ScopeConfigInterface $scopeConfig,
        private readonly NewsletterConfig $newsletterConfig,
        private readonly EmailValidator $emailValidator
    ) {
    }

    /**
     * @return string the message shown to the visitor
     * @throws LocalizedException
     */
    public function subscribe(string $email): string
    {
        if (!$this->newsletterConfig->isActive()) {
            throw new LocalizedException(__('Abonarea la newsletter nu este disponibila momentan.'));
        }
        if (!$this->emailValidator->isValid($email)) {
            throw new LocalizedException(__('Please enter a valid email address.'));
        }
        if ((int)$this->scopeConfig->getValue(Subscriber::XML_PATH_ALLOW_GUEST_SUBSCRIBE_FLAG, ScopeInterface::SCOPE_STORE) !== 1
            && !$this->customerSession->isLoggedIn()
        ) {
            throw new LocalizedException(__(
                'Sorry, but the administrator denied subscription for guests. Please <a href="%1">register</a>.',
                $this->customerUrl->getRegisterUrl()
            ));
        }
        $websiteId = (int)$this->storeManager->getStore()->getWebsiteId();
        if ($this->customerSession->isLoggedIn()
            && $this->customerSession->getCustomerDataObject()->getEmail() !== $email
            && !$this->accountManagement->isEmailAvailable($email, $websiteId)
        ) {
            throw new LocalizedException(__('This email address is already assigned to another user.'));
        }

        $subscriber = $this->subscriberFactory->create()->loadBySubscriberEmail($email, $websiteId);
        if ($subscriber->getId() && (int)$subscriber->getSubscriberStatus() === Subscriber::STATUS_SUBSCRIBED) {
            return (string)__('Thank you for your subscription.');
        }

        $storeId = (int)$this->storeManager->getStore()->getId();
        $customerId = $this->getCustomerId($email, $websiteId);
        $subscriber = $customerId
            ? $this->subscriptionManager->subscribeCustomer($customerId, $storeId)
            : $this->subscriptionManager->subscribe($email, $storeId);

        return (int)$subscriber->getSubscriberStatus() === Subscriber::STATUS_NOT_ACTIVE
            ? (string)__('The confirmation request has been sent.')
            : (string)__('Thank you for your subscription.');
    }

    private function getCustomerId(string $email, int $websiteId): ?int
    {
        try {
            return (int)$this->customerRepository->get($email, $websiteId)->getId();
        } catch (NoSuchEntityException) {
            return null;
        }
    }
}
