<?php
/**
 * Copyright © 2016 Filoblu S.r.l. All rights reserved.
 */

namespace FiloBlu\Esb\Setup;

use FiloBlu\Esb\Api\Data\ConsumerConfigurationInterface;
use FiloBlu\Esb\Api\Data\EventConfigurationInterface;
use FiloBlu\Esb\Api\Data\ProducerConfigurationInterface;
use FiloBlu\Esb\Api\Data\QueueItemInterface;
use FiloBlu\Esb\Api\Data\QueueMetadataInterface;
use FiloBlu\Esb\Api\QueueItemSqlRepositoryInterface;
use FiloBlu\Esb\Model\ConsumerConfigurationRepository;
use FiloBlu\Esb\Model\EventConfigurationRepository;
use FiloBlu\Esb\Model\ProducerConfigurationRepository;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Sales\Model\Order\StatusFactory;
use Zend_Db_Exception;

/**
 * Class UpgradeSchema
 * @package FiloBlu\Esb\Setup
 */
class UpgradeSchema implements UpgradeSchemaInterface
{
    /**
     * @var StatusFactory
     */
    protected $_statusFactory;

    /**
     * UpgradeSchema constructor.
     * @param StatusFactory $statusFactory
     */
    public function __construct(
        StatusFactory $statusFactory
    ) {
        $this->_statusFactory = $statusFactory;
    }

    /**
     * @param SchemaSetupInterface $setup
     * @param ModuleContextInterface $context
     * @throws Zend_Db_Exception
     */
    public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();

        if (version_compare($context->getVersion(), '1.1.12') < 0) {
            $this->addEsbEvents($setup);
            $this->addEsbQueue($setup);
            $this->addEsbConsumerConfiguration($setup);
            $this->addEsbProducerConfiguration($setup);
        }

        if (version_compare($context->getVersion(), '1.1.13') < 0) {
            $this->updateEsbConsumerConfiguration1_1_13($setup);
            $this->updateEsbProducerConfiguration1_1_13($setup);
        }

        if(version_compare($context->getVersion(), '2.0.2') < 0) {
            $this->updateEsbProducerConfiguration2_0_2($setup);
        }

        if(version_compare($context->getVersion(), '2.0.3') < 0) {
            $this->addMessageHash($setup);
        }

        if (version_compare($context->getVersion(), '2.0.4') < 0) {
            $this->updateMessageColumn($setup);
        }

        if (version_compare($context->getVersion(), '2.0.5') < 0) {
            $this->updateQueueTable($setup);
        }

        if(version_compare($context->getVersion(), '2.0.6') < 0) {
            $this->addObjectMetadataFields($setup);
        }

        $setup->endSetup();
    }

    /**
     * @param SchemaSetupInterface $setup
     * @throws Zend_Db_Exception
     */
    public function addEsbEvents(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(EventConfigurationRepository::TABLE);

        if ($setup->getConnection()->isTableExists($tableName)) {
            return;
        }

        $table = $setup->getConnection()
            ->newTable($tableName)
            ->addColumn(
                EventConfigurationInterface::ID,
                Table::TYPE_INTEGER,
                null,
                [
                    'identity' => true,
                    'unsigned' => true,
                    'nullable' => false,
                    'primary' => true
                ],
                'Configuration ID'
            )
            ->addColumn(
                EventConfigurationInterface::UUID,
                Table::TYPE_VARBINARY,
                40,
                [
                    'nullable' => false
                ]
            )
            ->addColumn(
                EventConfigurationInterface::EVENT_CODE,
                Table::TYPE_TEXT,
                255,
                [
                    'nullable' => false
                ]
            )
            ->addColumn(
                EventConfigurationInterface::LABEL,
                Table::TYPE_TEXT,
                255,
                [
                    'nullable' => false
                ]
            )
            ->addColumn(
                EventConfigurationInterface::BASE_EVENT,
                Table::TYPE_TEXT,
                2048,
                [
                    'nullable' => false
                ]
            )
            ->setComment('ESB Schedule Queue Events definitions')
            ->setOption('type', 'InnoDB')
            ->setOption('charset', 'utf8');
        $setup->getConnection()->createTable($table);
    }

    /**
     * @param SchemaSetupInterface $setup
     * @throws Zend_Db_Exception
     */
    public function addEsbQueue(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(QueueItemSqlRepositoryInterface::TABLE);

        if ($setup->getConnection()->isTableExists($tableName)) {
            return;
        }

        $table = $setup->getConnection()
            ->newTable($tableName)
            ->addColumn(
                'queue_id',
                Table::TYPE_INTEGER,
                null,
                [
                    'identity' => true,
                    'unsigned' => true,
                    'nullable' => false,
                    'primary' => true
                ],
                'Schedule ID'
            )
            ->addColumn(
                'priority',
                Table::TYPE_INTEGER,
                null,
                [
                    'unsigned' => true,
                    'default' => 0,
                    'nullable' => false
                ],
                'Priority'
            )
            ->addColumn(
                'retry_count',
                Table::TYPE_INTEGER,
                null,
                [
                    'unsigned' => true,
                    'default' => 0,
                    'nullable' => false
                ],
                'Retry Count'
            )
            ->addColumn(
                'retry_item_id',
                Table::TYPE_INTEGER,
                null,
                [
                    'unsigned' => true,
                    'default' => 0,
                    'nullable' => false
                ],
                'Retry item id'
            )
            ->addColumn(
                'process_label',
                Table::TYPE_TEXT,
                1024,
                [
                    'nullable' => false,
                    'default' => ''
                ],
                'Process label for producer or consumer'
            )
            ->addColumn(
                'process_class',
                Table::TYPE_TEXT,
                1024,
                [
                    'nullable' => false,
                    'default' => ''
                ],
                'Process class for producer or consumer'
            )
            ->addColumn(
                QueueItemInterface::FROM,
                Table::TYPE_VARBINARY,
                40,
                ['nullable' => true],
                'Process from uuid4'
            )
            ->addColumn(
                QueueItemInterface::TO,
                Table::TYPE_VARBINARY,
                40,
                ['nullable' => true],
                'Process to uuid4'
            )
            ->addColumn(
                'process_uuid',
                Table::TYPE_VARBINARY,
                40,
                ['nullable' => true],
                'Process uuid4'
            )
            ->addColumn(
                'event',
                Table::TYPE_TEXT,
                255,
                ['nullable' => false, 'default' => ''],
                'Event'
            )
            ->addColumn(
                'message',
                Table::TYPE_TEXT,
                null,
                ['nullable' => false, 'default' => ''],
                'Event Message'
            )
            ->addColumn(
                'status',
                Table::TYPE_TEXT,
                255,
                ['nullable' => false, 'default' => ''],
                'Status'
            )
            ->addColumn(
                'status_input_response',
                Table::TYPE_TEXT,
                null,
                ['nullable' => true, 'default' => ''],
                'Response'
            )
            ->addColumn(
                'status_output_response',
                Table::TYPE_TEXT,
                null,
                ['nullable' => true, 'default' => ''],
                'Response'
            )
            ->addColumn(
                'published_at',
                Table::TYPE_TIMESTAMP,
                null,
                ['nullable' => false, 'default' => Table::TIMESTAMP_INIT],
                'Published At'
            )
            ->addColumn(
                'executable_after',
                Table::TYPE_TIMESTAMP,
                null,
                ['nullable' => true],
                'Executable After'
            )
            ->addColumn(
                'executed_at',
                Table::TYPE_TIMESTAMP,
                null,
                ['nullable' => true],
                'Executed At'
            )
            ->addColumn(
                'finished_at',
                Table::TYPE_TIMESTAMP,
                null,
                [
                    'nullable' => true
                ],
                'Finished At'
            )
            ->setComment('ESB Schedule Queue')
            ->setOption('type', 'InnoDB')
            ->setOption('charset', 'utf8');
        $setup->getConnection()->createTable($table);
    }

    /**
     * @param SchemaSetupInterface $setup
     * @throws Zend_Db_Exception
     */
    public function addEsbConsumerConfiguration(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(ConsumerConfigurationRepository::TABLE);

        if ($setup->getConnection()->isTableExists($tableName)) {
            return;
        }

        $table = $setup->getConnection()
            ->newTable($tableName)
            ->addColumn(
                ConsumerConfigurationInterface::ID,
                Table::TYPE_INTEGER,
                null,
                [
                    'identity' => true,
                    'unsigned' => true,
                    'nullable' => false,
                    'primary' => true
                ],
                'Configuration ID'
            )
            ->addColumn(
                ConsumerConfigurationInterface::UUID,
                Table::TYPE_VARBINARY,
                40,
                [
                    'nullable' => false
                ]
            )
            ->addColumn(
                ConsumerConfigurationInterface::CONSUMER,
                Table::TYPE_TEXT,
                2048,
                ['nullable' => false, 'default' => ''],
                'Event'
            )->addColumn(
                ConsumerConfigurationInterface::BINDING_EVENT,
                Table::TYPE_TEXT,
                255,
                ['nullable' => false, 'default' => ''],
                'Event'
            )
            ->addColumn(
                ConsumerConfigurationInterface::OUTPUT_EVENT,
                Table::TYPE_TEXT,
                255,
                ['nullable' => false, 'default' => ''],
                'Event'
            )
            ->addColumn(
                ConsumerConfigurationInterface::LABEL,
                Table::TYPE_TEXT,
                255,
                ['nullable' => false, 'default' => ''],
                'Label'
            )
            ->addColumn(
                ConsumerConfigurationInterface::ENABLE,
                Table::TYPE_BOOLEAN,
                null,
                ['default' => '0'],
                'Enabled or disabled flag'
            )
            ->addColumn(
                ConsumerConfigurationInterface::RETRY_STRATEGY,
                Table::TYPE_TEXT,
                2048,
                ['nullable' => true],
                'Label'
            )
            ->addColumn(
                ConsumerConfigurationInterface::PARAMETERS,
                Table::TYPE_TEXT,
                null,
                ['nullable' => false, 'default' => ''],
                'Parameters'
            )->setComment('ESB Consumer configuration')
            ->setOption('type', 'InnoDB')
            ->setOption('charset', 'utf8');
        $setup->getConnection()->createTable($table);
    }

    /**
     * @param SchemaSetupInterface $setup
     * @throws Zend_Db_Exception
     */
    public function addEsbProducerConfiguration(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(ProducerConfigurationRepository::TABLE);

        if ($setup->getConnection()->isTableExists($tableName)) {
            return;
        }

        $table = $setup->getConnection()
            ->newTable($tableName)
            ->addColumn(
                ProducerConfigurationInterface::ID,
                Table::TYPE_INTEGER,
                null,
                [
                    'identity' => true,
                    'unsigned' => true,
                    'nullable' => false,
                    'primary' => true
                ],
                'Configuration ID'
            )
            ->addColumn(
                ProducerConfigurationInterface::UUID,
                Table::TYPE_VARBINARY,
                40,
                [
                    'nullable' => false
                ]
            )
            ->addColumn(
                ProducerConfigurationInterface::PRODUCER,
                Table::TYPE_TEXT,
                2048,
                ['nullable' => false, 'default' => ''],
                'Event'
            )
            ->addColumn(
                ProducerConfigurationInterface::OUTPUT_EVENT,
                Table::TYPE_TEXT,
                255,
                ['nullable' => false, 'default' => ''],
                'Output event'
            )
            ->addColumn(
                ProducerConfigurationInterface::LABEL,
                Table::TYPE_TEXT,
                255,
                ['nullable' => false, 'default' => ''],
                'Label'
            )
            ->addColumn(
                ProducerConfigurationInterface::ENABLE,
                Table::TYPE_BOOLEAN,
                null,
                ['default' => '0'],
                'Enabled or disabled flag'
            )
            ->addColumn(
                ProducerConfigurationInterface::RETRY_STRATEGY,
                Table::TYPE_TEXT,
                2048,
                ['nullable' => true],
                'Label'
            )
            ->addColumn(
                'parameters',
                Table::TYPE_TEXT,
                null,
                ['nullable' => false, 'default' => ''],
                'Parameters'
            )->setComment('ESB Producer configuration')
            ->setOption('type', 'InnoDB')
            ->setOption('charset', 'utf8');
        $setup->getConnection()->createTable($table);
    }

    /**
     * @param SchemaSetupInterface $setup
     * @throws Zend_Db_Exception
     */
    protected function updateEsbConsumerConfiguration1_1_13(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(ConsumerConfigurationRepository::TABLE);

        $setup->getConnection()
            ->addColumn(
                $tableName,
                ConsumerConfigurationInterface::SAVE_REQUEST,
                [
                    'type' => Table::TYPE_INTEGER,
                    'default' => 0,
                    'comment' => 'Save request on status'
                ]
            );
        $setup->getConnection()->addColumn(
            $tableName,
            ConsumerConfigurationInterface::SAVE_RESPONSE,
            [
                'type' => Table::TYPE_INTEGER,
                'default' => 0,
                'comment' => 'Save response on status'
            ]

        );
        $setup->getConnection()
            ->addColumn(
                $tableName,
                ConsumerConfigurationInterface::PRIORITY,
                [
                    'type' => Table::TYPE_INTEGER,
                    'default' => 1,
                    'comment' => 'Priority'
                ]

            );
        $setup->getConnection()
            ->addColumn(
                $tableName,
                ConsumerConfigurationInterface::RETRY_STRATEGY_CONFIGURATION,
                [
                    'type' => Table::TYPE_TEXT,
                    'length' => '2048',
                    'nullable' => true,
                    'default' => '',
                    'comment' => 'Retry Strategy configuration'
                ]
            );
        $setup->getConnection()
            ->addColumn(
                $tableName,
                ConsumerConfigurationInterface::OBJECT_LOADER,
                [
                    'type' => Table::TYPE_TEXT,
                    'length' => '2048',
                    'nullable' => true,
                    'default' => '',
                    'comment' => 'Object loader'
                ]

            );
        $setup->getConnection()
            ->addColumn(
                $tableName,
                ConsumerConfigurationInterface::OBJECT_SAVER,
                [
                    'type' => Table::TYPE_TEXT,
                    'length' => '2048',
                    'nullable' => true,
                    'default' => '',
                    'comment' => 'Object saver'
                ]
            );
    }

    /**
     * @param SchemaSetupInterface $setup
     * @throws Zend_Db_Exception
     */
    protected function updateEsbProducerConfiguration1_1_13(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(ProducerConfigurationRepository::TABLE);

        $setup->getConnection()
            ->addColumn(
                $tableName,
                ProducerConfigurationInterface::PRIORITY,
                [
                    'type' => Table::TYPE_INTEGER,
                    'default' => 1,
                    'comment' => 'Priority'
                ]
            );
    }

    /**
     * @param \Magento\Framework\Setup\SchemaSetupInterface $setup
     * @return void
     */
    public function updateEsbProducerConfiguration2_0_2(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(ConsumerConfigurationRepository::TABLE);

        $setup->getConnection()
            ->addColumn(
                $tableName,
                ConsumerConfigurationInterface::TRIGGER_DELAY,
                [
                    'type' => Table::TYPE_INTEGER,
                    'default' => 0,
                    'comment' => 'Initial Delay when triggered'
                ]
            );
    }

    /**
     * @param \Magento\Framework\Setup\SchemaSetupInterface $setup
     * @return void
     */
    public function addMessageHash(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(QueueItemSqlRepositoryInterface::TABLE);
        $connection = $setup->getConnection();

        $connection->addColumn(
                $tableName,
                QueueItemInterface::MESSAGE_HASH,
                [
                    'type' => Table::TYPE_TEXT,
                    'length' => 255,
                    'comment' => 'Message Hash'
                ]
            );

        $connection->modifyColumn(
            $tableName,
            QueueItemInterface::STATUS,
            [
                'type' => Table::TYPE_TEXT,
                'length' => 255,
                'comment' => 'Status'
            ],
            true
        );

        $connection->addIndex(
            $tableName,
            $setup->getIdxName($tableName, [QueueItemInterface::STATUS]),
            [QueueItemInterface::STATUS]
        );

        $connection->addIndex(
            $tableName,
            $setup->getIdxName($tableName, [QueueItemInterface::MESSAGE_HASH]),
            [QueueItemInterface::MESSAGE_HASH]
        );

        $connection->addIndex(
            $tableName,
            $setup->getIdxName($tableName, [QueueItemInterface::EVENT]),
            [QueueItemInterface::EVENT]
        );

        $connection->addIndex(
            $tableName,
            $setup->getIdxName($tableName, [QueueMetadataInterface::EXECUTABLE_AFTER]),
            [QueueMetadataInterface::EXECUTABLE_AFTER]
        );

        $connection->addIndex(
            $tableName,
            $setup->getIdxName($tableName, [QueueMetadataInterface::PUBLISHED_AT]),
            [QueueMetadataInterface::PUBLISHED_AT]
        );

        $connection->addIndex(
            $tableName,
            $setup->getIdxName($tableName, [QueueMetadataInterface::FINISHED_AT]),
            [QueueMetadataInterface::FINISHED_AT]
        );

        $connection->addIndex(
            $tableName,
            $setup->getIdxName($tableName, [QueueMetadataInterface::EXECUTED_AT]),
            [QueueMetadataInterface::EXECUTED_AT]
        );

        $connection->addIndex(
            $tableName,
            $setup->getIdxName($tableName, [QueueItemInterface::STATUS]),
            [QueueItemInterface::STATUS]
        );
    }

    /**
     * @param SchemaSetupInterface $setup
     * @return void
     */
    public function updateMessageColumn(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(QueueItemSqlRepositoryInterface::TABLE);
        $connection = $setup->getConnection();

        $connection->modifyColumn(
            $tableName,
            QueueItemInterface::MESSAGE,
            [
                'type' => Table::TYPE_BLOB,
                'length' => 17000000
            ]
        );
    }

    public function updateQueueTable(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(QueueItemSqlRepositoryInterface::TABLE);
        $connection = $setup->getConnection();
        $existingIndexes = $connection->getIndexList($tableName);

        if ($connection->tableColumnExists($tableName, 'process_label')
            && !$connection->tableColumnExists($tableName, 'object_id')) {
                $connection->changeColumn(
                    $tableName,
                    'process_label',
                    'object_id',
                    [
                        'type'    => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                        'length'  => 255,
                        'comment' => 'Object Id'
                    ]
                );
        }

        if ($connection->tableColumnExists($tableName, QueueItemInterface::OBJECT_ID)
            && !array_key_exists($setup->getIdxName($tableName, [QueueItemInterface::OBJECT_ID]), $existingIndexes)) {
            $connection->addIndex(
                $tableName,
                $setup->getIdxName($tableName, [QueueItemInterface::OBJECT_ID]),
                [QueueItemInterface::OBJECT_ID]
            );
        }

        if ($connection->tableColumnExists($tableName, 'process_class')
            && !$connection->tableColumnExists($tableName, 'object_type')) {

                $connection->changeColumn(
                    $tableName,
                    'process_class',
                    'object_type',
                    [
                        'type'    => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                        'length'  => 255,
                        'comment' => 'Object Type'
                    ]
                );
        }

        if ($connection->tableColumnExists($tableName, QueueItemInterface::OBJECT_TYPE)
            && !array_key_exists($setup->getIdxName($tableName, [QueueItemInterface::OBJECT_TYPE]), $existingIndexes)) {
                $connection->addIndex(
                    $tableName,
                    $setup->getIdxName($tableName, [QueueItemInterface::OBJECT_TYPE]),
                    [QueueItemInterface::OBJECT_TYPE]
                );
        }
    }

    /**
     * Add object metadata fields to queue table
     *
     * @param SchemaSetupInterface $setup
     * @return void
     * @throws Zend_Db_Exception
     */
    public function addObjectMetadataFields(SchemaSetupInterface $setup)
    {
        $tableName = $setup->getTable(QueueItemSqlRepositoryInterface::TABLE);
        $connection = $setup->getConnection();

        $objectMetadataColumns = [
            QueueItemInterface::OBJECT_METADATA_1,
            QueueItemInterface::OBJECT_METADATA_2,
            QueueItemInterface::OBJECT_METADATA_3,
            QueueItemInterface::OBJECT_METADATA_4
        ];

        if ($connection->isTableExists($tableName)) {
            foreach ($objectMetadataColumns as $objectMetadataColumn) {

                if (!$connection->tableColumnExists($tableName, $objectMetadataColumn)) {
                    $connection->addColumn(
                        $tableName,
                        $objectMetadataColumn,
                        [
                            'type'     => Table::TYPE_TEXT,
                            'nullable' => true,
                            'comment'  => 'Object Metadata Field',
                            'length'   => 255
                        ]
                    );
                    $connection->addIndex(
                        $tableName,
                            $setup->getIdxName($tableName, [$objectMetadataColumn]),
                            [$objectMetadataColumn]
                    );
                }
            }
        }
    }
}
