<?php

declare(strict_types=1);

namespace App\Framework\Console;

/**
 * Executes command workflows
 */
final readonly class WorkflowExecutor
{
    public function __construct(
        private CommandRegistry $commandRegistry,
        private CommandGroupRegistry $groupRegistry,
        private ConsoleOutputInterface $output
    ) {}

    /**
     * Execute workflow by name
     */
    public function executeWorkflow(string $workflowName, array $context = []): WorkflowResult
    {
        $workflow = $this->groupRegistry->getWorkflow($workflowName);
        if ($workflow === null) {
            return WorkflowResult::error("Workflow '{$workflowName}' not found");
        }

        return $this->executeWorkflowSteps($workflow, $context);
    }

    /**
     * Execute workflow steps
     */
    public function executeWorkflowSteps(array $workflow, array $context = []): WorkflowResult
    {
        $this->output->writeLine("🔄 Starting workflow: {$workflow['name']}", ConsoleColor::BRIGHT_CYAN);

        if (!empty($workflow['description'])) {
            $this->output->writeLine($workflow['description'], ConsoleColor::GRAY);
        }

        $this->output->newLine();

        // Check prerequisites
        if (!$this->checkPrerequisites($workflow['prerequisites'], $context)) {
            return WorkflowResult::error('Prerequisites not met');
        }

        $results = [];
        $stepNumber = 1;
        $totalSteps = count($workflow['steps']);

        foreach ($workflow['steps'] as $step) {
            $workflowStep = $step instanceof WorkflowStep ? $step : WorkflowStep::fromArray($step);

            $this->output->writeLine(
                "Step {$stepNumber}/{$totalSteps}: {$workflowStep->getDescription() ?: $workflowStep->getCommand()}",
                ConsoleColor::BRIGHT_WHITE
            );

            // Check if step should be executed
            if (!$workflowStep->shouldExecute($context)) {
                $this->output->writeLine('  ⏭️  Skipped (condition not met)', ConsoleColor::YELLOW);
                $stepNumber++;
                continue;
            }

            $stepResult = $this->executeStep($workflowStep, $context);
            $results[] = $stepResult;

            if ($stepResult->isSuccess()) {
                $this->output->writeLine('  ✅ Success', ConsoleColor::GREEN);
            } else {
                $message = "  ❌ Failed: {$stepResult->getError()}";

                if ($workflowStep->isOptional()) {
                    $this->output->writeLine("{$message} (optional step)", ConsoleColor::YELLOW);
                } else {
                    $this->output->writeLine($message, ConsoleColor::RED);

                    if ($workflow['stopOnError']) {
                        $this->output->newLine();
                        $this->output->writeLine('🛑 Workflow stopped due to error', ConsoleColor::RED);

                        // Execute rollback if configured
                        if (!empty($workflow['rollbackSteps'])) {
                            $this->executeRollback($workflow['rollbackSteps'], $context);
                        }

                        return WorkflowResult::error("Step failed: {$stepResult->getError()}");
                    }
                }
            }

            $stepNumber++;
        }

        $this->output->newLine();
        $this->output->writeLine('🎉 Workflow completed successfully!', ConsoleColor::BRIGHT_GREEN);

        return WorkflowResult::success($results);
    }

    /**
     * Execute a single workflow step
     */
    private function executeStep(WorkflowStep $step, array $context): StepResult
    {
        $retryCount = 0;
        $maxRetries = $step->getRetryCount();

        do {
            try {
                // Merge step environment with context
                $stepContext = array_merge($context, $step->getEnvironment());

                // Build command arguments
                $args = ['console', $step->getCommand()];
                foreach ($step->getParameters() as $param) {
                    // Simple parameter substitution
                    $args[] = $this->substituteVariables($param, $stepContext);
                }

                $exitCode = $this->commandRegistry->execute($args);

                if ($exitCode->value === 0) {
                    return StepResult::success($step->getCommand());
                } else {
                    $error = "Command exited with code {$exitCode->value}";

                    if ($retryCount < $maxRetries) {
                        $retryCount++;
                        $this->output->writeLine(
                            "    ⏳ Retrying ({$retryCount}/{$maxRetries})...",
                            ConsoleColor::YELLOW
                        );
                        continue;
                    }

                    return StepResult::error($error);
                }
            } catch (\Exception $e) {
                $error = "Exception: {$e->getMessage()}";

                if ($retryCount < $maxRetries) {
                    $retryCount++;
                    $this->output->writeLine(
                        "    ⏳ Retrying ({$retryCount}/{$maxRetries})...",
                        ConsoleColor::YELLOW
                    );
                    continue;
                }

                return StepResult::error($error);
            }
        } while ($retryCount <= $maxRetries);

        return StepResult::error('Max retries exceeded');
    }

    /**
     * Execute rollback steps
     */
    private function executeRollback(array $rollbackSteps, array $context): void
    {
        $this->output->writeLine('🔄 Executing rollback...', ConsoleColor::YELLOW);

        foreach ($rollbackSteps as $step) {
            $workflowStep = $step instanceof WorkflowStep ? $step : WorkflowStep::fromArray($step);
            $this->output->writeLine("  Rolling back: {$workflowStep->getCommand()}", ConsoleColor::GRAY);

            $result = $this->executeStep($workflowStep, $context);
            if ($result->isSuccess()) {
                $this->output->writeLine('    ✅ Rollback success', ConsoleColor::GREEN);
            } else {
                $this->output->writeLine('    ❌ Rollback failed', ConsoleColor::RED);
            }
        }
    }

    /**
     * Check workflow prerequisites
     */
    private function checkPrerequisites(array $prerequisites, array $context): bool
    {
        if (empty($prerequisites)) {
            return true;
        }

        $this->output->writeLine('🔍 Checking prerequisites...', ConsoleColor::YELLOW);

        foreach ($prerequisites as $prerequisite) {
            // Simple prerequisite checking - can be extended
            if ($prerequisite === 'database') {
                $result = $this->checkDatabaseConnection();
            } elseif ($prerequisite === 'migrations') {
                $result = $this->checkMigrations();
            } else {
                $result = true;
            }

            if (!$result) {
                $this->output->writeLine("❌ Prerequisite failed: {$prerequisite}", ConsoleColor::RED);
                return false;
            }

            $this->output->writeLine("✅ {$prerequisite}", ConsoleColor::GREEN);
        }

        return true;
    }

    /**
     * Substitute variables in parameters
     */
    private function substituteVariables(string $value, array $context): string
    {
        // Simple variable substitution: {{variable}}
        return preg_replace_callback('/\{\{(\w+)\}\}/', function($matches) use ($context) {
            return $context[$matches[1]] ?? $matches[0];
        }, $value);
    }

    /**
     * Check database connection (placeholder)
     */
    private function checkDatabaseConnection(): bool
    {
        // Implementation would check actual database connection
        return true;
    }

    /**
     * Check migrations (placeholder)
     */
    private function checkMigrations(): bool
    {
        // Implementation would check migration status
        return true;
    }
}