<?php
/**
 * Copyright © 2015 FiloBlu . All rights reserved.
 */

namespace FiloBlu\Flow\Helper;

use Exception;
use Magento\Catalog\Api\Data\ProductAttributeInterface;
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
use Magento\Catalog\Model\Product\Media\Config;
use Magento\Eav\Api\AttributeOptionManagementInterface;
use Magento\Eav\Api\Data\AttributeOptionInterfaceFactory;
use Magento\Eav\Api\Data\AttributeOptionLabelInterfaceFactory;
use Magento\Eav\Model\Entity\Attribute\OptionLabel;
use Magento\Eav\Model\Entity\Attribute\Source\Table;
use Magento\Eav\Model\Entity\Attribute\Source\TableFactory;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Filesystem;
use Magento\Swatches\Helper\Media;
use Magento\Swatches\Model\Swatch;

/**
 * Class Attribute
 * @package FiloBlu\Flow\Helper
 */
class Attribute extends AbstractHelper
{

    /**
     * Base64 encoded swatch placeolder image
     *
     * @var string
     */
    const PLACEHOLDER_SWATCH_IMAGE_BASE64 =
        '/9j/4AAQSkZJRgABAQEASABIAAD/4QCARXhpZgAASUkqAAgAAAAEABoBBQABAAAAPgAAABsBBQAB
AAAARgAAACgBAwABAAAAAgAAAGmHBAABAAAATgAAAAAAAABIAAAAAQAAAEgAAAABAAAAAwAAkAcA
BAAAADAyMTAAoAcABAAAADAxMDABoAMAAQAAAP//AAAAAAAA/+EDzGh0dHA6Ly9ucy5hZG9iZS5j
b20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRj
emtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHht
bG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+Cgog
PHJkZjpEZXNjcmlwdGlvbiB4bWxuczp4bXBNTT0naHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4w
L21tLyc+CiAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjlFMjExRDI4Q0Y4RjEx
RTY4MzMwODdFNTI0NkY4QjQ4PC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgPHhtcE1NOklu
c3RhbmNlSUQ+eG1wLmlpZDpCOTRCNDRGRjMxOTAxMUU4OTU2MTkyQUQ4MDY1MjEyQzwveG1wTU06
SW5zdGFuY2VJRD4KICA8eG1wTU06T3JpZ2luYWxEb2N1bWVudElEPnhtcC5kaWQ6OUUyMTFEMjhD
RjhGMTFFNjgzMzA4N0U1MjQ2RjhCNDg8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICA8eG1w
TU06RG9jdW1lbnRJRCByZGY6cmVzb3VyY2U9J3htcC5kaWQ6Qjk0QjQ1MDAzMTkwMTFFODk1NjE5
MkFEODA2NTIxMkMnIC8+CiAgPHhtcE1NOkluc3RhbmNlSUQ+eG1wLmlpZDpCOTRCNDRGRjMxOTAx
MUU4OTU2MTkyQUQ4MDY1MjEyQzwveG1wTU06SW5zdGFuY2VJRD4KICA8eG1wTU06RGVyaXZlZEZy
b20gcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogIDwveG1wTU06RGVyaXZlZEZyb20+CiA8L3Jk
ZjpEZXNjcmlwdGlvbj4KCiA8cmRmOkRlc2NyaXB0aW9uIHhtbG5zOnhtcD0naHR0cDovL25zLmFk
b2JlLmNvbS94YXAvMS4wLyc+CiAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0Mg
MjAxNSAoV2luZG93cyk8L3htcDpDcmVhdG9yVG9vbD4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKPC9y
ZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0ncic/Pgr/2wBDAAEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQECAgICAgICAgICAgMDAwMDAwMDAwP/2wBD
AQEBAQEBAQIBAQICAgECAgMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMD
AwMDAwMDAwP/wgARCAA8ADwDAREAAhEBAxEB/8QAGgABAAMBAQEAAAAAAAAAAAAAAAcJCggDBf/E
ABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAb/AAAAAU/FfRM5oZPYAECE9mYsvFOrw
ADmEzxmqc+mAAZRTT0SAAACpktmAAAMsxqZAAAAAB//EAB8QAAEEAwADAQAAAAAAAAAAAAYEBQcI
AgMgAAEwFf/aAAgBAQABBQL5TpZxbGhFlN1qnPTGVxHnB117MNuHHuMgraY+XNYmJrPYL3rVMQcy
tKjBE40BA5nZmQ0SNK3I+bEL15xP4uMMoaw9ENXf3pU7PY+Pllgfp//EABQRAQAAAAAAAAAAAAAA
AAAAAGD/2gAIAQMBAT8BAf/EABQRAQAAAAAAAAAAAAAAAAAAAGD/2gAIAQIBAT8BAf/EAC8QAAEE
AAQBDAEFAAAAAAAAAAIBAwQFBhESExQABxUWICEiMDEyQWEzEEJEUYH/2gAIAQEABj8C8qTg3DuH
I024jRYkiRa3Dzi1zXHRxkMhHr4ZtSJSo24mZE81kXdpL15dL18DEDVWqbqPVvNyzJrEbyzzSZJo
py7eXzu/7yjVHOhDhPVz7wxjxFXRShTa1wiQN+ygNqUaTFbL37INOAmaoh+3kDrRg404AuNuNkhg
4BpqAwMcxICFc0VPXszcezKSNZYnlhCbCfYiMwa8IEduMx0ZHdFWIbulvNXUTezVfFp7v0orGsZj
xbS9o3Zd4ywIhvusTTjxLJ8B/kSwQ2yP9+x/efLm8dn6+I6swG0Vz3LFYQmIBfaFBbbVPrtOXduv
EzpG5Ho6VpxAlW84Rz2xXItmIxqQn3lRUbFfkyASnYrxQ861QNzGivrRsSajMRmsljYaoALUiPcP
kKe7ZBd1xSMkRyJXwWG4sKDGYhw4zSaWo8WM0LMdhsfhtpoEFPpO0/hl2UYRIljh7CVUK+IIYTgg
LKeFvNBU3LGwcNV9VHSnwnKuw3h+GEGqq2BYjtDlrNfV2RIcyRX5clxVNxxe8zVV7Z85nXnhdeJK
rEPQvVnfy6MchOcJ0l1gZ/Pwf5Njw6vauXkWdzEwRi+VTuY/rpgWsfDVy/WnDCfCM5Qzm4RRSjCI
qqnq0oieb//EAB4QAQACAgMAAwAAAAAAAAAAAAERIQAgMDFBUbHR/9oACAEBAAE/IeKqMfcXRmLk
gMO/SyZVX3zbGE+mrVNlMuWMMh5UZVTAoggZNaaM+18bV+iBgch0iS61kogUCyEyg7GT9FBXz1De
00cgFWaSOlcBkRTJWpOg9WJfMr8IASZWV75sN2vp6RoLIErERj4jQKtZM2Tvf9oWjo6PtBuJ+L1K
eji2FYOX/9oADAMBAAIAAwAAABAAAAAASAAAAQAAQAAASAAAQAAAQAAAAAD/xAAUEQEAAAAAAAAA
AAAAAAAAAABg/9oACAEDAQE/EAH/xAAUEQEAAAAAAAAAAAAAAAAAAABg/9oACAECAQE/EAH/xAAe
EAEAAgICAwEAAAAAAAAAAAABABEgITAxEEFh8P/aAAgBAQABPxDiv1+b4ws9xIJO6fMifwKUy9JZ
EUFd1Kdu+sDsqEthlTFIiOLBjJES8ag5G8JtACugJMoGGLL3Z9Tk2ZHa4bW4lvAoH4q8kMmOlDx9
FpAgUhUB3PIbJKXAWgestDBV2xkQZScS7018YYwqKbuhl+vSqCup39bgm9a/JOi/QZROT//Z';

    /**
     * @var
     */
    const PLACEHOLDER_SWATCH_IMAGE_NAME = 'default_placeholder.jpg';
    /**
     * @var
     */
    protected $_connection;
    /**
     * @var
     */
    protected $_setup;

    /**
     * @var ProductAttributeRepositoryInterface
     */
    protected $attributeRepository;

    /**
     * @var array
     */
    protected $attributeValues;

    /**
     * @var TableFactory
     */
    protected $tableFactory;

    /**
     * @var AttributeOptionManagementInterface
     */
    protected $attributeOptionManagement;

    /**
     * @var AttributeOptionLabelInterfaceFactory
     */
    protected $optionLabelFactory;

    /**
     * @var AttributeOptionInterfaceFactory
     */
    protected $optionFactory;

    /**
     * @var Monolog\Logger
     */
    protected $_logger;

    /**
     * @var \Magento\Swatches\Helper\Data
     */
    protected $swatchHelper;

    /**
     * @var Media
     */
    protected $swatchMediaHelper;

    /**
     * @var Config
     */
    protected $mediaConfig;

    /**
     * @var Filesystem
     */
    protected $fileSystem;

    /**
     * Attribute constructor.
     * @param LoggerProvider $loggerProvider
     * @param Context $context
     * @param ProductAttributeRepositoryInterface $attributeRepository
     * @param TableFactory $tableFactory
     * @param AttributeOptionManagementInterface $attributeOptionManagement
     * @param AttributeOptionLabelInterfaceFactory $optionLabelFactory
     * @param AttributeOptionInterfaceFactory $optionFactory
     * @param \Magento\Swatches\Helper\Data $swatchHelper
     * @param Media $swatchMediaHelper
     * @param Config $mediaConfig
     * @param Filesystem $fileSystem
     */
    public function __construct(
        LoggerProvider $loggerProvider,
        Context $context,
        ProductAttributeRepositoryInterface $attributeRepository,
        TableFactory $tableFactory,
        AttributeOptionManagementInterface $attributeOptionManagement,
        AttributeOptionLabelInterfaceFactory $optionLabelFactory,
        AttributeOptionInterfaceFactory $optionFactory,
        \Magento\Swatches\Helper\Data $swatchHelper,
        Media $swatchMediaHelper,
        Config $mediaConfig,
        Filesystem $fileSystem
    ) {
        parent::__construct($context);

        $this->_logger = $loggerProvider->getLogger();
        $this->attributeRepository = $attributeRepository;
        $this->tableFactory = $tableFactory;
        $this->attributeOptionManagement = $attributeOptionManagement;
        $this->optionLabelFactory = $optionLabelFactory;
        $this->optionFactory = $optionFactory;
        $this->swatchHelper = $swatchHelper;
        $this->swatchMediaHelper = $swatchMediaHelper;
        $this->mediaConfig = $mediaConfig;
        $this->fileSystem = $fileSystem;
    }

    /**
     * Add attribute translation
     *
     * @param $option
     * @return bool
     * @throws Exception
     */
    public function addAttributeTranslation($option)
    {
        $optionId = $option['option_id'];
        $optionValue = $option['value'];
        $storeId = $option['store_id'];

        if (empty($optionValue)) {
            throw new Exception('Option value cannot be empty');
        }

        if (empty($storeId)) {
            throw new Exception("I can't add translation for default store view");
        }

        if (empty($optionId)) {
            throw new Exception("I can't add translation for unknown option");
        }

        $optionValueTable = 'eav_attribute_option_value';
        //we'd might update
        $select = $this->getConnection()->select()
            ->from($optionValueTable)
            ->where('option_id = ' . $optionId)
            ->where('store_id = ' . $storeId);

        $result = $this->getConnection()->fetchRow($select);

        if ($result) {
            $where = [
                'option_id =?' => $option['option_id'],
                'store_id =?' => $option['store_id']
            ];
            $this->getConnection()->update($optionValueTable, $option, $where);
            return true;
        }
        $this->getConnection()->insert($optionValueTable, $option);
        return true;
    }

    /**
     * @return AdapterInterface
     */
    protected function getConnection()
    {
        if (empty($this->_connection)) {
            $this->_connection = $this->getSetup()->getConnection(ResourceConnection::DEFAULT_CONNECTION);
        }
        return $this->_connection;
    }

    /**
     * @return mixed
     */
    protected function getSetup()
    {
        if (empty($this->_setup)) {
            $this->_setup = ObjectManager::getInstance()->get('\Magento\Framework\App\ResourceConnection');
        }
        return $this->_setup;
    }

    /**
     * Find or create a matching attribute option
     *
     * @param $attributeCode
     * @param $label
     * @return false|int
     * @throws Exception
     */
    public function createOrGetId($attributeCode, $label)
    {
        $label = trim($label);
        if ($label === '') {
            throw new LocalizedException(
                __('Label for %1 must not be empty.', $attributeCode)
            );
        }

        // Does it already exist?
        $optionId = $this->getOptionId($attributeCode, $label);

        if (!$optionId) {
            // If no, add it.

            /** @var OptionLabel $optionLabel */
            $optionLabel = $this->optionLabelFactory->create();
            $optionLabel->setStoreId(0);
            $optionLabel->setLabel($label);

            $option = $this->optionFactory->create();
            $option->setLabel($label);
            $option->setStoreLabels([$optionLabel]);
            $option->setSortOrder(0);
            $option->setIsDefault(false);

            try {
                $this->attributeOptionManagement->add(
                    \Magento\Catalog\Model\Product::ENTITY,
                    $this->getAttribute($attributeCode)->getAttributeId(),
                    $option
                );
            } catch (\Exception $e){
                // Magento failed adding the new attribute value!
                // Then next step we will try to direct sql the insert!
            }

            // Get the inserted ID. Should be returned from the installer, but it isn't.
            $optionId = $this->getOptionId($attributeCode, $label, true);
        }

        // Still can't insert option? Try direct sql then
        if (!$optionId) {
            $option_direct = [];
            $option_direct['attribute_id'] = $this->getAttribute($attributeCode)->getAttributeId();
            $option_direct['value'] = $label;
            $option_direct['store_id'] = 0;
            $this->addAttributeOption($option_direct);
            // Get the inserted ID. Should be returned from the installer, but it isn't.
            $optionId = $this->getOptionId($attributeCode, $label, true);
        }

        if ($optionId && $this->swatchHelper->isSwatchAttribute($this->getAttribute($attributeCode))) {
            $this->addSwatchOption($optionId, $this->getAttribute($attributeCode), $label);
        }

        return $optionId;
    }

    /**
     * Find the ID of an option matching $label, if any.
     *
     * @param $attributeCode Attribute code
     * @param $label Label to find
     * @param bool $force If true, will fetch the options even if they're already cached.
     * @return bool
     * @throws NoSuchEntityException
     */
    public function getOptionId($attributeCode, $label, $force = false)
    {
        /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
        $attribute = $this->getAttribute($attributeCode);
        $attribute->setStoreId(0);

        $attributeId = $attribute->getAttributeId();

        // Build option array if necessary
        if ($force === true || !isset($this->attributeValues[$attributeId])) {
            $this->attributeValues[$attributeId] = [];

            // We have to generate a new sourceModel instance each time through to prevent it from
            // referencing its _options cache. No other way to get it to pick up newly-added values.

            /** @var Table $sourceModel */
            $sourceModel = $this->tableFactory->create();
            $sourceModel->setAttribute($attribute);

            foreach ($sourceModel->getAllOptions() as $option) {
                $this->attributeValues[$attributeId][$option['label']] = $option['value'];
            }
        }

        // Return option ID if exists
        if (isset($this->attributeValues[$attributeId][$label])) {
            return $this->attributeValues[$attributeId][$label];
        }

        // Return false if does not exist
        return false;
    }

    /**
     * @param $attributeCode
     * @return ProductAttributeInterface
     * @throws NoSuchEntityException
     */
    public function getAttribute($attributeCode)
    {
        return $this->attributeRepository->get($attributeCode);
    }

    /**
     * @param $option
     * @return bool
     * @throws Exception
     */
    public function addAttributeOption($option)
    {
        $optionTable = 'eav_attribute_option';
        $attributeId = $option['attribute_id'];
        $optionValue = $option['value'];
        $storeId = $option['store_id'];

        if ($optionValue == null || $optionValue == '') {
            throw new Exception('Option value cannot be empty');
        }

        if ($storeId != 0) {
            throw new Exception('I can only add option for admin store view');
        }

        if (empty($attributeId)) {
            throw new Exception('Attribute id has to be defined when adding an option');
        }
        // add option
        $data = ['attribute_id' => $option['attribute_id']];
        $connection = $this->getConnection();

        $connection->insert($optionTable, $data);
        $optionId = $connection->lastInsertId($optionTable);

        if (empty($optionId)) {
            return false;
        }
        $optionValueTable = 'eav_attribute_option_value';
        $option['option_id'] = $optionId;

        unset($option['attribute_id']);

        $connection->insert($optionValueTable, $option);
        return $optionId;
    }

    /**
     *
     * @param $optionId
     * @param $attribute
     * @param $value
     * @param int $store
     * @return bool
     * @throws Exception
     */
    public function addSwatchOption($optionId, $attribute, $value, $store = 0)
    {
        if (!$this->swatchHelper->isSwatchAttribute($attribute)) {
            return false;
        }

        $connection = $this->getConnection();
        $attributeTable = $connection->getTableName('eav_attribute_option_swatch');

        $result = $connection->query("SELECT `option_id` FROM {$attributeTable} WHERE `option_id` = ? AND `store_id` = ?", [$optionId, $store])->fetchAll();

        if (!empty($result)) {
            return true;
        }

        $type = Swatch::SWATCH_TYPE_EMPTY;

        switch (true) {
            case $this->swatchHelper->isTextSwatch($attribute):
                $type = Swatch::SWATCH_TYPE_TEXTUAL;
                $realValue = $value;
                break;

            case $this->swatchHelper->isVisualSwatch($attribute):

                if (intval($attribute->getData('use_product_image_for_swatch')) !== 0) {
                    $type = Swatch::SWATCH_TYPE_VISUAL_IMAGE;

                    $mediaPath = $this->fileSystem->getDirectoryWrite(DirectoryList::MEDIA)->getAbsolutePath();
                    $realValue = DIRECTORY_SEPARATOR . $this->swatchMediaHelper->getAttributeSwatchPath(self::PLACEHOLDER_SWATCH_IMAGE_NAME);
                    $swatchImageAbsolutePath = DIRECTORY_SEPARATOR . $mediaPath . $realValue;

                    if (!file_exists($swatchImageAbsolutePath)) {
                        $fp = fopen($swatchImageAbsolutePath, 'wb');
                        fwrite($fp, base64_decode(self::PLACEHOLDER_SWATCH_IMAGE_BASE64));
                        fclose($fp);
                    }
                } else {
                    $type = Swatch::SWATCH_TYPE_VISUAL_COLOR;
                    $realValue = $value;
                }
                break;

            default:
                throw new Exception('Unknown swatch attribute type');
        }

        /* Whene creating a new swatch option if it is visual use  SWATCH_TYPE_VISUAL_COLOR otherwise product will not shown */
        //$type = $this->swatchHelper->isTextSwatch($attribute) ? Swatch::SWATCH_TYPE_TEXTUAL : Swatch::SWATCH_TYPE_VISUAL_COLOR;
        $connection->query("INSERT INTO {$attributeTable} (`option_id`, `store_id`, `type`, `value`) VALUES(?, ?, ?, ?)", [$optionId, $store, $type, $realValue]);
        $connection->lastInsertId($attributeTable);
        return true;
    }
}
