<?php

declare(strict_types=1);

namespace App\Framework\Console;

class Spinner
{
    private ConsoleOutputInterface $output;

    private string $message;

    private array $frames;

    private int $currentFrame = 0;

    private float $startTime;

    private bool $active = false;

    private float $interval;

    private int $updateCount = 0;

    public function __construct(
        ConsoleOutputInterface $output,
        string $message = 'Loading...',
        SpinnerStyle $style = SpinnerStyle::DOTS,
        float $interval = 0.1
    ) {
        $this->output = $output;
        $this->message = $message;
        $this->frames = $style->getFrames();
        $this->interval = $interval;
    }

    /**
     * Startet den Spinner
     */
    public function start(): self
    {
        $this->startTime = microtime(true);
        $this->active = true;
        $this->update();

        return $this;
    }

    /**
     * Stoppt den Spinner mit einer Erfolgs-, Fehler- oder neutralen Nachricht
     */
    public function stop(?string $message = null, ?ConsoleColor $color = null): self
    {
        $this->active = false;

        // Lösche die aktuelle Zeile
        $this->output->write("\r\033[2K");

        if ($message !== null) {
            $this->output->writeLine($message, $color);
        }

        return $this;
    }

    /**
     * Stoppt den Spinner mit einer Erfolgsmeldung
     */
    public function success(string $message): self
    {
        return $this->stop("✓ " . $message, ConsoleColor::BRIGHT_GREEN);
    }

    /**
     * Stoppt den Spinner mit einer Fehlermeldung
     */
    public function error(string $message): self
    {
        return $this->stop("✗ " . $message, ConsoleColor::BRIGHT_RED);
    }

    /**
     * Ändert die Nachricht des Spinners während er läuft
     */
    public function setMessage(string $message): self
    {
        $this->message = $message;
        if ($this->active) {
            $this->update();
        }

        return $this;
    }

    /**
     * Aktualisiert die Anzeige des Spinners
     */
    public function update(): self
    {
        if (! $this->active) {
            return $this;
        }

        $this->updateCount++;

        // Aktualisiere nur in festgelegten Intervallen
        $elapsed = microtime(true) - $this->startTime;
        $expectedUpdates = floor($elapsed / $this->interval);

        if ($this->updateCount < $expectedUpdates) {
            $this->currentFrame = ($this->currentFrame + 1) % count($this->frames);

            // Lösche die aktuelle Zeile und schreibe den aktualisierten Spinner
            $frame = $this->frames[$this->currentFrame];
            $this->output->write("\r\033[2K{$frame} {$this->message}");

            $this->updateCount = $expectedUpdates;
        }

        return $this;
    }

    /**
     * Beendet den Spinner ohne Ausgabe
     */
    public function clear(): self
    {
        return $this->stop();
    }
}
