<?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.

namespace Shopware\Plugins\ViisonCommon\Components\Migration;

use Enlight\Event\SubscriberInterface;
use Shopware\Components\DependencyInjection\Container;
use Shopware\Plugins\ViisonCommon\Classes\ExceptionHandling\SubscriberExceptionHandling;

/**
 * Subscriber that listens to the migration collection event of the MigrationService.
 *
 * Implement this subscriber if you have migrations outside of a plugin (use {@link AbstractPluginMigrationSubscriber}
 * for that instead).
 *
 * Example implementation:
 *
 * <code>
 * class MigrationSubscriber extends AbstractMigrationSubscriber
 * {
 *     // ...
 *
 *     /**
 *      * @inheritdoc
 *      * /
 *     public function createMigrations()
 *     {
 *         $this->addMigration('001_SampleMigration', new SampleMigration($this->get('db')));
 *         $this->addMigration('002_AnotherSampleMigration', new AnotherSampleMigration($this->get('db')));
 *     }
 * }
 * </code>
 */
abstract class AbstractMigrationSubscriber implements SubscriberInterface
{
    use SubscriberExceptionHandling;

    /**
     * @var Container
     */
    protected $container;

    /**
     * @var MigrationSet
     */
    protected $migrationSet;

    /**
     * @param string $name
     * @param Container $container
     */
    public function __construct($name, Container $container)
    {
        $this->container = $container;
        $this->migrationSet = new MigrationSet($name);
    }

    /**
     * Creates the instances of Migration and adds them to the collected MigrationSet via $this->addMigration();
     *
     * @see AbstractMigrationSubscriber::addMigration() for a recommended format for the migration names.
     *
     * @return void
     */
    abstract protected function createMigrations();

    /**
     * @inheritdoc
     */
    public static function getSubscribedEvents()
    {
        return [
            MigrationService::EVENT_NAME_COLLECT_MIGRATIONS => 'onCollectMigrations',
        ];
    }

    /**
     * Gets a service from the DI container.
     *
     * @param string $name
     * @return mixed
     */
    public function get($name)
    {
        return $this->container->get($name);
    }

    /**
     * @param \Enlight_Event_EventArgs $args
     * @return MigrationSet
     */
    public function onCollectMigrations(\Enlight_Event_EventArgs $args)
    {
        if (count($this->migrationSet->getMigrations()) !== 0) {
            return $this->migrationSet;
        }

        try {
            $this->createMigrations();
        } catch (\Exception $e) {
            $this->handleException($e, $args);
        }

        return $this->migrationSet;
    }

    /**
     * Add a migration to be executed for the plugin as soon as its canExecute() method returns true.
     *
     * Please use this format for the migration name:
     *
     * nnn_MigrationClassNameWithoutMigration_AnyIdentifyingParameters
     *
     * Where "nnn" is a number counting up within this subscriber from 001, and the last part is optional (when the same
     * migration class may be used multiple times with different parameters. E.g:
     *
     * 001_CreateSomeNewEntity
     * 002_DocumentTypeNameToKey_MyOneDocumentType
     * 002_DocumentTypeNameToKey_MyOtherDocumentType
     * 003_RemoveObsoleteConfig
     *
     * In this example, "002" was repeated because the migration was added in a for loop, so the migration number
     * actually refers to the position of the addMigration() call within the createMigrations() method.
     *
     * @param string $name an identifier for the migration.
     * @param Migration $migration
     */
    protected function addMigration($name, Migration $migration)
    {
        $this->migrationSet->add($name, $migration);
    }
}
