<?php

declare(strict_types=1);

namespace App\Framework\View\Components;

use App\Framework\View\ValueObjects\HtmlAttributes;
use App\Framework\View\ValueObjects\HtmlElement;
use App\Framework\View\ValueObjects\HtmlTag;
use App\Framework\View\ValueObjects\StandardHtmlElement;
use App\Framework\View\ValueObjects\TagName;

final readonly class Picture implements HtmlElement
{
    public HtmlTag $tag;

    public HtmlAttributes $attributes;

    public string $content;

    /**
     * @param array<PictureSource> $sources
     */
    public function __construct(
        public string $src,
        public string $alt,
        public array $sources = [],
        public ?int $width = null,
        public ?int $height = null,
        public string $loading = 'lazy',
        public HtmlAttributes $additionalAttributes = new HtmlAttributes()
    ) {
        $this->tag = new HtmlTag(TagName::PICTURE);
        $this->attributes = HtmlAttributes::empty();

        // Merge additional attributes
        foreach ($this->additionalAttributes->attributes as $name => $value) {
            $this->attributes = $this->attributes->with($name, $value);
        }

        $this->content = $this->buildContent();
    }

    private function buildContent(): string
    {
        $elements = [];

        // Add all source elements
        foreach ($this->sources as $source) {
            $elements[] = (string) $source;
        }

        // Add fallback img element
        $imgAttrs = HtmlAttributes::empty()
            ->with('src', $this->src)
            ->with('alt', $this->alt)
            ->with('loading', $this->loading);

        if ($this->width !== null) {
            $imgAttrs = $imgAttrs->with('width', (string) $this->width);
        }

        if ($this->height !== null) {
            $imgAttrs = $imgAttrs->with('height', (string) $this->height);
        }

        $elements[] = StandardHtmlElement::create(TagName::IMG, $imgAttrs);

        return implode('', array_map('strval', $elements));
    }

    public static function create(string $src, string $alt): self
    {
        return new self(src: $src, alt: $alt);
    }

    public static function withWebP(
        string $webpSrc,
        string $fallbackSrc,
        string $alt
    ): self {
        return new self(
            src: $fallbackSrc,
            alt: $alt,
            sources: [
                PictureSource::webp($webpSrc),
            ]
        );
    }

    public static function withAvif(
        string $avifSrc,
        string $webpSrc,
        string $fallbackSrc,
        string $alt
    ): self {
        return new self(
            src: $fallbackSrc,
            alt: $alt,
            sources: [
                PictureSource::avif($avifSrc),
                PictureSource::webp($webpSrc),
            ]
        );
    }

    public function withSource(PictureSource $source): self
    {
        return new self(
            src: $this->src,
            alt: $this->alt,
            sources: [...$this->sources, $source],
            width: $this->width,
            height: $this->height,
            loading: $this->loading,
            additionalAttributes: $this->additionalAttributes
        );
    }

    public function withDimensions(int $width, int $height): self
    {
        return new self(
            src: $this->src,
            alt: $this->alt,
            sources: $this->sources,
            width: $width,
            height: $height,
            loading: $this->loading,
            additionalAttributes: $this->additionalAttributes
        );
    }

    public function eager(): self
    {
        return new self(
            src: $this->src,
            alt: $this->alt,
            sources: $this->sources,
            width: $this->width,
            height: $this->height,
            loading: 'eager',
            additionalAttributes: $this->additionalAttributes
        );
    }

    public function __toString(): string
    {
        return (string) StandardHtmlElement::create(
            $this->tag->name,
            $this->attributes,
            $this->content
        );
    }
}
