<?php

declare(strict_types=1);

namespace App\Framework\Console\Components;

use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleFormat;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ConsoleStyle;

/**
 * TreeHelper zum Anzeigen hierarchischer Baumstrukturen in der Konsole.
 * Ähnlich dem Symfony TreeHelper, aber angepasst an unser Styling-System.
 */
final class TreeHelper
{
    private string $prefix = '';

    private bool $isLastElement = true;

    private ?ConsoleStyle $nodeStyle = null;

    private ?ConsoleStyle $leafStyle = null;

    private ?ConsoleStyle $lineStyle = null;

    /**
     * @var array<array{title: string, node: ?self, isLeaf: bool}>
     */
    private array $nodes = [];

    public function __construct(
        private string $title = '',
        private ConsoleOutput $output = new ConsoleOutput(),
    ) {
        $this->nodeStyle = ConsoleStyle::create(color: ConsoleColor::BRIGHT_YELLOW, format: ConsoleFormat::BOLD);
        $this->leafStyle = ConsoleStyle::create(color: ConsoleColor::WHITE);
        $this->lineStyle = ConsoleStyle::create(color: ConsoleColor::GRAY);
    }

    /**
     * Setzt den Stil für Knotentitel (Verzeichnisse/Kategorien).
     */
    public function setNodeStyle(?ConsoleStyle $style): self
    {
        $this->nodeStyle = $style;

        return $this;
    }

    /**
     * Setzt den Stil für Blätter/Endpunkte.
     */
    public function setLeafStyle(?ConsoleStyle $style): self
    {
        $this->leafStyle = $style;

        return $this;
    }

    /**
     * Setzt den Stil für Baumlinien.
     */
    public function setLineStyle(?ConsoleStyle $style): self
    {
        $this->lineStyle = $style;

        return $this;
    }

    /**
     * Setzt den Haupttitel des Baums.
     */
    public function setTitle(string $title): self
    {
        $this->title = $title;

        return $this;
    }

    /**
     * Fügt einen Unterknoten (z.B. Unterverzeichnis) hinzu.
     */
    public function addNode(string $title): self
    {
        $node = new self($title);
        $node->nodeStyle = $this->nodeStyle;
        $node->leafStyle = $this->leafStyle;
        $node->lineStyle = $this->lineStyle;

        $this->nodes[] = [
            'title' => $title,
            'node' => $node,
            'isLeaf' => false,
        ];

        return $node;
    }

    /**
     * Fügt einen Endpunkt (z.B. Datei) hinzu.
     */
    public function addLeaf(string $title): self
    {
        $this->nodes[] = [
            'title' => $title,
            'node' => null,
            'isLeaf' => true,
        ];

        return $this;
    }

    /**
     * Zeigt die vollständige Baumstruktur an.
     */
    public function display(): void
    {
        if (! empty($this->title)) {
            $this->output->writeLine($this->title, $this->nodeStyle);
        }

        $this->displayTree();
    }

    /**
     * Rendert die Baumstruktur und gibt den Text zurück.
     */
    public function render(): string
    {
        $output = '';

        if (! empty($this->title)) {
            $output .= $this->nodeStyle->apply($this->title) . "\n";
        }

        $output .= $this->renderTree();

        return $output;
    }

    /**
     * Setzt den Präfix für die aktuelle Ebene.
     * (Interne Methode für rekursives Rendern)
     */
    private function setPrefix(string $prefix, bool $isLastElement): self
    {
        $this->prefix = $prefix;
        $this->isLastElement = $isLastElement;

        return $this;
    }

    /**
     * Zeigt die Baumstruktur mit dem aktuellen Präfix an.
     * (Interne Methode für rekursives Rendern)
     */
    private function displayTree(): void
    {
        $count = count($this->nodes);

        foreach ($this->nodes as $index => $item) {
            $isLast = ($index === $count - 1);
            $nodePrefix = $this->prefix . ($this->isLastElement ? '    ' : '│   ');

            // Baumlinien und Verbindungen
            $connector = $isLast ? '└── ' : '├── ';
            $linePrefix = $this->prefix . $connector;

            // Titel anzeigen
            $style = $item['isLeaf'] ? $this->leafStyle : $this->nodeStyle;
            $title = $linePrefix . $item['title'];

            $this->output->writeLine(
                $this->lineStyle->apply($linePrefix) .
                $style->apply($item['title'])
            );

            // Unterelemente rekursiv anzeigen
            if (! $item['isLeaf'] && $item['node'] !== null) {
                $item['node']
                    ->setPrefix($nodePrefix, $isLast)
                    ->displayTree();
            }
        }
    }

    /**
     * Rendert die Baumstruktur mit dem aktuellen Präfix und gibt den Text zurück.
     * (Interne Methode für rekursives Rendern)
     */
    private function renderTree(): string
    {
        $output = '';
        $count = count($this->nodes);

        foreach ($this->nodes as $index => $item) {
            $isLast = ($index === $count - 1);
            $nodePrefix = $this->prefix . ($this->isLastElement ? '    ' : '│   ');

            // Baumlinien und Verbindungen
            $connector = $isLast ? '└── ' : '├── ';
            $linePrefix = $this->prefix . $connector;

            // Titel formatieren
            $style = $item['isLeaf'] ? $this->leafStyle : $this->nodeStyle;
            $title = $item['title'];

            $output .= $this->lineStyle->apply($linePrefix) .
                      $style->apply($title) . "\n";

            // Unterelemente rekursiv rendern
            if (! $item['isLeaf'] && $item['node'] !== null) {
                $childOutput = $item['node']
                    ->setPrefix($nodePrefix, $isLast)
                    ->renderTree();

                $output .= $childOutput;
            }
        }

        return $output;
    }
}
