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

/**
 * Represents a migration that is known to the system that should be executed only once.
 *
 * Whether a migration has been executed yet, is defined by it status. Calls of the method `apply` are idempotent by
 * the migrations status.
 */
class ManifestedMigration
{
    const STATUS_COMPLETED = 'completed';
    const STATUS_FAILED = 'failed';
    const STATUS_PENDING = 'pending';

    /**
     * @var string Name that should be unique for each migration.
     */
    private $name;

    /**
     * @var Migration
     */
    private $migration;

    /**
     * @var string
     */
    private $status = self::STATUS_PENDING;

    /**
     * @var null|\Exception Holds the exception that has been caught during the execution of the `apply` method. Is only
     * filled if self::$status === self::STATUS_FAILED
     */
    private $exception = null;

    /**
     * @param string $name Descriptive name of the migration, should be unique for each migration in a plugin/dependency
     * @param Migration $migration
     */
    public function __construct($name, Migration $migration)
    {
        $this->name = $name;
        $this->migration = $migration;
    }

    /**
     * @return bool
     */
    public function canExecute()
    {
        return $this->migration->canExecute();
    }

    /**
     * Executes the migration.
     *
     * This method call executes the migration only if its status is NOT self::STATUS_COMPLETED and the method
     * self::canExecute() returns true.
     *
     * @param MigrationTranscript $transcript
     */
    public function execute(MigrationTranscript $transcript)
    {
        if (!$this->canExecute() || $this->status === self::STATUS_COMPLETED) {
            return;
        }

        try {
            $this->migration->execute($transcript);

            $this->status = self::STATUS_COMPLETED;
            $this->exception = null;
        } catch (\Exception $e) {
            $this->status = self::STATUS_FAILED;
            $this->exception = $e;
        }
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return string
     */
    public function getStatus()
    {
        return $this->status;
    }

    /**
     * @param string $status
     */
    public function setStatus($status)
    {
        if (!in_array($status, [self::STATUS_FAILED, self::STATUS_PENDING, self::STATUS_COMPLETED], true)) {
            throw new \InvalidArgumentException(sprintf(
                'First parameter $status of method %s must be one of the constants %s::STATUS_*',
                __METHOD__,
                __CLASS__
            ));
        }
        $this->status = $status;
    }

    /**
     * Returns the exception that may have been thrown during the last self::execute() method execution.
     *
     * @return \Exception|null
     */
    public function getException()
    {
        return $this->exception;
    }
}
