<?php

declare(strict_types=1);

namespace App\Framework\Discovery\Memory;

use App\Framework\Core\ValueObjects\Byte;

/**
 * Memory guard for continuous memory monitoring during discovery operations
 *
 * Provides real-time memory monitoring with automatic emergency handling
 * to prevent out-of-memory conditions during long-running operations.
 */
final class MemoryGuard
{
    private CircularMemoryBuffer $memoryHistory;

    private int $checkCounter = 0;

    private bool $emergencyMode = false;

    private mixed $emergencyCallback = null;

    public function __construct(
        private readonly DiscoveryMemoryManager $memoryManager,
        ?callable $emergencyCallback = null
    ) {
        $this->memoryHistory = new CircularMemoryBuffer(maxSize: 100);
        $this->emergencyCallback = $emergencyCallback;
    }

    /**
     * Check memory status and take action if needed
     */
    public function check(): GuardResult
    {
        $this->checkCounter++;
        $memoryStatus = $this->memoryManager->getMemoryStatus();

        // Track memory history for leak detection using CircularMemoryBuffer
        $this->memoryHistory->add($memoryStatus->currentUsage);

        $actions = [];

        // Handle critical memory situations
        if ($memoryStatus->status === MemoryStatus::CRITICAL && ! $this->emergencyMode) {
            $this->emergencyMode = true;
            $actions[] = GuardAction::EMERGENCY_CLEANUP;

            if ($this->emergencyCallback) {
                ($this->emergencyCallback)();
            }

            // Perform immediate cleanup
            $cleanupResult = $this->memoryManager->performCleanup();
            $actions[] = $cleanupResult->wasEffective()
                ? GuardAction::CLEANUP_SUCCESSFUL
                : GuardAction::CLEANUP_FAILED;
        }

        // Handle warning conditions
        if ($memoryStatus->status === MemoryStatus::WARNING) {
            $actions[] = GuardAction::WARNING_ISSUED;

            // Suggest cleanup if it's time
            if ($this->memoryManager->shouldCleanup($this->checkCounter)) {
                $actions[] = GuardAction::CLEANUP_SUGGESTED;
            }
        }

        // Check for memory leaks periodically using CircularMemoryBuffer
        if ($this->checkCounter % 20 === 0) {
            $leakInfo = $this->memoryManager->checkForMemoryLeaks($this->memoryHistory);
            if ($leakInfo !== null) {
                $actions[] = match ($leakInfo->severity) {
                    LeakSeverity::CRITICAL => GuardAction::CRITICAL_LEAK_DETECTED,
                    LeakSeverity::HIGH => GuardAction::HIGH_LEAK_DETECTED,
                    LeakSeverity::MEDIUM => GuardAction::MEDIUM_LEAK_DETECTED,
                    LeakSeverity::LOW => GuardAction::LOW_LEAK_DETECTED
                };
            }
        }

        // Reset emergency mode if memory is back to normal
        if ($this->emergencyMode && $memoryStatus->status === MemoryStatus::NORMAL) {
            $this->emergencyMode = false;
            $actions[] = GuardAction::EMERGENCY_MODE_RESET;
        }

        return new GuardResult(
            memoryStatus: $memoryStatus,
            actions: $actions,
            checkNumber: $this->checkCounter,
            emergencyMode: $this->emergencyMode
        );
    }

    /**
     * Force emergency cleanup
     */
    public function forceEmergencyCleanup(): MemoryCleanupResult
    {
        $this->emergencyMode = true;

        if ($this->emergencyCallback) {
            ($this->emergencyCallback)();
        }

        return $this->memoryManager->performCleanup();
    }

    /**
     * Get current memory statistics
     */
    public function getStatistics(): GuardStatistics
    {
        $memoryStatus = $this->memoryManager->getMemoryStatus();

        $samples = $this->memoryHistory->getSamples();
        $historyCount = $this->memoryHistory->getCount();

        $averageUsage = $historyCount > 0
            ? Byte::fromBytes((int) (array_sum(array_map(fn (Byte $byte) => $byte->toBytes(), $samples)) / $historyCount))
            : Byte::zero();

        $peakUsage = $historyCount > 0
            ? array_reduce($samples, fn ($max, $current) => $current->greaterThan($max ?? Byte::zero()) ? $current : $max, Byte::zero())
            : Byte::zero();

        return new GuardStatistics(
            totalChecks: $this->checkCounter,
            currentStatus: $memoryStatus,
            averageUsage: $averageUsage,
            peakUsage: $peakUsage,
            historySize: $historyCount,
            emergencyMode: $this->emergencyMode
        );
    }

    /**
     * Reset guard state
     */
    public function reset(): void
    {
        $this->memoryHistory->clear();
        $this->checkCounter = 0;
        $this->emergencyMode = false;
    }

    /**
     * Check if it's safe to continue processing
     */
    public function isSafeToProcess(): bool
    {
        $memoryStatus = $this->memoryManager->getMemoryStatus();

        return $memoryStatus->status !== MemoryStatus::CRITICAL;
    }

    /**
     * Get recommendations for current memory state
     * @return string[]
     */
    public function getRecommendations(): array
    {
        $memoryStatus = $this->memoryManager->getMemoryStatus();
        $recommendations = [];

        switch ($memoryStatus->status) {
            case MemoryStatus::CRITICAL:
                $recommendations[] = 'Immediate action required: Reduce batch size or stop processing';
                $recommendations[] = 'Consider switching to STREAMING memory strategy';
                $recommendations[] = 'Force garbage collection and cleanup caches';

                break;

            case MemoryStatus::WARNING:
                $recommendations[] = 'Consider reducing batch size';
                $recommendations[] = 'Enable more frequent cleanup cycles';
                $recommendations[] = 'Monitor for memory leaks';

                break;

            case MemoryStatus::NORMAL:
                if ($memoryStatus->memoryPressure->toDecimal() > 0.5) {
                    $recommendations[] = 'Memory usage is moderate - consider optimization';
                }

                break;
        }

        // Check for potential leaks using CircularMemoryBuffer
        if ($this->memoryHistory->getCount() > 10) {
            $leakInfo = $this->memoryManager->checkForMemoryLeaks($this->memoryHistory);
            if ($leakInfo !== null) {
                $recommendations[] = "Memory leak detected ({$leakInfo->severity->value}): Review object retention";
            }
        }

        return $recommendations;
    }
}
