<?php

declare(strict_types=1);

namespace App\Framework\View\Dom\Renderer;

use App\Framework\View\Dom\CommentNode;
use App\Framework\View\Dom\DocumentNode;
use App\Framework\View\Dom\ElementNode;
use App\Framework\View\Dom\Node;
use App\Framework\View\Dom\TextNode;

final class HtmlRenderer
{
    private bool $pretty = false;
    private int $indentLevel = 0;
    private string $indentString = '    ';

    public function __construct(bool $pretty = false)
    {
        $this->pretty = $pretty;
    }

    public function render(Node $node): string
    {
        $this->indentLevel = 0;
        return $this->renderNode($node);
    }

    private function renderNode(Node $node): string
    {
        return match ($node::class) {
            DocumentNode::class => $this->renderDocument($node),
            ElementNode::class => $this->renderElement($node),
            TextNode::class => $this->renderText($node),
            CommentNode::class => $this->renderComment($node),
            default => '',
        };
    }

    private function renderDocument(DocumentNode $node): string
    {
        $html = '';

        if ($node->getDoctype() !== null) {
            $html .= $node->getDoctype();
            if ($this->pretty) {
                $html .= "\n";
            }
        }

        foreach ($node->getChildren() as $child) {
            $html .= $this->renderNode($child);
        }

        return $html;
    }

    private function renderElement(ElementNode $node): string
    {
        $html = '';

        if ($this->pretty) {
            $html .= str_repeat($this->indentString, $this->indentLevel);
        }

        $html .= '<' . $node->getTagName();

        // Render attributes
        foreach ($node->getAttributes() as $attribute) {
            $html .= ' ' . $attribute;
        }

        // Self-closing void elements
        if ($node->isVoidElement()) {
            $html .= ' />';

            if ($this->pretty) {
                $html .= "\n";
            }

            return $html;
        }

        $html .= '>';

        // Render children
        if ($node->hasChildren()) {
            if ($this->pretty) {
                $html .= "\n";
                $this->indentLevel++;
            }

            foreach ($node->getChildren() as $child) {
                $html .= $this->renderNode($child);
            }

            if ($this->pretty) {
                $this->indentLevel--;
                $html .= str_repeat($this->indentString, $this->indentLevel);
            }
        }

        // Closing tag
        $html .= '</' . $node->getTagName() . '>';

        if ($this->pretty) {
            $html .= "\n";
        }

        return $html;
    }

    private function renderText(TextNode $node): string
    {
        $text = $node->getTextContent();

        if ($this->pretty) {
            $text = trim($text);

            if ($text === '') {
                return '';
            }

            return str_repeat($this->indentString, $this->indentLevel) . $text . "\n";
        }

        return $text;
    }

    private function renderComment(CommentNode $node): string
    {
        $html = '';

        if ($this->pretty) {
            $html .= str_repeat($this->indentString, $this->indentLevel);
        }

        $html .= '<!--' . $node->getContent() . '-->';

        if ($this->pretty) {
            $html .= "\n";
        }

        return $html;
    }
}
