<?php

declare(strict_types=1);

namespace App\Framework\View\Components;

use App\Framework\Core\ValueObjects\AccessibleLink;
use App\Framework\Core\ValueObjects\HtmlLink;
use App\Framework\Core\ValueObjects\LinkRel;
use App\Framework\Core\ValueObjects\LinkTarget;
use App\Framework\View\Attributes\ComponentName;
use App\Framework\View\Contracts\StaticComponent;
use App\Framework\View\Dom\ElementNode;
use App\Framework\View\Dom\Node;
use App\Framework\View\Dom\TextNode;

/**
 * <x-a> Component
 *
 * Wrapper around HtmlLink and AccessibleLink Value Objects for template usage.
 * Provides consistent, accessible link rendering throughout the application.
 *
 * Usage in templates:
 * <x-a href="/dashboard">Dashboard</x-a>
 * <x-a href="https://example.com" external="true">External Link</x-a>
 * <x-a href="/current" aria-current="true">Current Page</x-a>
 * <x-a href="/downloads/file.pdf" download="true">Download PDF</x-a>
 * <x-a href="/page" class="custom-link" title="Tooltip">Link with custom class</x-a>
 */
#[ComponentName('a')]
final readonly class A implements StaticComponent
{
    private AccessibleLink $link;
    private string $text;

    public function __construct(
        string $content = '',
        array $attributes = []
    ) {
        // Content is the link text
        $this->text = $content;

        // Extract href from attributes (default to '#' if not provided)
        $href = $attributes['href'] ?? '#';

        // Extract other link attributes
        $title = $attributes['title'] ?? null;
        $class = $attributes['class'] ?? null;
        $target = $attributes['target'] ?? null;
        $rel = $attributes['rel'] ?? null;
        $external = isset($attributes['external']);
        $download = $attributes['download'] ?? null;

        // Extract ARIA attributes
        $ariaLabel = $attributes['aria-label'] ?? null;
        $ariaCurrent = $attributes['aria-current'] ?? null;
        $ariaDescribedBy = $attributes['aria-described-by'] ?? null;

        // Build HtmlLink
        $htmlLink = HtmlLink::create($href, $this->text);

        if ($title !== null) {
            $htmlLink = $htmlLink->withTitle($title);
        }

        if ($class !== null) {
            $htmlLink = $htmlLink->withClass($class);
        }

        // Handle target attribute
        if ($target !== null) {
            $linkTarget = match ($target) {
                '_blank' => LinkTarget::BLANK,
                '_self' => LinkTarget::SELF,
                '_parent' => LinkTarget::PARENT,
                '_top' => LinkTarget::TOP,
                default => null
            };
            if ($linkTarget !== null) {
                $htmlLink = $htmlLink->withTarget($linkTarget);
            }
        }

        // Handle rel attribute
        if ($rel !== null) {
            $relValues = array_map(
                fn(string $r) => match (trim($r)) {
                    'noopener' => LinkRel::NOOPENER,
                    'noreferrer' => LinkRel::NOREFERRER,
                    'nofollow' => LinkRel::NOFOLLOW,
                    'external' => LinkRel::EXTERNAL,
                    default => null
                },
                explode(' ', $rel)
            );
            $relValues = array_filter($relValues);
            if (!empty($relValues)) {
                $htmlLink = $htmlLink->withRel(...$relValues);
            }
        }

        // Handle external links (auto-adds security attributes)
        if ($external) {
            $htmlLink = HtmlLink::external($href, $this->text);
            if ($class !== null) {
                $htmlLink = $htmlLink->withClass($class);
            }
            if ($title !== null) {
                $htmlLink = $htmlLink->withTitle($title);
            }
        }

        // Handle downloads
        if ($download !== null) {
            $htmlLink = HtmlLink::download($href, $this->text, $download !== '' ? $download : null);
            if ($class !== null) {
                $htmlLink = $htmlLink->withClass($class);
            }
            if ($title !== null) {
                $htmlLink = $htmlLink->withTitle($title);
            }
        }

        // Wrap in AccessibleLink for ARIA support
        $link = AccessibleLink::fromHtmlLink($htmlLink);

        if ($ariaLabel !== null) {
            $link = $link->withAriaLabel($ariaLabel);
        }

        if ($ariaCurrent !== null) {
            $link = $link->withAriaCurrent(true, $ariaCurrent);
        }

        if ($ariaDescribedBy !== null) {
            $link = new AccessibleLink(
                baseLink: $link->baseLink,
                ariaLabel: $link->ariaLabel,
                ariaDescribedBy: $ariaDescribedBy,
                ariaCurrent: $link->ariaCurrent,
                ariaCurrentValue: $link->ariaCurrentValue,
            );
        }

        // Store the AccessibleLink
        $this->link = $link;
    }

    public function getRootNode(): Node
    {
        $a = new ElementNode('a');

        // Add href
        $a->setAttribute('href', (string) $this->link->baseLink->href);

        // Add class if present
        if ($this->link->baseLink->cssClass !== null) {
            $a->setAttribute('class', $this->link->baseLink->cssClass);
        }

        // Add title if present
        if ($this->link->baseLink->title !== null) {
            $a->setAttribute('title', $this->link->baseLink->title);
        }

        // Add target if present
        if ($this->link->baseLink->target !== null) {
            $a->setAttribute('target', $this->link->baseLink->target->value);
        }

        // Add rel if present
        if (!empty($this->link->baseLink->rel)) {
            $relValues = array_map(fn($r) => $r->value, $this->link->baseLink->rel);
            $a->setAttribute('rel', implode(' ', $relValues));
        }

        // Add download if present
        if ($this->link->baseLink->download !== null) {
            $a->setAttribute('download', $this->link->baseLink->download);
        }

        // Add ARIA attributes
        if ($this->link->ariaLabel !== null) {
            $a->setAttribute('aria-label', $this->link->ariaLabel);
        }

        if ($this->link->ariaCurrent) {
            $a->setAttribute('aria-current', $this->link->ariaCurrentValue ?? 'true');
        }

        if ($this->link->ariaDescribedBy !== null) {
            $a->setAttribute('aria-describedby', $this->link->ariaDescribedBy);
        }

        // Add text content
        $textNode = new TextNode($this->text);
        $a->appendChild($textNode);

        return $a;
    }
}
