<?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 FormInput implements HtmlElement
{
    public HtmlTag $tag;

    public HtmlAttributes $attributes;

    public string $content;

    public function __construct(
        public string $name,
        public string $type = 'text',
        public ?string $value = null,
        public ?string $label = null,
        public ?string $id = null,
        public ?string $placeholder = null,
        public bool $required = false,
        public bool $disabled = false,
        public bool $readonly = false,
        public ?string $pattern = null,
        public ?int $minLength = null,
        public ?int $maxLength = null,
        public ?string $errorMessage = null,
        public HtmlAttributes $additionalAttributes = new HtmlAttributes()
    ) {
        $this->tag = new HtmlTag(TagName::DIV);
        $this->attributes = HtmlAttributes::empty()->withClass('form-group');
        $this->content = $this->buildContent();
    }

    public static function text(
        string $name,
        ?string $label = null,
        ?string $value = null
    ): self {
        return new self(name: $name, type: 'text', label: $label, value: $value);
    }

    public static function email(
        string $name,
        ?string $label = null,
        ?string $value = null
    ): self {
        return new self(name: $name, type: 'email', label: $label, value: $value);
    }

    public static function password(
        string $name,
        ?string $label = null
    ): self {
        return new self(name: $name, type: 'password', label: $label);
    }

    public static function number(
        string $name,
        ?string $label = null,
        ?string $value = null
    ): self {
        return new self(name: $name, type: 'number', label: $label, value: $value);
    }

    public static function tel(
        string $name,
        ?string $label = null,
        ?string $value = null
    ): self {
        return new self(name: $name, type: 'tel', label: $label, value: $value);
    }

    public static function url(
        string $name,
        ?string $label = null,
        ?string $value = null
    ): self {
        return new self(name: $name, type: 'url', label: $label, value: $value);
    }

    public static function date(
        string $name,
        ?string $label = null,
        ?string $value = null
    ): self {
        return new self(name: $name, type: 'date', label: $label, value: $value);
    }

    public static function search(
        string $name,
        ?string $label = null,
        ?string $placeholder = null
    ): self {
        return new self(name: $name, type: 'search', label: $label, placeholder: $placeholder);
    }

    public function withRequired(bool $required = true): self
    {
        return new self(
            name: $this->name,
            type: $this->type,
            value: $this->value,
            label: $this->label,
            id: $this->id,
            placeholder: $this->placeholder,
            required: $required,
            disabled: $this->disabled,
            readonly: $this->readonly,
            pattern: $this->pattern,
            minLength: $this->minLength,
            maxLength: $this->maxLength,
            errorMessage: $this->errorMessage,
            additionalAttributes: $this->additionalAttributes
        );
    }

    public function withPattern(string $pattern): self
    {
        return new self(
            name: $this->name,
            type: $this->type,
            value: $this->value,
            label: $this->label,
            id: $this->id,
            placeholder: $this->placeholder,
            required: $this->required,
            disabled: $this->disabled,
            readonly: $this->readonly,
            pattern: $pattern,
            minLength: $this->minLength,
            maxLength: $this->maxLength,
            errorMessage: $this->errorMessage,
            additionalAttributes: $this->additionalAttributes
        );
    }

    public function withError(string $errorMessage): self
    {
        return new self(
            name: $this->name,
            type: $this->type,
            value: $this->value,
            label: $this->label,
            id: $this->id,
            placeholder: $this->placeholder,
            required: $this->required,
            disabled: $this->disabled,
            readonly: $this->readonly,
            pattern: $this->pattern,
            minLength: $this->minLength,
            maxLength: $this->maxLength,
            errorMessage: $errorMessage,
            additionalAttributes: $this->additionalAttributes
        );
    }

    private function buildContent(): string
    {
        $inputId = $this->id ?? "input-{$this->name}";

        $attributes = HtmlAttributes::empty()
            ->withType($this->type)
            ->withName($this->name)
            ->withId($inputId)
            ->withClass('form-input');

        if ($this->value !== null) {
            $attributes = $attributes->withValue($this->value);
        }

        if ($this->placeholder !== null) {
            $attributes = $attributes->with('placeholder', $this->placeholder);
        }

        if ($this->required) {
            $attributes = $attributes->withRequired();
        }

        if ($this->disabled) {
            $attributes = $attributes->withDisabled();
        }

        if ($this->readonly) {
            $attributes = $attributes->withReadonly();
        }

        if ($this->pattern !== null) {
            $attributes = $attributes->with('pattern', $this->pattern);
        }

        if ($this->minLength !== null) {
            $attributes = $attributes->with('minlength', (string) $this->minLength);
        }

        if ($this->maxLength !== null) {
            $attributes = $attributes->with('maxlength', (string) $this->maxLength);
        }

        if ($this->errorMessage !== null) {
            $attributes = $attributes
                ->withClass('form-input--error')
                ->with('aria-invalid', 'true')
                ->with('aria-describedby', "{$inputId}-error");
        }

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

        $elements = [];

        // Label
        if ($this->label !== null) {
            $labelAttrs = HtmlAttributes::empty()
                ->withClass('form-label')
                ->with('for', $inputId);

            if ($this->required) {
                $labelAttrs = $labelAttrs->withClass('form-label--required');
            }

            $elements[] = StandardHtmlElement::create(
                TagName::LABEL,
                $labelAttrs,
                $this->label
            );
        }

        // Input
        $elements[] = StandardHtmlElement::create(TagName::INPUT, $attributes);

        // Error message
        if ($this->errorMessage !== null) {
            $elements[] = StandardHtmlElement::create(
                TagName::SPAN,
                HtmlAttributes::empty()
                    ->withClass('form-error')
                    ->withId("{$inputId}-error")
                    ->with('role', 'alert'),
                $this->errorMessage
            );
        }

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

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