<?php

declare(strict_types=1);

namespace App\Framework\Deployment\Pipeline\Stages;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Deployment\Pipeline\ValueObjects\DeploymentEnvironment;
use App\Framework\Deployment\Pipeline\ValueObjects\PipelineStage;
use App\Framework\Deployment\Pipeline\ValueObjects\StageResult;
use App\Framework\Filesystem\Directory;
use App\Framework\Filesystem\File;
use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Logging\Logger;

/**
 * Cleanup Stage
 *
 * Cleans up temporary files, old releases, and caches after deployment.
 */
final readonly class CleanupStage implements PipelineStageInterface
{
    public function __construct(
        private Directory $directory,
        private File $file,
        private Logger $logger
    ) {}

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

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

        $this->logger->info('Running cleanup', [
            'environment' => $environment->value
        ]);

        try {
            $cleanedItems = [];

            // Clean cache directories
            $cacheCleanup = $this->cleanCacheDirectories();
            if ($cacheCleanup['success']) {
                $cleanedItems[] = sprintf('Cleaned %d cache files', $cacheCleanup['count']);
            }

            // Clean old log files (older than 30 days)
            $logCleanup = $this->cleanOldLogs(days: 30);
            if ($logCleanup['success']) {
                $cleanedItems[] = sprintf('Cleaned %d old log files', $logCleanup['count']);
            }

            // Clean temporary files
            $tempCleanup = $this->cleanTemporaryFiles();
            if ($tempCleanup['success']) {
                $cleanedItems[] = sprintf('Cleaned %d temporary files', $tempCleanup['count']);
            }

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

            return StageResult::success(
                stage: $this->getStage(),
                duration: $duration,
                output: implode("\n", $cleanedItems)
            );

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

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

            // Cleanup failures should not fail the deployment
            return StageResult::success(
                stage: $this->getStage(),
                duration: $duration,
                output: 'Cleanup completed with warnings: ' . $e->getMessage()
            );
        }
    }

    public function canRollback(): bool
    {
        return false; // Cleanup can't be rolled back (files already deleted)
    }

    public function rollback(DeploymentEnvironment $environment): StageResult
    {
        throw new \LogicException('Cleanup stage does not support rollback');
    }

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

    public function shouldSkip(DeploymentEnvironment $environment): bool
    {
        return false; // Always run cleanup
    }

    /**
     * Clean cache directories
     */
    private function cleanCacheDirectories(): array
    {
        try {
            $cacheDir = FilePath::fromString('/var/www/html/storage/cache');

            if (!$this->directory->exists($cacheDir)) {
                return ['success' => true, 'count' => 0];
            }

            $files = $this->directory->listFiles($cacheDir);
            $count = 0;

            foreach ($files as $file) {
                try {
                    if ($this->file->exists($file)) {
                        $this->file->delete($file);
                        $count++;
                    }
                } catch (\Throwable $e) {
                    $this->logger->warning('Failed to delete cache file', [
                        'file' => $file->value,
                        'error' => $e->getMessage()
                    ]);
                }
            }

            return ['success' => true, 'count' => $count];

        } catch (\Throwable $e) {
            $this->logger->warning('Cache cleanup failed', [
                'error' => $e->getMessage()
            ]);

            return ['success' => false, 'count' => 0];
        }
    }

    /**
     * Clean old log files
     */
    private function cleanOldLogs(int $days): array
    {
        try {
            $logsDir = FilePath::fromString('/var/www/html/storage/logs');

            if (!$this->directory->exists($logsDir)) {
                return ['success' => true, 'count' => 0];
            }

            $files = $this->directory->listFiles($logsDir);
            $count = 0;
            $cutoffTime = time() - ($days * 86400);

            foreach ($files as $file) {
                try {
                    if (!$this->file->exists($file)) {
                        continue;
                    }

                    $metadata = $this->file->getMetadata($file);

                    if ($metadata->modifiedAt->getTimestamp() < $cutoffTime) {
                        $this->file->delete($file);
                        $count++;
                    }
                } catch (\Throwable $e) {
                    $this->logger->warning('Failed to delete log file', [
                        'file' => $file->value,
                        'error' => $e->getMessage()
                    ]);
                }
            }

            return ['success' => true, 'count' => $count];

        } catch (\Throwable $e) {
            $this->logger->warning('Log cleanup failed', [
                'error' => $e->getMessage()
            ]);

            return ['success' => false, 'count' => 0];
        }
    }

    /**
     * Clean temporary files
     */
    private function cleanTemporaryFiles(): array
    {
        try {
            $tempDir = FilePath::fromString('/var/www/html/storage/tmp');

            if (!$this->directory->exists($tempDir)) {
                return ['success' => true, 'count' => 0];
            }

            $files = $this->directory->listFiles($tempDir);
            $count = 0;

            foreach ($files as $file) {
                try {
                    if ($this->file->exists($file)) {
                        $this->file->delete($file);
                        $count++;
                    }
                } catch (\Throwable $e) {
                    $this->logger->warning('Failed to delete temp file', [
                        'file' => $file->value,
                        'error' => $e->getMessage()
                    ]);
                }
            }

            return ['success' => true, 'count' => $count];

        } catch (\Throwable $e) {
            $this->logger->warning('Temp cleanup failed', [
                'error' => $e->getMessage()
            ]);

            return ['success' => false, 'count' => 0];
        }
    }
}
