<?php

declare(strict_types=1);

namespace App\Framework\ErrorReporting\Storage;

use App\Framework\ErrorReporting\ErrorReport;
use App\Framework\ErrorReporting\ErrorReportCriteria;
use App\Framework\ErrorReporting\ErrorStatistics;
use DateTimeImmutable;

/**
 * In-memory implementation of error report storage for testing
 */
final class InMemoryErrorReportStorage implements ErrorReportStorageInterface
{
    /** @var array<string, ErrorReport> */
    private array $reports = [];

    public function store(ErrorReport $report): void
    {
        $this->reports[$report->id] = $report;
    }

    public function storeBatch(array $reports): void
    {
        foreach ($reports as $report) {
            $this->store($report);
        }
    }

    public function find(string $reportId): ?ErrorReport
    {
        return $this->reports[$reportId] ?? null;
    }

    public function findRecent(int $limit = 100, int $offset = 0): array
    {
        $reports = array_values($this->reports);

        // Sort by timestamp descending
        usort($reports, fn ($a, $b) => $b->timestamp <=> $a->timestamp);

        return array_slice($reports, $offset, $limit);
    }

    public function findByCriteria(ErrorReportCriteria $criteria): array
    {
        return array_filter($this->reports, function (ErrorReport $report) use ($criteria) {
            // Simple criteria filtering - can be expanded
            if ($criteria->level && $report->level !== $criteria->level) {
                return false;
            }

            if ($criteria->exception && $report->exception !== $criteria->exception) {
                return false;
            }

            if ($criteria->from && $report->timestamp < $criteria->from) {
                return false;
            }

            if ($criteria->to && $report->timestamp > $criteria->to) {
                return false;
            }

            return true;
        });
    }

    public function countByCriteria(ErrorReportCriteria $criteria): int
    {
        return count($this->findByCriteria($criteria));
    }

    public function getStatistics(DateTimeImmutable $from, DateTimeImmutable $to): ErrorStatistics
    {
        $reportsInRange = array_filter(
            $this->reports,
            fn (ErrorReport $r) => $r->timestamp >= $from && $r->timestamp <= $to
        );

        $totalCount = count($reportsInRange);
        $byLevel = [];
        $byException = [];

        foreach ($reportsInRange as $report) {
            $byLevel[$report->level] = ($byLevel[$report->level] ?? 0) + 1;
            $byException[$report->exception] = ($byException[$report->exception] ?? 0) + 1;
        }

        return new ErrorStatistics(
            totalCount: $totalCount,
            byLevel: $byLevel,
            byException: $byException,
            period: [
                'from' => $from->format('c'),
                'to' => $to->format('c'),
            ]
        );
    }

    public function getTrends(DateTimeImmutable $from, DateTimeImmutable $to, string $groupBy = 'hour'): array
    {
        $reportsInRange = array_filter(
            $this->reports,
            fn (ErrorReport $r) => $r->timestamp >= $from && $r->timestamp <= $to
        );

        $trends = [];

        foreach ($reportsInRange as $report) {
            $key = match ($groupBy) {
                'hour' => $report->timestamp->format('Y-m-d H:00'),
                'day' => $report->timestamp->format('Y-m-d'),
                'week' => $report->timestamp->format('Y-W'),
                'month' => $report->timestamp->format('Y-m'),
                default => $report->timestamp->format('Y-m-d H:00'),
            };

            $trends[$key] = ($trends[$key] ?? 0) + 1;
        }

        return $trends;
    }

    public function getTopErrors(DateTimeImmutable $from, DateTimeImmutable $to, int $limit = 10): array
    {
        $reportsInRange = array_filter(
            $this->reports,
            fn (ErrorReport $r) => $r->timestamp >= $from && $r->timestamp <= $to
        );

        $errorCounts = [];

        foreach ($reportsInRange as $report) {
            $fingerprint = $report->getFingerprint();

            if (!isset($errorCounts[$fingerprint])) {
                $errorCounts[$fingerprint] = [
                    'fingerprint' => $fingerprint,
                    'count' => 0,
                    'first_seen' => $report->timestamp,
                    'last_seen' => $report->timestamp,
                    'example' => $report,
                ];
            }

            $errorCounts[$fingerprint]['count']++;
            $errorCounts[$fingerprint]['last_seen'] = max(
                $errorCounts[$fingerprint]['last_seen'],
                $report->timestamp
            );
        }

        // Sort by count descending
        usort($errorCounts, fn ($a, $b) => $b['count'] <=> $a['count']);

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

    public function findByFingerprint(string $fingerprint, int $limit = 100): array
    {
        $matching = array_filter(
            $this->reports,
            fn (ErrorReport $r) => $r->getFingerprint() === $fingerprint
        );

        // Sort by timestamp descending
        $matching = array_values($matching);
        usort($matching, fn ($a, $b) => $b->timestamp <=> $a->timestamp);

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

    public function deleteOlderThan(DateTimeImmutable $before): int
    {
        $count = 0;

        foreach ($this->reports as $id => $report) {
            if ($report->timestamp < $before) {
                unset($this->reports[$id]);
                $count++;
            }
        }

        return $count;
    }

    public function deleteByCriteria(ErrorReportCriteria $criteria): int
    {
        $toDelete = $this->findByCriteria($criteria);
        $count = 0;

        foreach ($toDelete as $report) {
            unset($this->reports[$report->id]);
            $count++;
        }

        return $count;
    }

    public function getHealthInfo(): array
    {
        return [
            'status' => 'healthy',
            'total_reports' => count($this->reports),
            'storage_type' => 'in_memory',
            'memory_usage' => memory_get_usage(true),
        ];
    }

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