<?php

declare(strict_types=1);

namespace App\Framework\MachineLearning\ModelManagement;

use App\Framework\Core\ValueObjects\Version;
use App\Framework\Core\ValueObjects\Duration;

/**
 * In-Memory Performance Storage Implementation
 *
 * Stores performance tracking data in memory for testing.
 */
final class InMemoryPerformanceStorage implements PerformanceStorage
{
    /** @var array<array> */
    private array $predictions = [];

    /** @var array<string, array{avg: float, stdDev: float}> */
    private array $confidenceBaselines = [];

    /**
     * Store a prediction record
     */
    public function storePrediction(array $predictionRecord): void
    {
        $this->predictions[] = $predictionRecord;
    }

    /**
     * Get predictions within time window
     */
    public function getPredictions(
        string $modelName,
        Version $version,
        Duration $timeWindow
    ): array {
        $cutoffTime = (new \DateTimeImmutable())->sub(
            new \DateInterval('PT' . $timeWindow->toSeconds() . 'S')
        );

        return array_values(array_filter(
            $this->predictions,
            fn($record) =>
                $record['model_name'] === $modelName
                && $record['version'] === $version->toString()
                && $record['timestamp'] >= $cutoffTime
        ));
    }

    /**
     * Get historical average confidence for baseline
     */
    public function getHistoricalAverageConfidence(
        string $modelName,
        Version $version
    ): ?float {
        $key = $this->getBaselineKey($modelName, $version);

        return $this->confidenceBaselines[$key]['avg'] ?? null;
    }

    /**
     * Store confidence baseline for drift detection
     */
    public function storeConfidenceBaseline(
        string $modelName,
        Version $version,
        float $avgConfidence,
        float $stdDevConfidence
    ): void {
        $key = $this->getBaselineKey($modelName, $version);

        $this->confidenceBaselines[$key] = [
            'avg' => $avgConfidence,
            'stdDev' => $stdDevConfidence
        ];
    }

    /**
     * Clear old prediction records (cleanup)
     */
    public function clearOldPredictions(Duration $olderThan): int
    {
        $cutoffTime = (new \DateTimeImmutable())->sub(
            new \DateInterval('PT' . $olderThan->toSeconds() . 'S')
        );

        $initialCount = count($this->predictions);

        $this->predictions = array_values(array_filter(
            $this->predictions,
            fn($record) => $record['timestamp'] >= $cutoffTime
        ));

        return $initialCount - count($this->predictions);
    }

    /**
     * Get recent predictions with limit
     */
    public function getRecentPredictions(
        string $modelName,
        Version $version,
        int $limit
    ): array {
        // Filter by model and version
        $filtered = array_filter(
            $this->predictions,
            fn($record) =>
                $record['model_name'] === $modelName
                && $record['version'] === $version->toString()
        );

        // Sort by timestamp descending (most recent first)
        usort($filtered, fn($a, $b) => $b['timestamp'] <=> $a['timestamp']);

        // Limit results
        return array_values(array_slice($filtered, 0, $limit));
    }

    /**
     * Calculate accuracy from recent predictions
     */
    public function calculateAccuracy(
        string $modelName,
        Version $version,
        int $limit
    ): float {
        $predictions = $this->getRecentPredictions($modelName, $version, $limit);

        if (empty($predictions)) {
            return 0.0;
        }

        $correctCount = 0;
        $totalCount = 0;

        foreach ($predictions as $prediction) {
            // Only count predictions that have actual labels
            if (!isset($prediction['actual_label'])) {
                continue;
            }

            $totalCount++;

            if (isset($prediction['predicted_label'])
                && $prediction['predicted_label'] === $prediction['actual_label']) {
                $correctCount++;
            }
        }

        if ($totalCount === 0) {
            return 0.0;
        }

        return $correctCount / $totalCount;
    }

    /**
     * Get confidence baseline as array
     */
    public function getConfidenceBaseline(
        string $modelName,
        Version $version
    ): ?array {
        $key = $this->getBaselineKey($modelName, $version);

        if (!isset($this->confidenceBaselines[$key])) {
            return null;
        }

        return [
            'avg_confidence' => $this->confidenceBaselines[$key]['avg'],
            'std_dev_confidence' => $this->confidenceBaselines[$key]['stdDev'],
        ];
    }

    /**
     * Get baseline key for confidence storage
     */
    private function getBaselineKey(string $modelName, Version $version): string
    {
        return "{$modelName}:{$version->toString()}";
    }

    /**
     * Get all stored predictions (for testing)
     */
    public function getAllPredictions(): array
    {
        return $this->predictions;
    }

    /**
     * Clear all data (for testing)
     */
    public function clear(): void
    {
        $this->predictions = [];
        $this->confidenceBaselines = [];
    }
}
