<?php

declare(strict_types=1);

namespace App\Framework\ErrorAggregation\Storage;

use App\Framework\ErrorAggregation\ErrorEvent;
use App\Framework\ErrorAggregation\ErrorPattern;
use App\Framework\Exception\Core\ErrorSeverity;

/**
 * In-memory error storage for testing
 */
final class InMemoryErrorStorage implements ErrorStorageInterface
{
    /** @var array<string, ErrorEvent> */
    private array $events = [];

    /** @var array<string, ErrorPattern> */
    private array $patterns = [];

    public function storeEvent(ErrorEvent $event): void
    {
        $this->events[(string) $event->id] = $event;
    }

    public function storeEventsBatch(array $events): void
    {
        foreach ($events as $event) {
            $this->storeEvent($event);
        }
    }

    public function storePattern(ErrorPattern $pattern): void
    {
        $this->patterns[(string) $pattern->id] = $pattern;
    }

    public function getPatternById(string $patternId): ?ErrorPattern
    {
        return $this->patterns[$patternId] ?? null;
    }

    public function getPatternByFingerprint(string $fingerprint): ?ErrorPattern
    {
        foreach ($this->patterns as $pattern) {
            if ($pattern->fingerprint === $fingerprint) {
                return $pattern;
            }
        }

        return null;
    }

    public function getActivePatterns(int $limit = 50, int $offset = 0): array
    {
        $activePatterns = array_filter(
            $this->patterns,
            fn(ErrorPattern $pattern) => $pattern->isActive
        );

        return array_slice(array_values($activePatterns), $offset, $limit);
    }

    public function getPatternsByService(string $service, int $limit = 50): array
    {
        $servicePatterns = array_filter(
            $this->patterns,
            fn(ErrorPattern $pattern) => $pattern->service === $service
        );

        return array_slice(array_values($servicePatterns), 0, $limit);
    }

    public function getRecentEvents(int $limit = 100, ?ErrorSeverity $severity = null): array
    {
        $events = $this->events;

        if ($severity !== null) {
            $events = array_filter(
                $events,
                fn(ErrorEvent $event) => $event->severity === $severity
            );
        }

        // Sort by occurredAt descending
        usort($events, fn(ErrorEvent $a, ErrorEvent $b) =>
            $b->occurredAt <=> $a->occurredAt
        );

        return array_slice($events, 0, $limit);
    }

    public function getStatistics(\DateTimeImmutable $from, \DateTimeImmutable $to): array
    {
        $eventsInRange = array_filter(
            $this->events,
            fn(ErrorEvent $event) =>
                $event->occurredAt >= $from && $event->occurredAt <= $to
        );

        $bySeverity = [];
        foreach ($eventsInRange as $event) {
            $severity = $event->severity->value;
            $bySeverity[$severity] = ($bySeverity[$severity] ?? 0) + 1;
        }

        $services = array_unique(array_map(
            fn(ErrorEvent $event) => $event->service,
            $eventsInRange
        ));

        $users = array_unique(array_filter(array_map(
            fn(ErrorEvent $event) => $event->userId,
            $eventsInRange
        )));

        $ips = array_unique(array_filter(array_map(
            fn(ErrorEvent $event) => $event->clientIp,
            $eventsInRange
        )));

        return [
            'total_events' => count($eventsInRange),
            'by_severity' => $bySeverity,
            'services_affected' => count($services),
            'users_affected' => count($users),
            'ips_affected' => count($ips),
        ];
    }

    public function getErrorTrends(
        \DateTimeImmutable $from,
        \DateTimeImmutable $to,
        string $groupBy = 'hour'
    ): array {
        $eventsInRange = array_filter(
            $this->events,
            fn(ErrorEvent $event) =>
                $event->occurredAt >= $from && $event->occurredAt <= $to
        );

        $trends = [];
        foreach ($eventsInRange as $event) {
            $period = $event->occurredAt->format('Y-m-d H:00:00');
            $trends[$period] = ($trends[$period] ?? 0) + 1;
        }

        return $trends;
    }

    public function getTopPatterns(int $limit = 10, ?string $service = null): array
    {
        $patterns = $this->patterns;

        if ($service !== null) {
            $patterns = array_filter(
                $patterns,
                fn(ErrorPattern $pattern) => $pattern->service === $service
            );
        }

        usort($patterns, fn(ErrorPattern $a, ErrorPattern $b) =>
            $b->occurrenceCount <=> $a->occurrenceCount
        );

        return array_slice($patterns, 0, $limit);
    }

    public function deleteOldEvents(\DateTimeImmutable $cutoffDate, ErrorSeverity $severity): int
    {
        $beforeCount = count($this->events);

        $this->events = array_filter(
            $this->events,
            fn(ErrorEvent $event) =>
                $event->occurredAt >= $cutoffDate || $event->severity !== $severity
        );

        return $beforeCount - count($this->events);
    }

    public function deleteOldPatterns(\DateTimeImmutable $cutoffDate): int
    {
        $beforeCount = count($this->patterns);

        $this->patterns = array_filter(
            $this->patterns,
            fn(ErrorPattern $pattern) =>
                $pattern->lastOccurrence >= $cutoffDate || $pattern->isActive
        );

        return $beforeCount - count($this->patterns);
    }

    public function exportEvents(
        \DateTimeImmutable $from,
        \DateTimeImmutable $to,
        array $filters = []
    ): \Generator {
        $eventsInRange = array_filter(
            $this->events,
            fn(ErrorEvent $event) =>
                $event->occurredAt >= $from && $event->occurredAt <= $to
        );

        foreach ($eventsInRange as $event) {
            yield $event->toArray();
        }
    }

    public function getHealthStatus(): array
    {
        return [
            'status' => 'healthy',
            'event_count' => count($this->events),
            'pattern_count' => count($this->patterns),
        ];
    }
}
