<?php

declare(strict_types=1);

namespace App\Framework\Queue\Services;

use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\Performance\ValueObjects\MemorySummary;
use App\Framework\Queue\ValueObjects\JobMetrics;

/**
 * Manages memory usage and optimization for queue jobs using framework's performance monitoring
 */
final readonly class JobMemoryManager
{
    private const CRITICAL_MEMORY_THRESHOLD = 90.0; // 90% of limit
    private const WARNING_MEMORY_THRESHOLD = 75.0;  // 75% of limit
    private const OPTIMAL_MEMORY_THRESHOLD = 50.0;  // 50% is considered optimal

    public function __construct(
        private MemoryMonitor $memoryMonitor,
        private Logger $logger
    ) {
    }

    /**
     * Get current memory usage for job context
     */
    public function getJobMemorySnapshot(string $jobId): array
    {
        $summary = $this->memoryMonitor->getSummary();

        return [
            'job_id' => $jobId,
            'timestamp' => date('Y-m-d H:i:s'),
            'current' => $summary->getCurrentHumanReadable(),
            'current_bytes' => $summary->getCurrentBytes(),
            'peak' => $summary->getPeakHumanReadable(),
            'peak_bytes' => $summary->getPeakBytes(),
            'limit' => $summary->getLimitHumanReadable(),
            'limit_bytes' => $summary->getLimitBytes(),
            'available' => $summary->getAvailableMemory()->toHumanReadable(),
            'available_bytes' => $summary->getAvailableMemory()->toBytes(),
            'usage_percentage' => $summary->getUsagePercentageFormatted(),
            'is_critical' => $summary->isMemoryCritical(self::CRITICAL_MEMORY_THRESHOLD),
            'is_warning' => $summary->isMemoryLow(self::WARNING_MEMORY_THRESHOLD),
            'status' => $this->getMemoryStatus($summary),
        ];
    }

    /**
     * Monitor memory during job execution
     */
    public function monitorJobExecution(string $jobId, string $phase): MemorySummary
    {
        $summary = $this->memoryMonitor->getSummary();

        // Log based on memory status
        if ($summary->isMemoryCritical(self::CRITICAL_MEMORY_THRESHOLD)) {
            $this->logger->error('Critical memory usage during job execution', LogContext::withData([
                'job_id' => $jobId,
                'phase' => $phase,
                'memory' => $summary->getCurrentHumanReadable(),
                'usage' => $summary->getUsagePercentageFormatted(),
                'available' => $summary->getAvailableMemory()->toHumanReadable(),
            ]));
        } elseif ($summary->isMemoryLow(self::WARNING_MEMORY_THRESHOLD)) {
            $this->logger->warning('High memory usage during job execution', LogContext::withData([
                'job_id' => $jobId,
                'phase' => $phase,
                'memory' => $summary->getCurrentHumanReadable(),
                'usage' => $summary->getUsagePercentageFormatted(),
            ]));
        }

        return $summary;
    }

    /**
     * Check if job should be aborted due to memory constraints
     */
    public function shouldAbortJob(string $jobId): bool
    {
        $summary = $this->memoryMonitor->getSummary();

        if ($summary->isMemoryCritical(self::CRITICAL_MEMORY_THRESHOLD)) {
            $this->logger->error('Job should be aborted due to critical memory usage', LogContext::withData([
                'job_id' => $jobId,
                'memory' => $summary->getCurrentHumanReadable(),
                'usage' => $summary->getUsagePercentageFormatted(),
                'limit' => $summary->getLimitHumanReadable(),
            ]));

            return true;
        }

        return false;
    }

    /**
     * Optimize memory before job execution
     */
    public function optimizeForJob(string $jobId): array
    {
        $before = $this->memoryMonitor->getSummary();

        // Force garbage collection
        gc_collect_cycles();

        $after = $this->memoryMonitor->getSummary();

        $freedBytes = $before->getCurrentBytes() - $after->getCurrentBytes();
        $freedMemory = Byte::fromBytes(max(0, $freedBytes));

        $optimization = [
            'job_id' => $jobId,
            'optimized_at' => date('Y-m-d H:i:s'),
            'before' => $before->getCurrentHumanReadable(),
            'after' => $after->getCurrentHumanReadable(),
            'freed' => $freedMemory->toHumanReadable(),
            'freed_bytes' => $freedMemory->toBytes(),
            'usage_before' => $before->getUsagePercentageFormatted(),
            'usage_after' => $after->getUsagePercentageFormatted(),
        ];

        $this->logger->debug('Memory optimized for job', LogContext::withData([
            'job_id' => $jobId,
            'freed' => $freedMemory->toHumanReadable(),
            'usage_before' => $before->getUsagePercentageFormatted(),
            'usage_after' => $after->getUsagePercentageFormatted(),
        ]));

        return $optimization;
    }

    /**
     * Calculate memory efficiency for job metrics
     */
    public function calculateJobMemoryEfficiency(array $jobMetrics): array
    {
        if (empty($jobMetrics)) {
            return [
                'total_jobs' => 0,
                'average_memory' => Byte::fromBytes(0),
                'peak_memory' => Byte::fromBytes(0),
                'total_memory' => Byte::fromBytes(0),
                'efficiency_score' => Percentage::from(100),
                'efficiency_rating' => 'excellent',
            ];
        }

        $totalBytes = 0;
        $peakBytes = 0;
        $jobCount = count($jobMetrics);

        foreach ($jobMetrics as $metrics) {
            if ($metrics instanceof JobMetrics) {
                $bytes = $metrics->memoryUsageBytes;
                $totalBytes += $bytes;
                $peakBytes = max($peakBytes, $bytes);
            }
        }

        $averageBytes = $jobCount > 0 ? (int)($totalBytes / $jobCount) : 0;
        $averageMemory = Byte::fromBytes($averageBytes);
        $peakMemory = Byte::fromBytes($peakBytes);
        $totalMemory = Byte::fromBytes($totalBytes);

        // Calculate efficiency score
        $memoryLimit = $this->memoryMonitor->getMemoryLimit();
        $optimalLimit = Byte::fromBytes((int)($memoryLimit->toBytes() * self::OPTIMAL_MEMORY_THRESHOLD / 100));

        // Efficiency is how well we stay under the optimal threshold
        $efficiencyRatio = $averageBytes > 0 ? min(1.0, $optimalLimit->toBytes() / $averageBytes) : 1.0;
        $efficiencyScore = Percentage::from($efficiencyRatio * 100);

        return [
            'total_jobs' => $jobCount,
            'average_memory' => $averageMemory,
            'average_memory_human' => $averageMemory->toHumanReadable(),
            'peak_memory' => $peakMemory,
            'peak_memory_human' => $peakMemory->toHumanReadable(),
            'total_memory' => $totalMemory,
            'total_memory_human' => $totalMemory->toHumanReadable(),
            'efficiency_score' => $efficiencyScore,
            'efficiency_score_formatted' => $efficiencyScore->format(2),
            'efficiency_rating' => $this->getEfficiencyRating($efficiencyScore),
        ];
    }

    /**
     * Get memory recommendations for job processing
     */
    public function getMemoryRecommendations(): array
    {
        $summary = $this->memoryMonitor->getSummary();
        $recommendations = [];
        $priority = 'low';

        if ($summary->isMemoryCritical(self::CRITICAL_MEMORY_THRESHOLD)) {
            $priority = 'critical';
            $recommendations[] = [
                'type' => 'critical',
                'message' => 'Memory usage is critical. Consider immediate action.',
                'actions' => [
                    'Increase memory limit or reduce job complexity',
                    'Force garbage collection before new jobs',
                    'Split large jobs into smaller chunks',
                    'Consider job queue throttling',
                ],
            ];
        } elseif ($summary->isMemoryLow(self::WARNING_MEMORY_THRESHOLD)) {
            $priority = 'warning';
            $recommendations[] = [
                'type' => 'warning',
                'message' => 'Memory usage is high. Monitor closely.',
                'actions' => [
                    'Schedule periodic garbage collection',
                    'Review job memory patterns',
                    'Consider optimizing job processing',
                    'Monitor for memory leaks',
                ],
            ];
        } else {
            $recommendations[] = [
                'type' => 'info',
                'message' => 'Memory usage is within normal range.',
                'actions' => [
                    'Continue monitoring',
                    'Maintain current optimization strategies',
                ],
            ];
        }

        return [
            'priority' => $priority,
            'current_usage' => $summary->getUsagePercentageFormatted(),
            'available' => $summary->getAvailableMemory()->toHumanReadable(),
            'recommendations' => $recommendations,
            'timestamp' => date('Y-m-d H:i:s'),
        ];
    }

    /**
     * Check if enough memory is available for job
     */
    public function hasEnoughMemoryForJob(Byte $estimatedMemory): bool
    {
        $summary = $this->memoryMonitor->getSummary();
        $available = $summary->getAvailableMemory();

        // Add 20% buffer for safety
        $requiredWithBuffer = Byte::fromBytes((int)($estimatedMemory->toBytes() * 1.2));

        return $available->toBytes() >= $requiredWithBuffer->toBytes();
    }

    /**
     * Get memory status string
     */
    private function getMemoryStatus(MemorySummary $summary): string
    {
        if ($summary->isMemoryCritical(self::CRITICAL_MEMORY_THRESHOLD)) {
            return 'critical';
        } elseif ($summary->isMemoryLow(self::WARNING_MEMORY_THRESHOLD)) {
            return 'warning';
        } elseif ($summary->usagePercentage->greaterThan(Percentage::from(self::OPTIMAL_MEMORY_THRESHOLD))) {
            return 'elevated';
        } else {
            return 'normal';
        }
    }

    /**
     * Get efficiency rating based on score
     */
    private function getEfficiencyRating(Percentage $score): string
    {
        $value = $score->getValue();

        return match (true) {
            $value >= 90 => 'excellent',
            $value >= 70 => 'good',
            $value >= 50 => 'fair',
            $value >= 30 => 'poor',
            default => 'critical'
        };
    }
}
