<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

use Shopware\Bundle\PluginInstallerBundle\Service\InstallerService;
use Shopware\Components\Model\ModelManager;
use Shopware\Plugins\ViisonCommon\Classes\ExceptionHandling\BackendExceptionHandling;
use Shopware\Plugins\ViisonCommon\Classes\Util\Util as ViisonCommonUtil;
use Shopware\Plugins\ViisonCommon\Components\ExceptionTranslation\ExceptionTranslator;
use Shopware\Plugins\ViisonCommon\Components\Migration\ManifestedMigration;
use Shopware\Plugins\ViisonCommon\Components\Migration\MigrationExecutionResult;
use Shopware\Plugins\ViisonCommon\Components\Migration\MigrationInProgressException;
use Shopware\Plugins\ViisonCommon\Components\Migration\MigrationMessage;
use Shopware\Plugins\ViisonCommon\Components\Migration\MigrationService;
use Shopware\Plugins\ViisonCommon\Components\Migration\MigrationSet;
use Shopware\Plugins\ViisonCommon\Components\Migration\MigrationTranscript;
use Shopware\Plugins\ViisonCommon\Controllers\ViisonCommonBaseController;

class Shopware_Controllers_Backend_ViisonCommonMigration extends ViisonCommonBaseController
{
    use BackendExceptionHandling;

    public function executeMigrationsAction()
    {
        $migrationExecutionResult = null;
        try {
            /** @var MigrationService $migrationService */
            $migrationService = $this->get('viison_common.migration_service');

            $migrationExecutionResult = $migrationService->executeMigrations();
        } catch (MigrationInProgressException $e) {
            $this->get('viison_common.logger')->error(
                'MigrationService locking failed with the following error:  ' . $e->getMessage(),
                ViisonCommonUtil::exceptionToArray($e)
            );

            // Ignore every running migration and just return an empty migration execution result
            $migrationTranscript = new MigrationTranscript($this->get('viison_common.logger'));
            $migrationExecutionResult = new MigrationExecutionResult($migrationTranscript, []);
        } catch (\Exception $e) {
            $this->handleException($e);

            return;
        }

        $this->View()->assign([
            'success' => true,
            'data' => $this->migrationExecutionResultToArray($migrationExecutionResult),
        ]);
    }

    public function disablePluginsWithExecutableMigrationsAction()
    {
        $messages = [];
        $invalidatedCaches = [];
        try {
            /** @var MigrationService $migrationService */
            $migrationService = $this->get('viison_common.migration_service');

            $setsWithExecutableMigrations = $migrationService->getSetsWithExecutableMigrations();
            $technicalPluginNames = array_map(function (MigrationSet $migrationSet) {
                return $migrationSet->getName();
            }, $setsWithExecutableMigrations);

            /** @var ModelManager $entityManager */
            $entityManager = $this->get('models');
            $plugins = $entityManager->getRepository('Shopware\\Models\\Plugin\\Plugin')->findBy([
                'name' => $technicalPluginNames,
            ]);

            /** @var InstallerService $pluginManager */
            $pluginManager = $this->get('shopware_plugininstaller.plugin_manager');
            foreach ($plugins as $plugin) {
                $deactivationContext = $pluginManager->deactivatePlugin($plugin);
                $messages[] = $deactivationContext->getScheduled()['message'];
                $invalidatedCaches = array_merge($invalidatedCaches, $deactivationContext->getScheduled()['cache']);
            }
            $invalidatedCaches = array_unique($invalidatedCaches);
        } catch (\Exception $e) {
            $this->handleException($e);

            return;
        }

        $this->View()->assign([
            'data' => [
                'messages' => $messages,
                'invalidatedCaches' => $invalidatedCaches,
            ],
            'success' => true,
        ]);
    }

    /**
     * @param MigrationExecutionResult $result
     * @return array
     */
    private function migrationExecutionResultToArray(MigrationExecutionResult $result)
    {
        $migrationSets = $this->migrationSetsToArray($result->getMigrationSets());

        foreach ($migrationSets as &$migrationSet) {
            $migrationSet['migrationExecutionResultId'] = 1;
        }
        unset($migrationSet);

        return [
            'id' => 1,
            'migrationTranscript' => $this->migrationTranscriptToArray($result->getMigrationTranscript()),
            'migrationSets' => $migrationSets,
        ];
    }

    /**
     * @param MigrationTranscript $transcript
     * @return array
     */
    private function migrationTranscriptToArray(MigrationTranscript $transcript)
    {
        $snippetManager = $this->get('snippets');

        $messages = array_map(function (MigrationMessage $message) use ($snippetManager) {
            return $message->localize($snippetManager);
        }, $transcript->getMessages());

        return [
            'messages' => $messages,
            'invalidatedCaches' => $transcript->getInvalidatedCaches(),
        ];
    }

    /**
     * @param MigrationSet[] $migrationSets
     * @return array
     */
    private function migrationSetsToArray(array $migrationSets)
    {
        $migrationSetsArray = [];
        foreach ($migrationSets as $migrationSetId => $migrationSet) {
            $setName = $migrationSet->getName();
            $migrations = [];
            foreach ($migrationSet->getMigrations() as $migration) {
                $migration = $this->migrationToArray($migration);
                $migration['migrationSetId'] = $migrationSetId;
                $migrations[] = $migration;
            }
            $migrationSetsArray[] = [
                'id' => $migrationSetId,
                'name' => $setName,
                'migrations' => $migrations,
                'hasExecutableMigrations' => $migrationSet->hasExecutableMigrations(),
            ];
        }

        return $migrationSetsArray;
    }

    /**
     * @param ManifestedMigration $migration
     * @return array
     */
    private function migrationToArray(ManifestedMigration $migration)
    {
        $exceptionMessage = null;
        $exceptionDetails = null;
        $e = $migration->getException();
        if ($e) {
            /** @var ExceptionTranslator $exceptionTranslator */
            $exceptionTranslator = $this->get('viison_common.exception_translator');
            $exceptionMessage = $exceptionTranslator->translate($e);
            $exceptionDetails = json_encode(ViisonCommonUtil::exceptionToArray($e));
        }

        return [
            'name' => $migration->getName(),
            'status' => $migration->getStatus(),
            'canExecute' => $migration->canExecute(),
            'exceptionMessage' => $exceptionMessage,
            'exceptionDetails' => $exceptionDetails,
        ];
    }
}
