<?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\Config\Source\JobStatus;
use FiloBlu\ActiveCampaign\Model\Resource\ActiveCampaign;
use Magento\Customer\Api\AddressRepositoryInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Newsletter\Model\SubscriberFactory;
use Magento\Store\Model\Service\StoreConfigManager;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Newsletter\Model\Subscriber;

/**
 * Class Customer
 * @package FiloBlu\ActiveCampaign\Model\Entity\Export
 */
class Customer extends AbstractEntityExport {

    /**
     * @var ResourceConnection
     */
    protected $resourceConnection;
    /**
     * @var Data
     */
    protected $helperData;
    /**
     * @var JobsRepositoryInterface
     */
    protected $jobsRepository;
    /**
     * @var string
     */
    protected $entity = EntityType::ENTITY_CUSTOMER;
    /**
     * @var string
     */
    protected $direction = DirectionType::DIRECTION_EXPORT;
    /**
     * @var Subscriber
     */
    protected $subscriber;
    /**
     * @var CustomerRepositoryInterface
     */
    protected $customerRepository;
    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;
    /**
     * @var AddressRepositoryInterface
     */
    protected $addressRepository;
    /**
     * @var \Magento\Framework\Stdlib\DateTime\DateTime
     */
    protected $date;
    /**
     * @var StoreConfigManager
     */
    protected $storeConfigManager;
    /**
     * @var array|mixed
     */
    private $genderMapping;
    /**
     * @var array
     */
    protected $storeLocales = [];

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


    /**
     * @return array|mixed
     */
    public function getAllIncrementalIds(): array
    {
        if(!$this->helperData->isCustomerEnabled()) {
            return [];
        }
        $connection = $this->resourceConnection->getConnection();
        $tableName = $this->resourceConnection->getTableName('customer_entity');
        $tableNameJobs = $this->resourceConnection->getTableName(JobsInterface::DB_TABLE);

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

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

        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 LocalizedException
     * @throws NoSuchEntityException
     */
    public function executeProcess(array $job): bool
    {
        if(!$this->helperData->isCustomerEnabled()) {
            $this->jobsRepository->setStatus($this->options,$job['entity_id'],JobStatus::JOB_STATUS_SKIPPED,'The customer job is not active');
            return false;
        }
        $this->init();
        $customerData = $this->getCustomerData($job);
        $this->executeContactRestProcess($customerData,$job);
        return true;
    }

    /**
     * @param array $job
     * @return array
     * @throws LocalizedException
     * @throws NoSuchEntityException
     */
    public function getCustomerData(array $job): array
    {
        $data = [];
        $customerMapping = $this->helperData->getCustomerMapping();
        $customerBillingMapping = $this->helperData->getCustomerBillingAddressMapping();
        $customerShippingMapping = $this->helperData->getCustomerShippingAddressMapping();
        $customer = $this->customerRepository->getById($job['entity_id']);
        $subscriber = $this->subscriber->create()->loadByCustomerId($job['entity_id']);
        $additionalData = [
            '_customer_id' => $customer->getId(),
            '_store_code' => $this->storeManager->getStore($customer->getStoreId())->getCode(),
            '_store_name' => $this->storeManager->getStore($customer->getStoreId())->getName(),
            '_website_name' => $this->storeManager->getStore($customer->getStoreId())->getWebsite()->getName(),
            '_website_code' => $this->storeManager->getStore($customer->getStoreId())->getWebsite()->getCode(),
            '_type' => 'CUSTOMER',
            '_subscriber_status_normalized' => ($subscriber->isSubscribed() == 1) ? '1' : '0',
            '_gender_normalized' => $this->getGenderNormalized($customer->getGender()),
            '_created_at_normalized' => $this->helperData->getACDatetimeFormat($customer->getCreatedAt()),
            '_updated_at_normalized' => $this->helperData->getACDatetimeFormat($customer->getUpdatedAt()),
            '_language' => $this->storeLocales[$customer->getStoreId()],
            '_language_iso2' => strtolower(substr($this->storeLocales[$customer->getStoreId()], 0,2)),
            '_source' => 'ACCOUNT'
        ];

        $result = [
            'email' => $customer->getEmail(),
            'firstName' => $customer->getFirstname(),
            'lastName' => $customer->getLastname()
        ];

        foreach ($customerMapping as $key => $val) {
            if($this->helperData->isAdditional($key)){
                if(!empty($additionalData[$key])) {
                    $data[$key] = [
                        'field' => $val,
                        'value' => $additionalData[$key]
                    ];
                }
                continue;
            }
            if(!empty($customer->{'get'.$this->helperData->snakeToCamelCase($key)}())) {
                $data[$key] = [
                    '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->getCustomerList(),
                        'status' => ($this->helperData->isCustomerListEnabled()) ? '1' : '2'
                    ]
                ]
            ]
        ];

        return $request;
    }

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

    /**
     * @param $genderField
     * @return mixed|string
     */
    public function getGenderNormalized($genderField) {
        $genderNormalized = '';
        if(!empty($genderField)){
            $gender = $this->getGenderMapping();
            if(count($gender) > 0 && isset($gender[$genderField])){
                $genderNormalized = $gender[$genderField];
            }
        }
        return $genderNormalized;
    }
}