<?php
/**
 * @link https://craftcms.com/
 * @copyright Copyright (c) Pixel & Tonic, Inc.
 * @license https://craftcms.github.io/license/
 */

namespace craft\console\controllers;

use Craft;
use craft\console\Controller;
use craft\db\MigrationManager;
use craft\enums\LicenseKeyStatus;
use craft\errors\OperationAbortedException;
use craft\helpers\App;
use craft\helpers\Console;
use Throwable;
use yii\console\ExitCode;

/**
 * Runs pending migrations and applies pending project config changes.
 *
 * @author Pixel & Tonic, Inc. <support@pixelandtonic.com>
 * @since 3.7.13
 */
class UpController extends Controller
{
    /**
     * @var bool Skip backing up the database.
     * @since 4.5.8
     */
    public bool $noBackup = false;

    /**
     * @var bool Whether to perform the action even if a mutex lock could not be acquired.
     */
    public bool $force = false;

    /**
     * @inheritdoc
     */
    public bool $isolated = true;

    /**
     * @inheritdoc
     */
    public function options($actionID): array
    {
        return array_merge(parent::options($actionID), [
            'noBackup',
            'force',
        ]);
    }

    /**
     * Runs pending migrations and applies pending project config changes.
     *
     * @return int
     */
    public function actionIndex(): int
    {
        $this->showLicensingIssues();

        try {
            $pendingChanges = Craft::$app->getProjectConfig()->areChangesPending(force: true);

            // Craft + plugin migrations
            $res = $this->run('migrate/all', [
                'noContent' => true,
                'noBackup' => $this->noBackup,
            ]);
            if ($res !== ExitCode::OK) {
                $this->stderr("\nAborting remaining tasks.\n", Console::FG_YELLOW);
                return $res;
            }
            $this->stdout("\n");

            // Project Config
            if ($pendingChanges) {
                $res = $this->run('project-config/apply');
                if ($res !== ExitCode::OK) {
                    return $res;
                }
                $this->stdout("\n");
            }

            // Content migration
            $res = $this->run('migrate/up', [
                'track' => MigrationManager::TRACK_CONTENT,
            ]);
            if ($res !== ExitCode::OK) {
                return $res;
            }
            $this->stdout("\n");

            // Delete compiled templates
            $this->run('clear-caches/compiled-templates');
            $this->stdout("\n");
        } catch (Throwable $e) {
            if (!$e instanceof OperationAbortedException) {
                throw $e;
            }
            return ExitCode::UNSPECIFIED_ERROR;
        }

        return ExitCode::OK;
    }

    private function showLicensingIssues(): bool
    {
        $this->stdout('Fetching license info ... ');
        Craft::$app->getUpdates()->getUpdates(true);
        $this->stdout("done\n", Console::FG_GREEN);

        $issues = App::licensingIssues([LicenseKeyStatus::Astray]);

        if (empty($issues)) {
            return true;
        }

        $this->stdout("\nThe following licensing issues were detected:\n", Console::FG_RED);
        foreach ($issues as [$name, $message, $resolveItem]) {
            $this->stdout(" - $message\n", Console::FG_RED);
        }
        $this->stdout("\n");

        return $this->confirm('Continue anyway?');
    }
}
