<?php
declare(strict_types=1);

namespace App\Framework\Worker;

use App\Framework\CommandBus\CommandBus;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleOutput;
use App\Framework\DateTime\Clock;
use App\Framework\DateTime\Timer;
use App\Framework\DI\Container;
use App\Framework\Queue\FileQueue;
use App\Framework\Queue\Queue;

final class Worker
{
    private int $processedJobs = 0;
    private int $startTime;
    private bool $running = false;
    private readonly Timer $timer;
    private readonly Clock $clock;

    public function __construct(
        private readonly Container     $container,
        private readonly Queue     $queue,
        private readonly CommandBus    $commandBus,
        private readonly ConsoleOutput $output
    )
    {
        $this->timer = $this->container->get(Timer::class);
        $this->clock = $this->container->get(Clock::class);

        $this->startTime = $this->clock->time();
    }

    public function start(): void
    {
        $this->running = true;
        $this->output->writeLine('🚀 Worker gestartet', ConsoleColor::BRIGHT_GREEN);

        while ($this->running) {
            $this->processJob();
        }
    }

    public function stop(): void
    {
        $this->running = false;
        $this->output->writeLine('🛑 Worker gestoppt', ConsoleColor::BRIGHT_RED);
    }

    private function processJob(): void
    {
        $job = $this->queue->pop();

        if ($job) {
            $this->handleJob($job);
        } else {
            $this->idle();
        }
    }

    private array $processedJobHashes = [];

    private function handleJob(object $job): void
    {
        try {
            $this->processedJobs++;
            $jobClass = $job::class;

            // Job-Hash für Duplicate-Detection
            $jobHash = md5(serialize($job));

            if (isset($this->processedJobHashes[$jobHash])) {
                $this->output->writeLine("⚠️  DUPLICATE JOB erkannt #{$this->processedJobs}: " . $jobClass, ConsoleColor::BRIGHT_RED);
                $this->output->writeLine("   Bereits verarbeitet: " . $this->processedJobHashes[$jobHash] . " mal", ConsoleColor::RED);
                $this->processedJobHashes[$jobHash]++;

                // Nach 5 Duplikaten Worker stoppen
                if ($this->processedJobHashes[$jobHash] > 5) {
                    $this->output->writeLine("🛑 Zu viele Duplikate - Worker wird gestoppt", ConsoleColor::BRIGHT_RED);
                    $this->stop();
                    return;
                }
            } else {
                $this->processedJobHashes[$jobHash] = 1;
            }

            $this->output->writeLine("🔄 Verarbeite Job #{$this->processedJobs}: " . $jobClass, ConsoleColor::BRIGHT_YELLOW);

            $this->commandBus->dispatch($job);

            $this->output->writeLine("✅ Job #{$this->processedJobs} erfolgreich verarbeitet", ConsoleColor::BRIGHT_GREEN);
            $this->showStatistics();
        } catch (\Throwable $e) {
            $this->handleError($job, $e);
        }
    }

    private function handleError(object $job, \Throwable $e): void
    {
        $this->output->writeLine("❌ Fehler beim Verarbeiten von Job #{$this->processedJobs}:", ConsoleColor::BRIGHT_RED);
        $this->output->writeLine('   Typ: ' . $job::class, ConsoleColor::RED);
        $this->output->writeLine('   Fehler: ' . $e->getMessage(), ConsoleColor::RED);
        $this->output->writeLine('   Datei: ' . $e->getFile() . ':' . $e->getLine(), ConsoleColor::RED);

        error_log('Worker Error: ' . $e->getMessage());
    }

    private function showStatistics(): void
    {
        $runtime = $this->clock->time() - $this->startTime;
        $avgJobsPerSecond = $runtime > 0 ? round($this->processedJobs / $runtime, 2) : 0;
        $this->output->writeLine("📊 Statistiken: {$this->processedJobs} Jobs | Laufzeit: {$runtime}s | ⌀ {$avgJobsPerSecond} Jobs/s", ConsoleColor::BRIGHT_CYAN);
        $this->output->newLine();
    }

    private function idle(): void
    {
        static $idleCycles = 0;
        $idleCycles++;

        if ($idleCycles % 100 === 0) {
            $runtime = $this->clock->time() - $this->startTime;
            $this->output->writeLine("💓 Heartbeat: {$this->processedJobs} Jobs verarbeitet | Laufzeit: {$runtime}s", ConsoleColor::BRIGHT_MAGENTA);
        }

        # usleep(100_000);
        $this->timer->msleep(100);
    }
}
