<?php

namespace Benhauer\Salesmanago\Observer;

use \Psr\Log\LoggerInterface;

use \Magento\Framework\Stdlib\DateTime\TimezoneInterface as MagentoDateTime;
use \Magento\Framework\Event\Observer;
use \Magento\Framework\Event\ObserverInterface as MagentoObserverInterface;
use \Magento\Checkout\Model\Session;
use \Magento\Store\Api\Data\StoreInterface;
use \Magento\Framework\UrlInterface;
use \Magento\Newsletter\Model\Subscriber;
use \Magento\Customer\Api\CustomerRepositoryInterface;
use \Magento\Framework\Stdlib\CookieManagerInterface;
use \Magento\Store\Model\StoreManagerInterface;

use \SALESmanago\Provider\UserProvider;
use \SALESmanago\Factories\FactoryOrganizer;

use \Benhauer\Salesmanago\Model\Hooks as HooksModel;
use \Benhauer\Salesmanago\Model\Contact;
use \Benhauer\Salesmanago\Helper\Tagger;

abstract class AbstractObserver implements MagentoObserverInterface
{
    const
        UPSERT_FLAG_STATUS = 'statusUpsert',
        UPSERT_FLAG_ALL_DATA = 'allDataUpser',
        UPSERT_FLAG_EMAIL = 'emailUpsert',
        UPSERT_FLAG_NO_CHECK = 'noCheck';

    public $session;
    public $store;
    public $subscriber;
    public $model;
    public $customerRepositoryInterface;
    public $objDate;
    public $factoryOrganizer;
    public $cookieManager;
    public $tagger;
    public $logger;
    public $CustomerSession;

    public function __construct(
        Session $session,
        StoreInterface $store,
        Subscriber $subscriber,
        HooksModel $model,
        CustomerRepositoryInterface $customerRepositoryInterface,
        CookieManagerInterface $cookieManager,
        MagentoDateTime $objDate,
        FactoryOrganizer $factoryOrganizer,
        Tagger $tagger,
        LoggerInterface $logger,
        Contact $modelContact,
        StoreManagerInterface $storeManager
    ) {
        $this->session                     = $session;
        $this->store                       = $store;
        $this->subscriber                  = $subscriber;
        $this->customerRepositoryInterface = $customerRepositoryInterface;
        $this->model                       = $model;
        $this->factoryOrganizer            = $factoryOrganizer;
        $this->objDate                     = $objDate;
        $this->cookieManager               = $cookieManager;
        $this->tagger                      = $tagger;
        $this->logger                      = $logger;
        $this->modelContact                = $modelContact;
        $this->storeManager                = $storeManager;
    }

    public function execute(Observer $observer)
    {
        $this->setStoreViewForModel();
        if ($this->model->getUserSettings(null) == false) {
            return true;
        }
        $this->hookAct($observer);
    }

    /**
     * Main method
     * @param Observer $observer
     * @return mixed
    */
    public function hookAct(Observer $observer)
    {
        return $observer;
    }

    /**
     * @param int $customerId
     * @return array
     * @throws \Exception
     */
    public function getCustomerById($customerId)
    {
        try {
            $Contact = $this->modelContact->getContactByCustomerId($customerId);
            $Contact->getOptions()
                ->setAdditional(
                    [HooksModel::SYNC_RULE => $this->model->getSynchronizeRule()]
                );
            $contactData = $Contact->getDataOptions();
            return $contactData;
        } catch (\Exception $e) {
            $this->logger->critical($e->getMessage());
        }
    }

    public function getCartModified($observer)
    {
        try {
            $cart = $observer->getCart()->getQuote();
            $cartItems = $cart->getAllItems();

            $prods = [
                'ids' => [],
                'names' => [],
                'qty' => [],
                'sku' => []
            ];

            $dateTime = $this->objDate->date();

            foreach ($cartItems as $item) {
                if ($item->getPrice() > 0
                    && $item->getPrice() != null
                ) {
                    array_push($prods['ids'], $item->getProductId());
                    array_push($prods['names'], $item->getName());
                    array_push($prods['qty'], $item->getQty());
                    array_push($prods['sku'], $item->getSku());
                }
            }

            $contactEvent = [
                'date' => $dateTime->format('c'),
                'description' => '',
                'products' => implode(",", $prods['ids']),
                'location' => $this->model->getSMStoreId(UrlInterface::URL_TYPE_WEB),
                'value' => $cart->getGrandTotal(),
                'detail1' => implode(",", $prods['names']),
                'detail2' => implode("/", $prods['qty']),
                'detail3' => implode(",", $prods['sku']),
                'detail4' => $this->store->getLocaleCode(),
                'externalId' => $cart->getEntityId()
            ];

            return $this->model->filterData($contactEvent);
        } catch (\Exception $e) {
            $this->logger->critical($e->getMessage());
        }
    }

    public function getProductsFromOrder($order, $quoteFactory)
    {
        try {
            $dateTime = $this->objDate->date();

            $prods = [
                'ids' => [],
                'names' => [],
                'qty' => [],
                'sku' => []
            ];

            $quote = $quoteFactory->create()->load($order->getQuoteId());

            $quoteItems = $quote->getAllVisibleItems();

            foreach ($quoteItems as $item) {
                if ($item->getPrice() > 0
                    && $item->getPrice() != null
                ) {
                    array_push($prods['ids'], $item->getProductId());
                    array_push($prods['names'], $item->getName());
                    array_push($prods['qty'], $item->getQty());
                    array_push($prods['sku'], $item->getSku());
                }
            }

            $products = [
                'date' => $dateTime->format('c'),
                'description' => $order->getPayment()->getMethod(),
                'products' => implode(",", $prods['ids']),
                'location' => $this->model->getSMStoreId(UrlInterface::URL_TYPE_WEB),
                'value' => $order->getGrandTotal(),
                'detail1' => implode(",", $prods['names']),
                'detail2' => implode("/", $prods['qty']),
                'detail3' => implode(",", $prods['sku']),
                'detail4' => $order->getBaseCurrencyCode(),
                'externalId' => $order->getEntityId(),
                'shopDomain' => $this->store->getBaseUrl(UrlInterface::URL_TYPE_WEB)
            ];

            return $products;
        } catch (\Exception $e) {
            $this->logger->critical($e->getMessage());
        }
    }

    public function createContactCookie($response)
    {
        try {
            if ($response['success']) {
                $connectSalesManago = $this->factoryOrganizer->getInst(
                    FactoryOrganizer::CONNECT_SALESMANAGO_C,
                    UserProvider::initSettingsUser($this->model)
                );

                $connectSalesManago->createCookie(
                    FactoryOrganizer::COOKIES_CLIENT,
                    $response['contactId']
                );
            }
        } catch (\Exception $e) {
            $this->logger->critical($e->getMessage());
        }
    }

    /**
     * Creates smevent cookie for CART event;
     * @param $response - Sm response with smevent id;
    */
    public function createEventCookie($response)
    {
        try {
            if ($response['success']) {
                $connectSalesManago = $this->factoryOrganizer->getInst(
                    FactoryOrganizer::CONNECT_SALESMANAGO_C,
                    UserProvider::initSettingsUser($this->model)
                );
                $extEventCookieLiveTime = intval($this->model->getExtEventCookieLifeTime());

                $time = $this->objDate->date();
                $period = $time->getTimestamp() + (3600 * $extEventCookieLiveTime);

                $connectSalesManago->createCookie(
                    FactoryOrganizer::COOKIES_EXT_EVENT,
                    $response['eventId'],
                    $period
                );
            }
        } catch (\Exception $e) {
            $this->logger->critical($e->getMessage());
        }
    }

    public function deleteEventCookie($response)
    {
        try {
            if ($response['success']) {
                $connectSalesManago = $this->getConnectSalesManago();

                $connectSalesManago->deleteCookie(
                    FactoryOrganizer::COOKIES_EXT_EVENT
                );
            }
        } catch (\Exception $e) {

        }
    }

    public function filterData($data)
    {
        $data = array_map(function ($var) {
            return is_array($var) ? $this->filterData($var) : $var;
        }, $data);
        $data = array_filter($data, function ($value) {
            return !empty($value) || $value === false;
        });
        return $data;
    }

    /**
     * Subscribe contact to magento newsletter if "syncRule" & optIn state constact in SALESmanago
     *
     * @param $email
     * @param $responceAfterUpsert
     * @return bool
     */
    public function synchronizeContactFromSm($email, $responceAfterUpsert)
    {
        try {
            if (!isset($responceAfterUpsert['synchronizeFromSales'])) {
                return true;
            }

            if (!$responceAfterUpsert['synchronizeFromSales']) {
                return true;
            }

            $checkSubscriber = $this->subscriber->loadByEmail($email);

            if ($this->session->getLastSyncEmail()
                && ($this->session->getLastSyncEmail() == $email)
            ) {
                return true;
            }

            if ($checkSubscriber->isSubscribed()) {
                return true;
            }

            $this->session->setProgrammaticallyNewsletterStatus(true);

            if ($checkSubscriber->getOrigData() == null) {
                //next one subscribing customer which don't exist in subscribers:
                $checkSubscriber->subscribe($email);
                $checkSubscriber->setStatus(Subscriber::STATUS_SUBSCRIBED)->save();
            } else {
                //next one change status:
                $checkSubscriber->setStatus(Subscriber::STATUS_SUBSCRIBED)->save();
            }

            $this->session->setLastSyncEmail($email);

            return true;
        } catch (\Exception $e) {
            $this->logger->critical($e->getMessage());
        }
    }

    public function getConnectSalesManago()
    {
        $connectSalesManago = $this->factoryOrganizer->getInst(
            FactoryOrganizer::CONNECT_SALESMANAGO_C,
            UserProvider::initSettingsUser($this->model)
        );

        return $connectSalesManago;
    }

    /**
     * @param int $contact - with key data & options;
     * @return boolean
     */
    public function isOptIn($contact)
    {
        if(!isset($contact['options'])){
            return false;
        }

        if(!isset($contact['options']['forceOptIn'])){
            return false;
        }

        return $contact['options']['forceOptIn'];
    }

    /**
     * Set store view id from $this->requestPrams to each model;
     */
    public function setStoreViewForModel()
    {
        $storeId = $this->storeManager->getStore()->getId();
        if ($storeId != null && $storeId != 0) {
            $this->model->setScopeId($storeId);
            $this->model->setScope('store');
        }
    }

    /**
     * @param array $contact
     * @param object $controller
     */
    protected function upsertContact($contact, $controller, $flag = self::UPSERT_FLAG_NO_CHECK)
    {
        $response = $controller->contactUpsert(
            $contact['data'],
            $contact['options'],
            ''
        );

        $this->createContactCookie($response);

        $this->synchronizeContactFromSm(
            $contact['data']['email'],
            $response
        );

        return $response;
    }

    /**
     * @param string $email
     * @param string $flag $this CONST;
     */
    protected function setCheckUpsertFlags($email, $flag)
    {
        if ($flag == self::UPSERT_FLAG_NO_CHECK) {
            return true;
        }

        $arrFlag = json_decode($this->session->getUpsertFlags(), true);

        if (boolval($arrFlag) && in_array($flag, $arrFlag[$email])) {
            return false;
        } else if (boolval($arrFlag) && !in_array($flag, $arrFlag[$email])) {
            array_push($arrFlag[$email], $flag);
        } else if (!boolval($arrFlag)) {
            $arrFlag = [$email => [] ];
            array_push($arrFlag[$email], $flag);
        }

        $this->session->setUpsertFlags(json_encode($arrFlag));
        return true;
    }
}
