<?php

declare(strict_types=1);

namespace App\Framework\View\Dom\Transformer;

use App\Framework\Template\Processing\AstTransformer;
use App\Framework\View\Dom\DocumentNode;
use App\Framework\View\Dom\ElementNode;
use App\Framework\View\Dom\Node;
use App\Framework\View\RenderContext;
use App\Framework\Vite\ViteService;

/**
 * AssetInjectorTransformer - Injects Vite assets into HTML head
 *
 * Automatically detects development vs production mode and injects
 * appropriate script and stylesheet tags using ViteService.
 *
 * Framework Pattern: readonly class, AST-based transformation
 */
final readonly class AssetInjectorTransformer implements AstTransformer
{
    public function __construct(
        private ViteService $viteService
    ) {}

    public function transform(DocumentNode $document, RenderContext $context): DocumentNode
    {
        // Skip if this is a partial render (e.g. component)
        if ($context->isPartial ?? false) {
            return $document;
        }

        // Find head element
        $head = $this->findHeadElement($document);

        if (!$head) {
            return $document;
        }

        // Generate Vite tags (dev mode with HMR or production from manifest)
        $viteTags = $this->viteService->getTags();

        // Parse and inject tags
        $this->injectViteTags($head, $viteTags);

        return $document;
    }

    /**
     * Find the head element in the document
     */
    private function findHeadElement(DocumentNode $document): ?ElementNode
    {
        foreach ($document->getChildren() as $child) {
            if ($child instanceof ElementNode && strtolower($child->getTagName()) === 'html') {
                // Found html element, look for head inside
                foreach ($child->getChildren() as $htmlChild) {
                    if ($htmlChild instanceof ElementNode && strtolower($htmlChild->getTagName()) === 'head') {
                        return $htmlChild;
                    }
                }
            } elseif ($child instanceof ElementNode && strtolower($child->getTagName()) === 'head') {
                // Direct head element
                return $child;
            }
        }

        return null;
    }

    /**
     * Parse Vite tags HTML and inject into head
     */
    private function injectViteTags(ElementNode $head, string $viteTags): void
    {
        if (empty($viteTags)) {
            return;
        }

        // Parse each line and inject appropriately
        $lines = explode("\n", $viteTags);

        foreach ($lines as $line) {
            $line = trim($line);

            if (empty($line) || str_starts_with($line, '<!--')) {
                continue;
            }

            // Detect tag type and inject
            if (str_contains($line, '<link') && str_contains($line, 'stylesheet')) {
                $this->injectStylesheet($head, $line);
            } elseif (str_contains($line, '<script')) {
                $this->injectScript($head, $line);
            } elseif (str_contains($line, '<link') && str_contains($line, 'preload')) {
                $this->injectPreload($head, $line);
            }
        }
    }

    /**
     * Extract href from stylesheet tag and inject
     */
    private function injectStylesheet(ElementNode $head, string $tag): void
    {
        if (preg_match('/href="([^"]+)"/', $tag, $matches)) {
            $link = new ElementNode('link');
            $link->setAttribute('rel', 'stylesheet');
            $link->setAttribute('href', $matches[1]);

            $head->appendChild($link);
        }
    }

    /**
     * Extract src from script tag and inject
     */
    private function injectScript(ElementNode $head, string $tag): void
    {
        if (preg_match('/src="([^"]+)"/', $tag, $matches)) {
            $script = new ElementNode('script');
            $script->setAttribute('src', $matches[1]);

            // Extract type="module"
            if (str_contains($tag, 'type="module"')) {
                $script->setAttribute('type', 'module');
            }

            // Extract nonce
            if (preg_match('/nonce="([^"]+)"/', $tag, $nonceMatches)) {
                $script->setAttribute('nonce', $nonceMatches[1]);
            }

            // Extract integrity
            if (preg_match('/integrity="([^"]+)"/', $tag, $integrityMatches)) {
                $script->setAttribute('integrity', $integrityMatches[1]);
            }

            $head->appendChild($script);
        }
    }

    /**
     * Extract href from preload tag and inject
     */
    private function injectPreload(ElementNode $head, string $tag): void
    {
        if (preg_match('/href="([^"]+)"/', $tag, $matches)) {
            $link = new ElementNode('link');
            $link->setAttribute('rel', 'preload');
            $link->setAttribute('href', $matches[1]);

            // Extract 'as' attribute
            if (preg_match('/as="([^"]+)"/', $tag, $asMatches)) {
                $link->setAttribute('as', $asMatches[1]);
            } else {
                $link->setAttribute('as', 'script');
            }

            $head->appendChild($link);
        }
    }
}
