<?php

declare(strict_types=1);

namespace App\Framework\Deployment\Pipeline\Stages;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Database\Backup\BackupManager;
use App\Framework\Database\Backup\BackupMetadata;
use App\Framework\Deployment\Pipeline\ValueObjects\DeploymentEnvironment;
use App\Framework\Deployment\Pipeline\ValueObjects\PipelineStage;
use App\Framework\Deployment\Pipeline\ValueObjects\StageResult;
use App\Framework\Logging\Logger;

/**
 * Backup Stage
 *
 * Creates database and filesystem backups before deployment.
 * Supports rollback by restoring from backup.
 */
final class BackupStage implements PipelineStageInterface
{
    private ?BackupMetadata $lastBackup = null;

    public function __construct(
        private readonly BackupManager $backupManager,
        private readonly Logger $logger
    ) {}

    public function getStage(): PipelineStage
    {
        return PipelineStage::BACKUP;
    }

    public function execute(DeploymentEnvironment $environment): StageResult
    {
        if ($this->shouldSkip($environment)) {
            return StageResult::success(
                stage: $this->getStage(),
                duration: Duration::fromMilliseconds(0),
                output: 'Backup skipped for development environment'
            );
        }

        $startTime = microtime(true);

        $this->logger->info('Creating deployment backup', [
            'environment' => $environment->value
        ]);

        try {
            // Create database backup
            $backupMetadata = $this->backupManager->createBackup(
                description: "Pre-deployment backup for {$environment->value}"
            );

            // Store backup reference for potential rollback
            $this->lastBackup = $backupMetadata;

            $duration = Duration::fromSeconds(microtime(true) - $startTime);

            return StageResult::success(
                stage: $this->getStage(),
                duration: $duration,
                output: sprintf(
                    'Backup created: %s (Size: %s)',
                    $backupMetadata->filename,
                    $backupMetadata->size->toHumanReadable()
                )
            );

        } catch (\Throwable $e) {
            $duration = Duration::fromSeconds(microtime(true) - $startTime);

            $this->logger->error('Backup creation failed', [
                'error' => $e->getMessage(),
                'environment' => $environment->value
            ]);

            return StageResult::failure(
                stage: $this->getStage(),
                duration: $duration,
                error: $e->getMessage()
            );
        }
    }

    public function canRollback(): bool
    {
        return true; // Backup can be restored
    }

    public function rollback(DeploymentEnvironment $environment): StageResult
    {
        $startTime = microtime(true);

        if ($this->lastBackup === null) {
            return StageResult::failure(
                stage: $this->getStage(),
                duration: Duration::fromMilliseconds(0),
                error: 'No backup available for rollback'
            );
        }

        $this->logger->warning('Restoring from backup', [
            'environment' => $environment->value,
            'backup' => $this->lastBackup->filename
        ]);

        try {
            $this->backupManager->restore($this->lastBackup->filename);

            $duration = Duration::fromSeconds(microtime(true) - $startTime);

            return StageResult::success(
                stage: $this->getStage(),
                duration: $duration,
                output: 'Backup restored successfully'
            );

        } catch (\Throwable $e) {
            $duration = Duration::fromSeconds(microtime(true) - $startTime);

            $this->logger->error('Backup restoration failed', [
                'error' => $e->getMessage()
            ]);

            return StageResult::failure(
                stage: $this->getStage(),
                duration: $duration,
                error: $e->getMessage()
            );
        }
    }

    public function getDescription(): string
    {
        return $this->getStage()->getDescription();
    }

    public function shouldSkip(DeploymentEnvironment $environment): bool
    {
        // Skip backups in development
        return $environment === DeploymentEnvironment::DEVELOPMENT;
    }
}
