<?php

namespace Benhauer\Salesmanago\Helper;

use \Magento\Framework\App\Helper\AbstractHelper;
use \Magento\Framework\App\Helper\Context;
use \Magento\Store\Model\StoreManagerInterface;

use \Magento\Framework\Stdlib\CookieManagerInterface;
use \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory;
use \Magento\Framework\Session\SessionManagerInterface;

use \Benhauer\Salesmanago\Model\Hooks as HooksModel;

class Tagger extends AbstractHelper
{
    const
        T_PURCHASE = 'prc',
        T_GUEST_PURCHASE = 'gprc',
        T_REGISTER = 'rgs',
        T_NEWSLETTER = 'nws',
        T_STORE_VIEW = 'strvw',
        T_COOKIE_NAME = 'smcntctgs',
        T_SESSION_NAME = 'sessionTags';

    private $model;
    private $contact;
    private $storeManager;
    private $cookieManager;
    private $cookieMetadataFactory;
    private $sessionManager;
    private $_checkOptInState = true;
    private $_cookieExpiredTime = 30960000;

    public function __construct(
        Context $context,
        StoreManagerInterface $storeManager,
        CookieManagerInterface $cookieManager,
        CookieMetadataFactory $cookieMetadataFactory,
        SessionManagerInterface $sessionManager
    ) {
        $this->storeManager = $storeManager;
        $this->cookieManager = $cookieManager;
        $this->sessionManager = $sessionManager;
        $this->cookieMetadataFactory = $cookieMetadataFactory;
        parent::__construct($context);
    }

    /**
     * Set Model;
     * @param object $model;
     * @return $this;
     */
    public function setModel($model)
    {
        $this->model = $model;
        return $this;
    }

    /**
     * @param bool $checkOptInState
     * @return $this
     */
    public function setCheckOptInState($checkOptInState = true)
    {
        $this->_checkOptInState = $checkOptInState;
        return $this;
    }

    /**
     * @param $tagsType = array()
     * @param $contact = array() of strings OR STRING
     * @param boolean $reqSubscriptionConfirmation;
     *
     * @return $contact
    */
    public function setTags(&$contact = null, $tagsType, $reqSubscriptionConfirmation = false)
    {
        if(!isset($this->contact)
            && $contact === null
        ) {
            return false;
        }

        if ($contact != null) {
            $this->contact = $contact;
        }

        if ($reqSubscriptionConfirmation && is_string($tagsType)) {
            $tagsType = '';
        }

        if ($reqSubscriptionConfirmation && is_array($tagsType)) {
            $tagsType = array_diff($tagsType, [self::T_NEWSLETTER]);
        }

        if ($tagsType == self::T_STORE_VIEW) {
            $this->_setSpacialTags();
        } else {
            if (is_array($tagsType)) {
                foreach($tagsType as $type) {
                    $this->_switcher($type);
                }
            } else {
                $this->_switcher($tagsType);
            }

            //Always tagged by Store View name
            $this->_setStoreView();
        }

        return $this->contact;
    }

    /**
     * Overwritte system methods which sets tags veriable to cookie;
     */
    protected function _setCookie($stringTagsToSet)
    {
        $metadata = $this->cookieMetadataFactory
            ->createPublicCookieMetadata()
            ->setDuration($this->_cookieExpiredTime)
            ->setPath($this->sessionManager->getCookiePath())
            ->setDomain($this->sessionManager->getCookieDomain());

        $this->cookieManager->setPublicCookie(self::T_COOKIE_NAME, $stringTagsToSet, $metadata);
    }

    /**
     * Overwritte system methods which get tags veriable from cookie;
     */
    protected function _getCookie()
    {
        return $this->cookieManager->getCookie(self::T_COOKIE_NAME);
    }

    /**
     * Overwritte system methods which set tags veriable to session;
     */
    protected function _setToSession($param)
    {
        return $this->sessionManager->setSessionTags($param);
    }

    /**
     * Overwritte system methods which get tags veriable from session;
    */
    protected function _getFromSession()
    {
        return $this->sessionManager->getSessionTags();
    }

    /**
     * Assight tags to contact;
    */
    protected function _assignToContact($param)
    {
        $this->contact['options']['tags'] = $param;
        return true;
    }

    /**
     * Get tags from contact
    */
    protected function _getFromContact()
    {
        return (isset($this->contact['options']) && isset($this->contact['options']['tags']))
            ? $this->contact['options']['tags']
            : false;
    }

    /**
     * Get email from contact
     */
    protected function _getContactEmail()
    {
        $email = $this->contact['data']['email'];
        return hash("sha256", $email);
    }

    /**
     * Get optin state from contact
     */
    protected function _getContactOptInState()
    {
        $state = (isset($this->contact['options']['forceOptIn'])
        && $this->contact['options']['forceOptIn'] == true)
            ? true
            : false;

        return $state;
    }

    /**
     * Get Store Name for tag
     */
    protected function _getStoreName()
    {
        return $this->storeManager->getStore()->getName();
    }

    /**
     * _switcher for tags type;
     * @param string $type;
     */
    private function _switcher($type)
    {
        switch ($type) {
            case self::T_REGISTER:
                $this->_setRegister();
                break;
            case self::T_PURCHASE:
                $this->_setPurchase();
                break;
            case self::T_GUEST_PURCHASE:
                $this->_setGuestPurchase();
                break;
            case self::T_NEWSLETTER:
                $this->_setNewsletter();
                break;
        }
    }

    /**
     * Sets & Check set purchase tag;
     */
    private function _setPurchase()
    {
        $tag = strtolower($this->model->getTags(HooksModel::T_PURCHASE));
        $this->_customCheck($tag, self::T_PURCHASE);
    }

    /**
     * Sets & Check set guest purchase tag;
     */
    private function _setGuestPurchase()
    {
        $tag = strtolower($this->model->getTags(HooksModel::T_GUEST_PURCHASE));
        $this->_customCheck($tag, self::T_GUEST_PURCHASE);
    }

    /**
     * Sets & Check register tag
     */
    private function _setRegister()
    {
        $tag = strtolower($this->model->getTags(HooksModel::T_REGISTER));
        $this->_customCheckWithCookie($tag, self::T_REGISTER);
    }

    /**
     * Sets & Check Newsletter Tag
     */
    private function _setNewsletter()
    {
        if ($this->_checkOptInState && $this->_getContactOptInState()) {
            $tag = strtolower($this->model->getTags(HooksModel::T_NEWS));
            $this->_customCheckWithCookie($tag, self::T_NEWSLETTER);
        } elseif (!$this->_checkOptInState) {
            $tag = strtolower($this->model->getTags(HooksModel::T_NEWS));
            $this->_customCheckWithCookie($tag, self::T_NEWSLETTER);
        }
    }

    /**
     * Sets tag Store View Name
     */
    private function _setStoreView()
    {
        $tag = str_replace(' ', '_', strtolower($this->_getStoreName()));
        $this->_customCheckWithCookie($tag, self::T_STORE_VIEW);
    }

    /**
     * Set tags as newsletter and store name;
    */
    private function _setSpacialTags()
    {
        $this->_setStoreView();
        $this->_setNewsletter();
    }

    /**
     * Checks if tag already exist in customer tags, session variable, cookie;
     * Checks type of tags (repeatable or non repeatable);
     * @param string $tag - tag;
     * @param string shortTag - tag;
     * @return array $this->contact;
     */
    private function _customCheckWithCookie($tag, $shortTag)
    {
        $tag = strtolower($tag);

        $contactTags = $this->_getContactTagsFromWebStorage();
        $tagsFromContact = $this->_getFromContact();

        if ($contactTags) {
            $contactTags = explode(',', $contactTags);

            if (!in_array($shortTag, $contactTags)) {
                $contactTags[] = $shortTag;
                $contactTags = implode(',', $contactTags);
                $this->_setContactTagsToSession($contactTags);

                if ($tagsFromContact
                    && !empty($tagsFromContact)
                    && !empty($tag)
                ) {
                    $this->_assignToContact($tagsFromContact.','.$tag);
                } elseif(!empty($tag)) {
                    $this->_assignToContact($tag);
                }
            }
        } else {
            $this->_setContactTagsToSession($shortTag);
            if ($tagsFromContact
                && !empty($tagsFromContact)
                && !empty($tag)
            ) {
                $this->_assignToContact($tagsFromContact.','.$tag);
            } elseif(!empty($tag)) {
                $this->_assignToContact($tag);
            }
        }

        if ($this->_getFromSession()) {
            $this->_setContactTagsToCookie($this->_getFromSession());
        }

        return $this->contact;
    }

    /**
     * Checks if tag already exist in customer tags
     * @param string $tag - tag
     */
    private function _customCheck($tag)
    {
        $tag = strtolower($tag);
        $contactTags = $this->_getFromContact();
        if ($contactTags) {
            if (!in_array($tag, explode(',', $contactTags))) {
                $this->_assignToContact($contactTags.','.$tag);
            }
        } else {
            $this->_assignToContact($tag);
        }
    }

    /**
     * @return mixed - array if cookie exist otherwise false.
     */
    private function _getParseCookie()
    {
        $tagsCookie = $this->_getCookie();

        return (empty($tagsCookie) || !isset($tagsCookie))
            ? ''
            : json_decode(stripslashes($tagsCookie), true);
    }

    /**
     * @return mixed - array if session variable exist otherwise false.
     */
    private function _getParseFromSession()
    {
        $tagsSession = $this->_getFromSession();

        return (empty($tagsSession) || !isset($tagsSession))
            ? ''
            : json_decode(stripslashes($tagsSession), true);
    }

    /**
     * Get tags form SESSION variable or Cookies depends which exist,
     * for $this->Contact->email
     *
     * @return string $tags
     */
    private function _getContactTagsFromWebStorage()
    {
        $contactTags = $this->_getContactTagsFromCookie();

        $contactTags = !empty($contactTags)
            ? $contactTags
            : $this->_getContactTagsFromSession();

        return strtolower($contactTags);
    }

    /**
     * Get tags from cookie by $this->Contact->email
     * @return string $tagsFromCookie - string of tags for contact email;
     */
    private function _getContactTagsFromCookie()
    {
        $contactEmail = $this->_getContactEmail();
        $tagsFromCookie = $this->_getParseCookie();

        if (!empty( $tagsFromCookie )) {
            $tagsFromCookie = (array_key_exists($contactEmail, $tagsFromCookie))
                ? $tagsFromCookie[$contactEmail]
                : '';
        }

        return strtolower($tagsFromCookie);
    }

    /**
     * Set tags for $this->Contact->email to Cookie from SESSION variable;
     * @param string $sessionVariableWithAllTags - json from session variable
     * @return boolean _setCookie()
     */
    private function _setContactTagsToCookie($sessionVariableWithAllTags)
    {
        $contactEmail = $this->_getContactEmail();

        $tagsCookie = $this->_getParseCookie();
        $tagsSession = json_decode($sessionVariableWithAllTags, true);

        if ($tagsCookie == null) {
            return $this->_setCookie($sessionVariableWithAllTags);
        }

        if ((array_key_exists($contactEmail, $tagsCookie)
                && array_key_exists($contactEmail, $tagsSession))

            || (!array_key_exists($contactEmail, $tagsCookie)
                && array_key_exists($contactEmail, $tagsSession)
            )
        ) {
            $tagsCookie[$contactEmail] = $tagsSession[$contactEmail];
        }

        return $this->_setCookie(json_encode($tagsCookie));
    }

    /**
     * Get tags from SESSION variable by $this->Contact->email
     * @return string $sessionTags - string of tags for $this->Contact->email;
     */
    private function _getContactTagsFromSession()
    {
        $contactEmail = $this->_getContactEmail();
        $sessionTags = $this->_getParseFromSession();

        if (!empty( $sessionTags )) {
            $sessionTags = (array_key_exists($contactEmail, $sessionTags))
                ? $sessionTags[$contactEmail]
                : '';
        }

        return strtolower($sessionTags);
    }

    /**
     * Set tags for $this->Contact->email to Session variable;
     * @param string $string - tags for $this->Contact->email
     * @return string/json
     */
    private function _setContactTagsToSession($string)
    {
        $contactEmail = $this->_getContactEmail();

        if (!$this->sessionManager->getSessionTags()) {
            $this->sessionManager->setSessionTags(json_encode(array($contactEmail => $string)));
            return $this->sessionManager->getSessionTags();
        }

        $sessionUnJsonVariable = json_decode($this->sessionManager->getSessionTags(), true);

        if (!is_array($sessionUnJsonVariable)) {
            $this->sessionManager->setSessionTags('');
            $this->_setContactTagsToSession($string);
        }

        $sessionUnJsonVariable[$contactEmail] = $string;
        $this->sessionManager->setSessionTags(json_encode($sessionUnJsonVariable));
        return $this->sessionManager->getSessionTags();
    }
}
