<?php

namespace FiloBlu\ActiveCampaign\Model\Entity\Export;

use Exception;
use FiloBlu\ActiveCampaign\Api\Data\JobsInterface;
use FiloBlu\ActiveCampaign\Api\Data\JobsInterfaceFactory;
use FiloBlu\ActiveCampaign\Api\JobsRepositoryInterface;
use FiloBlu\ActiveCampaign\Helper\Data;
use FiloBlu\ActiveCampaign\Model\Config\Source\DirectionType;
use FiloBlu\ActiveCampaign\Model\Config\Source\EntityType;
use FiloBlu\ActiveCampaign\Model\Resource\ActiveCampaign;
use Magento\Customer\Api\AddressRepositoryInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Newsletter\Model\SubscriberFactory as MagentoSubscriber;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Store\Model\Service\StoreConfigManager;
use Zend_Db;

/**
 * Class Subscriber
 * @package FiloBlu\ActiveCampaign\Model\Entity\Export
 */
class Subscriber extends AbstractEntityExport {
    /**
     * @var ResourceConnection
     */
    protected $resourceConnection;
    /**
     * @var Data
     */
    protected $helperData;
    /**
     * @var JobsRepositoryInterface
     */
    protected $jobsRepository;
    /**
     * @var string
     */
    protected $entity = EntityType::ENTITY_SUBSCRIBER;
    /**
     * @var string
     */
    protected $direction = DirectionType::DIRECTION_EXPORT;
    /**
     * @var CustomerRepositoryInterface
     */
    protected $customerRepository;
    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;
    /**
     * @var MagentoSubscriber
     */
    protected $subscriber;
    /**
     * @var ActiveCampaign
     */
    protected $activeCampaign;
    /**
     * @var AddressRepositoryInterface
     */
    protected $addressRepository;
    /**
     * @var StoreConfigManager
     */
    protected $storeConfigManager;
    /**
     * @var array|mixed
     */
    private $genderMapping;
    /**
     * @var array
     */
    protected $storeLocales = [];

    /**
     * Subscriber constructor.
     * @param ResourceConnection $resourceConnection
     * @param Data $helperData
     * @param JobsRepositoryInterface $jobsRepository
     * @param JobsInterfaceFactory $jobsFactory
     * @param ActiveCampaign $activeCampaign
     * @param MagentoSubscriber $subscriber
     * @param CustomerRepositoryInterface $customerRepository
     * @param StoreManagerInterface $storeManager
     * @param AddressRepositoryInterface $addressRepository
     * @param StoreConfigManager $storeConfigManager
     */
    public function __construct(
        ResourceConnection $resourceConnection,
        Data $helperData,
        JobsRepositoryInterface $jobsRepository,
        JobsInterfaceFactory $jobsFactory,
        ActiveCampaign $activeCampaign,
        MagentoSubscriber $subscriber,
        CustomerRepositoryInterface $customerRepository,
        StoreManagerInterface $storeManager,
        AddressRepositoryInterface $addressRepository,
        StoreConfigManager $storeConfigManager
    )
    {
        parent::__construct($resourceConnection, $helperData, $jobsRepository, $jobsFactory, $activeCampaign);
        $this->subscriber = $subscriber;
        $this->customerRepository = $customerRepository;
        $this->storeManager = $storeManager;
        $this->activeCampaign = $activeCampaign;
        $this->addressRepository = $addressRepository;
        $this->storeConfigManager = $storeConfigManager;
    }


    /**
     * @return array|mixed
     */
    public function getAllIncrementalIds() {
        $connection = $this->resourceConnection->getConnection();
        $tableName = $this->resourceConnection->getTableName('newsletter_subscriber');
        $tableNameJobs = $this->resourceConnection->getTableName(JobsInterface::DB_TABLE);

        if (empty($sw = $this->helperData->getEnabledStoreViews())) {
            return [];
        }

        $sql = "SELECT
                  ce.subscriber_id as subscriber_id
                FROM
                  {$tableName} AS ce
                LEFT JOIN
                  {$tableNameJobs} AS j
                ON
                  j.entity_id = ce.subscriber_id
                AND
                  j.entity = ?
                AND
                  j.direction = ?
                WHERE
                  j.status IS NULL
                AND
                  FIND_IN_SET(ce.store_id, ?) > 0";

        return $connection->fetchCol($sql, [$this->entity, $this->direction, $sw]);

    }

    /**
     * Init
     */
    public function init() {
        if(count($this->storeLocales)==0){
            foreach ($this->storeConfigManager->getStoreConfigs() as $storeConfig) {
                $this->storeLocales[$storeConfig->getId()] = $storeConfig->getLocale();
            }
        }
    }

    /**
     * @param array $job
     * @return bool
     * @throws \Magento\Framework\Exception\LocalizedException
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function executeProcess(array $job): bool
    {
        $this->init();
        $subscriberData = $this->getSubscriberData($job);
        $this->executeContactRestProcess($subscriberData,$job);
        return true;
    }

    /**
     * @param array $job
     * @return array
     * @throws \Magento\Framework\Exception\LocalizedException
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getSubscriberData(array $job) {
        $data = [];
        $subscriberMapping = $this->helperData->getSubscriberMapping();
        $customerMapping = $this->helperData->getCustomerMapping();
        $customerBillingMapping = $this->helperData->getCustomerBillingAddressMapping();
        $customerShippingMapping = $this->helperData->getCustomerShippingAddressMapping();

        $subscriber = $this->subscriber->create()->load($job['entity_id']);
        if(!empty($subscriber->getCustomerId())){
            $customer = $this->customerRepository->getById($subscriber->getCustomerId());
        }

        $additionalNewsletterData = [];
        if($this->helperData->isNewsletterExtensionEnabled()){
            $additionalNewsletterData = $this->normalizeFields($this->getNewsletterAdditionalInfo($job['entity_id']));
        }

        $additionalData = [
            '_customer_id' => $subscriber->getCustomerId(),
            '_store_code' => $this->storeManager->getStore($subscriber->getStoreId())->getCode(),
            '_store_name' => $this->storeManager->getStore($subscriber->getStoreId())->getName(),
            '_website_name' => $this->storeManager->getStore($subscriber->getStoreId())->getWebsite()->getName(),
            '_website_code' => $this->storeManager->getStore($subscriber->getStoreId())->getWebsite()->getCode(),
            '_type' => 'SUBSCRIBER',
            '_subscriber_status_normalized' => ($subscriber->isSubscribed() == 1) ? '1' : '0',
            '_created_at_normalized' => '',
            '_updated_at_normalized' => '',
            '_language' => $this->storeLocales[$subscriber->getStoreId()],
            '_language_iso2' => strtolower(substr($this->storeLocales[$subscriber->getStoreId()], 0,2)),
            '_source' => ''
        ];

        $result = [
            'email' => $subscriber->getEmail()
        ];

        if($additionalNewsletterData!==false){
            $additionalData = array_merge($additionalData, $additionalNewsletterData);
            if(isset($additionalNewsletterData['firstname']) && !empty($additionalNewsletterData['firstname'])){
                $result['firstName'] = $additionalNewsletterData['firstname'];
            }
            if(isset($additionalNewsletterData['lastname']) && !empty($additionalNewsletterData['lastname'])){
                $result['lastName'] = $additionalNewsletterData['lastname'];
            }
            if(isset($additionalNewsletterData['phone']) && !empty($additionalNewsletterData['phone'])){
                $result['phone'] = $additionalNewsletterData['phone'];
            }
            if(isset($additionalNewsletterData['created_at']) && !empty($additionalNewsletterData['created_at'])){
                $additionalData['_created_at_normalized'] = $this->helperData->getACDatetimeFormat($additionalNewsletterData['created_at']);
            }
            if(isset($additionalNewsletterData['updated_at']) && !empty($additionalNewsletterData['updated_at'])) {
                $additionalData['_updated_at_normalized'] = $this->helperData->getACDatetimeFormat($additionalNewsletterData['updated_at']);
            }
            if(isset($additionalNewsletterData['language']) && !empty($additionalNewsletterData['language'])) {
                $additionalData['_language'] = $additionalNewsletterData['language'];
            }
            if(isset($additionalNewsletterData['source']) && !empty($additionalNewsletterData['source'])) {
                $additionalData['_source'] = $additionalNewsletterData['source'];
            }
        }

        foreach ($subscriberMapping as $key => $val) {
            if($this->helperData->isAdditional($key) || isset($additionalData[$key])){
                if(!empty($additionalData[$key])) {
                    $data[$val] = [
                        'field' => $val,
                        'value' => $additionalData[$key]
                    ];
                }
                continue;
            }
            if(!empty($subscriber->{'get'.$this->helperData->snakeToCamelCase($key)}())) {
                $data[$val] = [
                    'field' => $val,
                    'value' => $subscriber->{'get' . $this->helperData->snakeToCamelCase($key)}()
                ];
            }
        }

        if(!empty($subscriber->getCustomerId())) {
            $result['firstName'] = $customer->getFirstname();
            $result['lastName'] = $customer->getLastname();
            $additionalData = $this->completeAdditionalData($additionalData,$customer);
            if(!isset($result['phone'])||empty($result['phone'])){
                //try to get phone
            }
            foreach ($customerMapping as $key => $val) {
                if($this->helperData->isAdditional($key)){
                    if(!empty($additionalData[$key])) {
                        $data[$val] = [
                            'field' => $val,
                            'value' => $additionalData[$key]
                        ];
                    }
                    continue;
                }
                if (!empty($customer->{'get' . $this->helperData->snakeToCamelCase($key)}())) {
                    $data[$val] = [
                        'field' => $val,
                        'value' => $customer->{'get' . $this->helperData->snakeToCamelCase($key)}()
                    ];
                }
            }

            if(count($customerShippingMapping)) {
                try {
                    //If there is no address
                    $shippingAddress = $this->addressRepository->getById($customer->getDefaultShipping());
                    if(!empty($shippingAddress->getTelephone())){
                        $result['phone'] = $shippingAddress->getTelephone();
                    }
                    foreach ($customerShippingMapping as $key => $val) {
                        if ($this->helperData->isAdditional($key)) {
                            if(!empty($additionalData[$key])) {
                                $data[$key] = [
                                    'field' => $val,
                                    'value' => $additionalData[$key]
                                ];
                            }
                            continue;
                        }
                        if(!empty($shippingAddress->{'get'.$this->helperData->snakeToCamelCase($key)}())) {
                            $data[$key] = [
                                'field' => $val,
                                'value' => $shippingAddress->{'get' . $this->helperData->snakeToCamelCase($key)}()
                            ];
                        }
                    }
                }
                catch (Exception $exception) {
                    // No address
                }
            }

            if(count($customerBillingMapping)) {
                try {
                    //If there is no address
                    $billingAddress = $this->addressRepository->getById($customer->getDefaultBilling());
                    if(!empty($billingAddress->getTelephone())){
                        $result['phone'] = $billingAddress->getTelephone();
                    }
                    foreach ($customerBillingMapping as $key => $val) {
                        if($this->helperData->isAdditional($key)){
                            if(!empty($additionalData[$key])){
                                $data[$key] = [
                                    'field' => $val,
                                    'value' => $additionalData[$key]
                                ];
                            }
                            continue;
                        }
                        if(!empty($billingAddress->{'get'.$this->helperData->snakeToCamelCase($key)}())){
                            $data[$key] = [
                                'field' => $val,
                                'value' => $billingAddress->{'get'.$this->helperData->snakeToCamelCase($key)}()
                            ];
                        }
                    }
                }
                catch (Exception $exception) {
                    // No Address
                }

            }
        }

        if(count($data)){
            $d = [];
            foreach ($data as $dat){
                $d[] = $dat;
            }
            $result['fieldValues'] = $d;
        }

        $finalRequest['contact'] = $result;

        $request = [
            'create_update_contact' => [
                'request' => $finalRequest
            ],
            'list_subscription' => [
                'request' => [
                    'contactList' => [
                        'list' => $this->helperData->getSubscriberList(),
                        'status' => ($subscriber->isSubscribed() == 1) ? '1' : '2'
                    ]
                ]
            ]
        ];


        return $request;
    }

    /**
     * @param $subscriberId
     * @return array
     */
    public function getNewsletterAdditionalInfo($subscriberId) {
        $connection = $this->resourceConnection->getConnection();
        $tableName = $this->resourceConnection->getTableName('filoblu_newsletter_subscriber_info');
        $sql = "SELECT * FROM {$tableName} WHERE subscriber_id = ?";
        return $connection->fetchRow($sql, [$subscriberId], Zend_Db::FETCH_ASSOC);
    }

    /**
     * @param $fields
     * @return mixed
     */
    public function normalizeFields($fields) {
        if($fields == false) {
            return false;
        }

        $fields['_dob_normalized'] = '';
        if(isset($fields['dob']) && !empty($fields['dob'])){
            $date = explode('/',$fields['dob']);
            if(count($date)>1){
                $fields['_dob_normalized'] = $date[2].'-'.$date[1].'-'.$date[0];
            }
            else {
                $fields['_dob_normalized'] = $fields['dob'];
            }
        }
        $fields['_gender_normalized'] = '';
        if(isset($fields['gender']) && !empty($fields['gender'])){
            $gender = $this->getGenderMapping();
            if(count($gender) > 0 && isset($gender[$fields['gender']])){
                $fields['_gender_normalized'] = $gender[$fields['gender']];
            }
        }
        return $fields;
    }

    /**
     * @return array|mixed
     */
    public function getGenderMapping() {
        if($this->genderMapping && count($this->genderMapping)){
            return $this->genderMapping;
        }
        $this->genderMapping = $this->helperData->getGenderMapping();
        return $this->genderMapping;
    }

    /**
     * @param $additionalData
     * @param $customer
     * @return mixed
     */
    function completeAdditionalData($additionalData,$customer) {
        $additionalData['_created_at_normalized'] = $this->helperData->getACDatetimeFormat($customer->getCreatedAt());
        $additionalData['_updated_at_normalized'] = $this->helperData->getACDatetimeFormat($customer->getUpdatedAt());
        $additionalData['_type'] = 'CUSTOMER';

        if(empty($additionalData['_store_code'])) {
            $additionalData['_store_code'] = $this->storeManager->getStore($customer->getStoreId())->getCode();
        }
        if(empty($additionalData['_store_name'])) {
            $additionalData['_store_name'] = $this->storeManager->getStore($customer->getStoreId())->getName();
        }
        if(empty($additionalData['_website_code'])) {
            $additionalData['_website_code'] = $this->storeManager->getStore($customer->getStoreId())->getWebsite()->getCode();
        }
        if(empty($additionalData['_website_name'])) {
            $additionalData['_website_name'] = $this->storeManager->getStore($customer->getStoreId())->getWebsite()->getName();
        }
        if(empty($additionalData['_language'])) {
            $additionalData['_language'] = $this->storeLocales[$customer->getStoreId()];
        }
        if(empty($additionalData['_source'])) {
            $additionalData['_source'] = 'ACCOUNT';
        }
        if(empty($additionalData['_dob_normalized'])) {
            $additionalData['_dob_normalized'] = $customer->getDob();
        }

        return $additionalData;
    }
}
