<?php

declare(strict_types=1);

namespace App\Framework\Process\Services;

use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Process\Process;
use App\Framework\Process\ValueObjects\Command;
use App\Framework\Process\ValueObjects\Logs\LogAnalysisResult;
use App\Framework\Process\ValueObjects\Logs\LogEntry;

/**
 * Log Analysis Service.
 *
 * Analysiert Log-Dateien und gibt strukturierte Ergebnisse zurück.
 */
final readonly class LogAnalysisService
{
    public function __construct(
        private Process $process
    ) {
    }

    /**
     * Analysiert eine Log-Datei.
     */
    public function analyze(
        FilePath $logFile,
        int $lines = 1000,
        ?string $levelFilter = null
    ): LogAnalysisResult {
        $command = $this->buildCommand($logFile, $lines, $levelFilter);

        $result = $this->process->run($command);

        if (! $result->isSuccess()) {
            return LogAnalysisResult::fromEntries([], 0);
        }

        $logLines = explode("\n", trim($result->stdout));
        $entries = array_map(
            fn (string $line) => LogEntry::fromLine($line),
            array_filter($logLines)
        );

        return LogAnalysisResult::fromEntries($entries, count($logLines));
    }

    /**
     * Findet Errors in einer Log-Datei.
     */
    public function findErrors(FilePath $logFile, int $lines = 1000): LogAnalysisResult
    {
        return $this->analyze($logFile, $lines, 'ERROR');
    }

    /**
     * Findet Warnings in einer Log-Datei.
     */
    public function findWarnings(FilePath $logFile, int $lines = 1000): LogAnalysisResult
    {
        return $this->analyze($logFile, $lines, 'WARNING');
    }

    /**
     * Gibt die letzten N Zeilen einer Log-Datei zurück.
     */
    public function tail(FilePath $logFile, int $lines = 100): string
    {
        $result = $this->process->run(
            Command::fromArray(['tail', '-n', (string) $lines, $logFile->toString()])
        );

        return $result->stdout;
    }

    /**
     * Sucht nach einem Pattern in einer Log-Datei.
     */
    public function search(FilePath $logFile, string $pattern, int $lines = 1000): LogAnalysisResult
    {
        $command = Command::fromString(
            "tail -n {$lines} {$logFile->toString()} | grep -i '{$pattern}'"
        );

        $result = $this->process->run($command);

        $logLines = explode("\n", trim($result->stdout));
        $entries = array_map(
            fn (string $line) => LogEntry::fromLine($line),
            array_filter($logLines)
        );

        return LogAnalysisResult::fromEntries($entries, count($logLines));
    }

    /**
     * Gibt Log-Statistiken zurück.
     *
     * @return array<string, mixed>
     */
    public function getStatistics(FilePath $logFile, int $lines = 1000): array
    {
        $analysis = $this->analyze($logFile, $lines);

        return [
            'total_lines' => $analysis->totalLines,
            'error_count' => $analysis->getErrorCount(),
            'warning_count' => $analysis->getWarningCount(),
            'level_distribution' => $analysis->levelCounts,
            'top_errors' => $analysis->getTopErrors(5),
        ];
    }

    /**
     * Baut das Kommando zum Lesen der Log-Datei.
     */
    private function buildCommand(FilePath $logFile, int $lines, ?string $levelFilter): Command
    {
        $cmd = "tail -n {$lines} {$logFile->toString()}";

        if ($levelFilter !== null) {
            $cmd .= " | grep -i '{$levelFilter}'";
        }

        return Command::fromString($cmd);
    }
}
