<?php

declare(strict_types=1);

namespace App\Framework\Logging\ValueObjects;

use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Logging\LogLevel;

/**
 * Log Read Result Value Object
 * Immutable result from reading log files
 */
final readonly class LogReadResult
{
    /**
     * @param array<int, LogEntry> $entries
     */
    public function __construct(
        public LogName $logName,
        public FilePath $logPath,
        public array $entries,
        public int $totalEntries,
        public int $limit,
        public ?string $search = null,
        public ?LogLevel $levelFilter = null
    ) {
        // Validate entries array contains only LogEntry objects
        foreach ($entries as $entry) {
            if (!$entry instanceof LogEntry) {
                throw new \InvalidArgumentException('All entries must be LogEntry instances');
            }
        }
    }

    /**
     * Create empty result
     */
    public static function empty(LogName $logName, FilePath $logPath): self
    {
        return new self(
            logName: $logName,
            logPath: $logPath,
            entries: [],
            totalEntries: 0,
            limit: 0
        );
    }

    /**
     * Create from entries array
     */
    public static function fromEntries(
        LogName $logName,
        FilePath $logPath,
        array $entries,
        int $limit,
        ?string $search = null,
        ?LogLevel $levelFilter = null
    ): self {
        return new self(
            logName: $logName,
            logPath: $logPath,
            entries: $entries,
            totalEntries: count($entries),
            limit: $limit,
            search: $search,
            levelFilter: $levelFilter
        );
    }

    /**
     * Check if result is empty
     */
    public function isEmpty(): bool
    {
        return $this->totalEntries === 0;
    }

    /**
     * Check if result has entries
     */
    public function hasEntries(): bool
    {
        return $this->totalEntries > 0;
    }

    /**
     * Get first entry
     */
    public function first(): ?LogEntry
    {
        return $this->entries[0] ?? null;
    }

    /**
     * Get last entry
     */
    public function last(): ?LogEntry
    {
        return $this->entries[array_key_last($this->entries)] ?? null;
    }

    /**
     * Check if search was applied
     */
    public function hasSearch(): bool
    {
        return $this->search !== null && $this->search !== '';
    }

    /**
     * Check if level filter was applied
     */
    public function hasLevelFilter(): bool
    {
        return $this->levelFilter !== null;
    }

    /**
     * Get entries filtered by level
     */
    public function filterByLevel(LogLevel $level): self
    {
        $filtered = array_filter(
            $this->entries,
            fn(LogEntry $entry) => $entry->matchesLevel($level)
        );

        return new self(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: array_values($filtered),
            totalEntries: count($filtered),
            limit: $this->limit,
            search: $this->search,
            levelFilter: $level
        );
    }

    /**
     * Get entries filtered by search term
     */
    public function filterBySearch(string $search): self
    {
        $filtered = array_filter(
            $this->entries,
            fn(LogEntry $entry) => $entry->matchesSearch($search)
        );

        return new self(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: array_values($filtered),
            totalEntries: count($filtered),
            limit: $this->limit,
            search: $search,
            levelFilter: $this->levelFilter
        );
    }

    /**
     * Get entries with minimum log level
     */
    public function filterByMinimumLevel(LogLevel $minimumLevel): self
    {
        $filtered = array_filter(
            $this->entries,
            fn(LogEntry $entry) => $entry->isAtLeastLevel($minimumLevel)
        );

        return new self(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: array_values($filtered),
            totalEntries: count($filtered),
            limit: $this->limit,
            search: $this->search,
            levelFilter: $minimumLevel
        );
    }

    /**
     * Take first N entries
     */
    public function take(int $count): self
    {
        $taken = array_slice($this->entries, 0, $count);

        return new self(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $taken,
            totalEntries: count($taken),
            limit: $count,
            search: $this->search,
            levelFilter: $this->levelFilter
        );
    }

    /**
     * Skip first N entries
     */
    public function skip(int $count): self
    {
        $remaining = array_slice($this->entries, $count);

        return new self(
            logName: $this->logName,
            logPath: $this->logPath,
            entries: $remaining,
            totalEntries: count($remaining),
            limit: $this->limit,
            search: $this->search,
            levelFilter: $this->levelFilter
        );
    }

    /**
     * Convert to array for backwards compatibility
     */
    public function toArray(): array
    {
        return [
            'log_name' => $this->logName->value,
            'log_path' => $this->logPath->toString(),
            'entries' => array_map(
                fn(LogEntry $entry) => $entry->toArray(),
                $this->entries
            ),
            'total_entries' => $this->totalEntries,
            'limit' => $this->limit,
            'search' => $this->search,
            'level_filter' => $this->levelFilter?->getName(),
        ];
    }

    /**
     * Get metadata about the result
     */
    public function getMetadata(): array
    {
        return [
            'log_name' => $this->logName->value,
            'log_path' => $this->logPath->toString(),
            'total_entries' => $this->totalEntries,
            'limit' => $this->limit,
            'has_search' => $this->hasSearch(),
            'search_term' => $this->search,
            'has_level_filter' => $this->hasLevelFilter(),
            'level_filter' => $this->levelFilter?->getName(),
            'is_empty' => $this->isEmpty(),
            'file_info' => [
                'size' => $this->logPath->getSize()->toHumanReadable(),
                'modified' => date('Y-m-d H:i:s', $this->logPath->getModifiedTime()),
                'readable' => $this->logPath->isReadable(),
            ],
        ];
    }

    /**
     * Get statistics about log levels in result
     */
    public function getLevelStatistics(): array
    {
        $stats = [];

        foreach ($this->entries as $entry) {
            $levelName = $entry->level->getName();
            $stats[$levelName] = ($stats[$levelName] ?? 0) + 1;
        }

        // Sort by severity (highest first)
        uksort($stats, function($a, $b) {
            $levelA = LogLevel::cases()[array_search($a, array_column(LogLevel::cases(), 'name'))];
            $levelB = LogLevel::cases()[array_search($b, array_column(LogLevel::cases(), 'name'))];
            return $levelB->value <=> $levelA->value;
        });

        return $stats;
    }

    /**
     * Create iterator for entries
     */
    public function getIterator(): \ArrayIterator
    {
        return new \ArrayIterator($this->entries);
    }
}
