"""
Form components for data input and validation.
"""
from .base import Component
from typing import List, Optional, Callable, Union
from django.middleware.csrf import get_token
import logging
from django.db import models
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
from phonenumber_field.phonenumber import PhoneNumber
from django.utils import timezone
from datetime import datetime, date, time
import json
import html
from .container import Column


logger = logging.getLogger(__name__)


class FormErrors(Component):
    """
    Renders form messages from kwargs: errors (red), success (green), info (blue).
    - errors: dict {"field1": "message", ...}
    - success: str message
    - info: str message
    """

    def _render_alert(self, alert_type: str, content: str) -> str:
        return f"""
        <div class="alert alert-{alert_type} my-2 {self.classes}">
            <div class="flex-1">{content}</div>
        </div>
        """

    def render_html(self, **kwargs) -> str:
        html = ""

        errors = kwargs.get("errors", {})
        if errors:
            error_items = "".join(
                f'<div><span class="font-semibold">{field}:</span> {message}</div>'
                for field, message in errors.items()
            )
            html += self._render_alert("error", error_items)

        success = kwargs.get("success")
        if success:
            html += self._render_alert(
                "success", f'<span class="font-semibold">Success:</span> {success}'
            )

        info = kwargs.get("info")
        if info:
            html += self._render_alert(
                "info", f'<span class="font-semibold">Info:</span> {info}'
            )

        return html


class Form(Component):
    """
    A flexible form component that supports:
    - Create forms (POST)
    - Update forms (POST with object prefill)
    - Filter forms (GET)

    Parameters:
        action: URL to submit to (can be a string or callable that takes object)
        target: HTMX target selector
        children: List of input components
        method: HTTP method - "post" (create/update) or "get" (filter)
        key: Key to get object from kwargs for prefilling (for edit forms)
        swap: HTMX swap mode (default "outerHTML")
        push_url: Whether to push URL on submit (default True for GET, False for POST)
        x_data: Alpine.js x-data object for reactive form state (e.g., "{isDirectory: false}")
    """

    def __init__(
        self,
        target: str,
        method: str = "post",
        swap: str = "outerHTML",
        push_url: Optional[bool] = None,
        title: Optional[str] = None,
        subtitle: Optional[str] = None,
        encoding: str = "multipart/form-data",
        x_data: Optional[str] = None,
        alpine_event: Optional[str] = None,
        hx_trigger: Optional[str] = None,
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.target = target
        self.method = method
        self.swap = swap
        self.push_url = push_url if push_url is not None else method.lower() == "get"
        self.title = title
        self.subtitle = subtitle
        self.encoding = encoding
        self.x_data = x_data
        self.alpine_event = alpine_event
        self.hx_trigger = hx_trigger

    def _get_title(self, obj=None) -> Optional[str]:
        if callable(self.title):
            return self.title(obj)
        return self.title

    def _get_subtitle(self, obj=None) -> Optional[str]:
        if callable(self.subtitle):
            return self.subtitle(obj)
        return self.subtitle

    def _get_all_inputs(self, children: List[Component]) -> List[Component]:
        """Recursively find all input components with a key attribute."""
        inputs = []
        for child in children:
            if isinstance(child, Input):
                inputs.append(child)
            inputs.extend(self._get_all_inputs(child.children))
        return inputs

    def _get_object_values(self, obj) -> dict:
        """Extract field values from an object for prefilling."""
        values = {}
        for child in self._get_all_inputs(self.children):
            values[child.key] = child.get_value(**{"object": obj})
        return values

    def render_html(self, **kwargs) -> str:
        request = kwargs.get("request")
        value = self.get_value(**kwargs)
        action = self.get_url(**kwargs)

        # Get values for prefilling
        child_kwargs = {**kwargs, "object": value}

        # CSRF token for POST/PUT/PATCH
        csrf_input = ""
        if self.method.lower() in ("post", "put", "patch", "delete") and request:
            csrf_token = get_token(request)
            csrf_input = (
                f'<input type="hidden" name="csrfmiddlewaretoken" value="{csrf_token}">'
            )

        children_html = Column(children=self.children, uid=f"{self.uid}_fields").render(
            **child_kwargs
        )

        errors_html = FormErrors(uid=f"{self.uid}_errors").render(**kwargs)

        x_data_attr = f'x-data="{self.x_data}"' if self.x_data else ""

        all_inputs = self._get_all_inputs(self.children)
        input_ids = [(input_comp.uid or input_comp.key) for input_comp in all_inputs]
        input_ids_json = "[" + ",".join([f"'{uid}'" for uid in input_ids]) + "]"

        onreset_js = f"setTimeout(() => {{ const inputIds = {input_ids_json}; inputIds.forEach(id => {{ const displayEl = document.getElementById(id + '_display'); if (displayEl) displayEl.dispatchEvent(new Event('form-reset')); const itemsEl = document.getElementById(id + '_items'); if (itemsEl) itemsEl.dispatchEvent(new Event('form-reset')); }}); }}, 10)"

        onsubmit_js = ""
        if self.alpine_event:
            push_url_js = ""
            if self.push_url:
                push_url_js = "const u = new window.URL(window.location.href); u.search = ''; new window.FormData($el).forEach((v, k) => { if(v) u.searchParams.append(k, v); }); window.history.pushState({}, '', u);"

            # Prevent default submit and dispatch the custom Alpine event, passing the form event
            onsubmit_js = f"@submit.prevent=\"{push_url_js} $dispatch('{self.alpine_event}', {{ form: $el, event: $event }});\""

        hx_attributes = ""
        if not self.alpine_event:
            hx_trigger_attr = (
                f'hx-trigger="{self.hx_trigger}"' if self.hx_trigger else ""
            )
            hx_attributes = f"""
                {"hx-" + self.method + '="' + action + '"' if action is not None else ""}
                hx-target="{self.target}"
                hx-swap="{self.swap}"
                hx-push-url="{str(self.push_url).lower()}"
                {hx_trigger_attr}
                {f'enctype="{self.encoding}" hx-encoding="{self.encoding}"' if self.encoding else ""}
            """

        title = self._get_title(value)
        subtitle = self._get_subtitle(value)

        return f"""
        {f"<div class='text-xl font-semibold text-primary'>{title}</div>" if title else ""}
        {f"<div class='text-md text-gray-500 mb-2'>{subtitle}</div>" if subtitle else ""}
        <form id="{self.uid}" class="flex flex-col {self.classes}"
              {hx_attributes}
              {x_data_attr}
              {onsubmit_js}
              onreset="{onreset_js}">
            {csrf_input}
            {errors_html}
            {children_html}
        </form>
        """


class Input(Component):
    """Base class for all form input components."""

    def __init__(
        self,
        label: str = "",
        required: bool = False,
        placeholder: Optional[str] = None,
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.label = label or self.key.replace("_", " ").title()
        self.required = required
        self.placeholder = placeholder

    def clean(self, value):
        """Clean and validate the input value. Override in subclasses to perform specific sanitization."""
        return value


class TextInput(Input):
    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        value = "" if value is None else value
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        error_class = "input-error" if error else ""
        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <input type="text"
                   id="{self.uid or self.key}"
                   name="{self.key}"
                   value="{{% verbatim %}}{value}{{% endverbatim %}}"
                   placeholder="{self.placeholder}"
                   class="input input-bordered w-full {error_class}"
                   {required_attr} />
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""

    def clean(self, value):
        if not value:
            return value
        if not isinstance(value, str):
            value = str(value)
        return value.strip()


class TernaryInput(Input):
    def __init__(
        self,
        true_label: str = "Yes",
        false_label: str = "No",
        none_label: str = "Not Set",
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.true_label = true_label
        self.false_label = false_label
        self.none_label = none_label

    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        true_selected = "selected" if value is True else ""
        false_selected = "selected" if value is False else ""
        none_selected = "selected" if value is None else ""

        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <select id="{self.uid or self.key}"
                    name="{self.key}"
                    class="select select-bordered w-full">
                <option value="" {none_selected}>{self.none_label}</option>
                <option value="True" {true_selected}>{self.true_label}</option>
                <option value="False" {false_selected}>{self.false_label}</option>
            </select>
        </div>"""

    def clean(self, value):
        if value in ("True", "true", "1", True):
            return True
        elif value in ("False", "false", "0", False):
            return False
        return None


class SubmitInput(Component):
    """Submit button - not an Input since it doesn't have a key."""

    def __init__(self, label: str = "Submit", **kwargs):
        super().__init__(**kwargs)
        self.label = label

    def render_html(self, **kwargs) -> str:
        return f"""
        <button type="submit" class="btn btn-primary my-2 {self.classes}">{self.label}</button>
        """


class DateTimeInput(Input):
    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        if value and hasattr(value, "strftime"):
            value = timezone.localtime(value)
            value = value.strftime("%Y-%m-%dT%H:%M")
        elif value is None:
            value = ""
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        error_class = "input-error" if error else ""
        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <input type="datetime-local"
                   id="{self.uid or self.key}"
                   name="{self.key}"
                   value="{{% verbatim %}}{value}{{% endverbatim %}}"
                   class="input input-bordered w-full {error_class}"
                   {required_attr} />
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""

    def clean(self, value):
        if not value:
            return None
        if hasattr(value, "strftime"):
            return value
        try:
            dt = datetime.strptime(value, "%Y-%m-%dT%H:%M")
            return timezone.make_aware(dt)
        except ValueError:
            raise ValueError("Invalid datetime format. Expected YYYY-MM-DDTHH:MM.")


class DateInput(Input):
    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        if value and hasattr(value, "strftime"):
            value = value.strftime("%Y-%m-%d")
        elif value is None:
            value = ""
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        error_class = "input-error" if error else ""
        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <input type="date"
                   id="{self.uid or self.key}"
                   name="{self.key}"
                   value="{{% verbatim %}}{value}{{% endverbatim %}}"
                   class="input input-bordered w-full {error_class}"
                   {required_attr} />
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""

    def clean(self, value):
        if not value:
            return None
        if hasattr(value, "strftime"):
            return value

        try:
            return date.fromisoformat(value)
        except ValueError:
            raise ValueError("Invalid date format. Expected YYYY-MM-DD.")


class TimeInput(Input):
    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        if value and hasattr(value, "strftime"):
            value = value.strftime("%H:%M")
        elif value is None:
            value = ""
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        error_class = "input-error" if error else ""
        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <input type="time"
                   id="{self.uid or self.key}"
                   name="{self.key}"
                   value="{{% verbatim %}}{value}{{% endverbatim %}}"
                   class="input input-bordered w-full {error_class}"
                   {required_attr} />
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""

    def clean(self, value):
        if not value:
            return None
        if hasattr(value, "hour"):
            return value

        try:
            return time.fromisoformat(value)
        except ValueError:
            raise ValueError("Invalid time format. Expected HH:MM or HH:MM:SS.")


class ClearInput(Component):
    """Clear/reset button that resets the form to its initial state."""

    def __init__(self, label: str = "Clear", **kwargs):
        super().__init__(**kwargs)
        self.label = label

    def render_html(self, **kwargs) -> str:
        return f"""
        <button type="reset" class="btn btn-ghost my-2 {self.classes}">{self.label}</button>
        """


class TextareaInput(Input):
    def __init__(self, rows: int = 3, **kwargs):
        super().__init__(**kwargs)
        self.rows = rows

    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        error_class = "textarea-error" if error else ""
        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <textarea
                   id="{self.uid or self.key}"
                   name="{self.key}"
                   placeholder="{self.placeholder}"
                   rows="{self.rows}"
                   class="textarea textarea-bordered w-full {error_class}"
                   {required_attr}>{{% verbatim %}}{value or ""}{{% endverbatim %}}</textarea>
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""

    def clean(self, value):
        if not value:
            return value
        if not isinstance(value, str):
            value = str(value)
        return value.strip()


class ForeignKeyInput(Input):
    """
    A foreign key selector that opens a modal with a selection table.

    Parameters:
        key: Field name (e.g., "semester")
        label: Display label
        selection_url: URL to fetch the selection table modal
        display_attr: Attribute to show for selected object (e.g., "name")
        placeholder: Placeholder text when no selection
    """

    def __init__(
        self,
        model: models.Model,
        display_attr: str = "name",
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.display_attr = display_attr
        self.model = model

    def _get_display_value(self, obj) -> str:
        if obj is None:
            return ""
        if self.display_attr == "__str__":
            return str(obj)
        return str(getattr(obj, self.display_attr, obj))

    def clean(self, value):
        if not self.required and not value:
            return None
        return self.model.objects.get(id=int(value))

    def render_html(self, **kwargs) -> str:
        # Get the current value - could be an object or a pk
        value = self.get_value(**kwargs)
        value_pk = ""
        display_value = ""

        if value is not None:
            if hasattr(value, "pk"):
                # It's a model instance
                value_pk = str(value.pk)
                display_value = self._get_display_value(value)
            else:
                # It's just a pk value
                value_pk = str(value)
                display_value = str(value)

        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        error_class = "input-error" if error else ""
        input_id = self.uid or self.key

        # Build selection URL with target_input and modal_id params
        base_url = self.get_url(**kwargs)
        separator = "&" if "?" in base_url else "?"
        selection_url = (
            f"{base_url}{separator}target_input={input_id}&modal_id={input_id}_modal"
        )

        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <input type="hidden"
                   id="{input_id}_value"
                   name="{self.key}"
                   value="{value_pk}"
                   {required_attr} />
            <div id="{input_id}_display"
                 class="input input-bordered w-full flex items-center cursor-pointer {error_class} {"text-base-content/50" if not display_value else ""}"
                 hx-get="{selection_url}"
                 hx-target="body"
                 hx-push-url="false"
                 hx-swap="beforeend"
                 x-data="{{ value_status: '{"filled" if display_value else "empty"}' }}"
                 @form-reset="value_status = 'empty'; $el.innerHTML = '{self.placeholder}'; $el.classList.add('text-base-content/50'); document.getElementById('{input_id}_value').value = '';"
                 data-placeholder="{self.placeholder}"
                 >{display_value or self.placeholder}</div>
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""


class ManyToManyInput(Input):
    """
    A many-to-many selector that opens a modal with a multi-selection table.

    Parameters:
        key: Field name
        label: Display label
        selection_url: URL to fetch the multi-selection table modal
        display_attr: Attribute to show for selected objects
        placeholder: Placeholder text when no selection
    """

    def __init__(
        self,
        model: type[models.Model],
        display_attr: str = "name",
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.display_attr = display_attr
        self.model = model

    def _get_display_value(self, obj) -> str:
        if obj is None:
            return ""
        if self.display_attr == "__str__":
            return str(obj)
        return str(getattr(obj, self.display_attr, obj))

    def clean(self, value):
        ids = [int(value)] if isinstance(value, str) else [int(id) for id in value]
        objects = list(self.model.objects.filter(id__in=ids))
        return objects

    def render_html(self, **kwargs) -> str:
        values = self.get_value(**kwargs)
        error = kwargs.get("errors", {}).get(self.key)
        error_class = "border-error" if error else ""
        input_id = self.uid or self.key

        base_url = self.get_url(**kwargs)
        separator = "&" if "?" in base_url else "?"
        selection_url = (
            f"{base_url}{separator}target_input={input_id}&modal_id={input_id}_modal"
        )

        # Render existing selected items
        items_html = ""
        if values:
            if isinstance(values, str) or not hasattr(values, "__iter__"):
                values = [values]
            # Handle both querysets and lists
            for item in values:
                value_pk = str(item.pk) if hasattr(item, "pk") else str(item)
                display = (
                    self._get_display_value(item) if hasattr(item, "pk") else str(item)
                )
                items_html += f"""
                    <div id="{input_id}_item_{value_pk}"
                         class="flex items-center gap-2 bg-base-200 rounded-lg px-3 py-1">
                        <input type="hidden" name="{input_id}_values" value="{value_pk}" />
                        <span class="text-sm">{display}</span>
                        <button type="button" class="btn btn-ghost btn-xs"
                                onclick="event.stopPropagation(); this.parentElement.remove()">
                            {{% heroicon_mini "x-mark" class="w-3 h-3" %}}
                        </button>
                    </div>
                """

        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <div id="{input_id}_items"
                 class="flex flex-wrap gap-2 min-h-[2.5rem] p-2 rounded-lg border border-base-content/20 cursor-pointer {error_class}"
                 hx-get="{selection_url}"
                 hx-include="[name='{input_id}_values']"
                 hx-target="body"
                 hx-push-url="false"
                 hx-swap="beforeend"
                 @form-reset="$el.innerHTML = '';">
                {items_html}
            </div>
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""


class DeleteConfirmation(Component):
    """
    A delete confirmation component with title, message, confirm and cancel buttons.

    Parameters:
        key: Key to get the object from kwargs
        title: Title text (default: "Confirm Deletion")
        message: Confirmation message
        cancel_url: URL or callable for the cancel button
    """

    def __init__(
        self,
        title: str = "Confirm Deletion",
        message: str = "Are you sure you want to delete this item?",
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.title = title
        self.message = message

    def render_html(self, **kwargs) -> str:
        request = kwargs.get("request")
        cancel_url = self.get_url(**kwargs)

        csrf_token = ""
        if request:

            csrf_token = get_token(request)

        return f"""
        <div class="container mx-auto {self.classes}">
            <h2 class="text-xl font-bold text-error">{self.title}</h2>
            <p class="my-2">{self.message}</p>
            <form hx-post hx-target="#app-layout" hx-swap="outerHTML" class="flex gap-2 my-4">
                <input type="hidden" name="csrfmiddlewaretoken" value="{csrf_token}">
                <button type="submit" class="btn btn-error">Confirm Delete</button>
                <a hx-get="{cancel_url}" hx-target="#app-layout" hx-swap="outerHTML" class="btn btn-ghost">Cancel</a>
            </form>
        </div>
        """


class Button(Component):
    """A button component that supports HTMX, Alpine.js, and form submissions."""

    def __init__(
        self,
        label: str = "",
        target: Optional[str] = None,
        swap: str = "morph",
        push_url: bool = True,
        hx_trigger: Optional[str] = None,
        icon: Optional[str] = None,
        icon_alt: Optional[str] = None,
        icon_condition: Optional[str] = None,
        onclick: Optional[str] = None,
        method: Optional[str] = None,
        disabled: Union[bool, Callable] = False,
        disabled_elt: Optional[str] = None,
        as_link: bool = False,
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.label = label
        self.target = target
        self.swap = swap
        self.push_url = push_url
        self.hx_trigger = hx_trigger
        self.icon = icon
        self.icon_alt = icon_alt
        self.icon_condition = icon_condition
        self.onclick = onclick
        self.method = method
        self.disabled = disabled
        self.disabled_elt = disabled_elt
        self.as_link = as_link

    def _is_disabled(self, **kwargs) -> bool:
        if callable(self.disabled):
            return self.disabled(self.get_value(), kwargs.get("request"))
        return self.disabled

    def render_html(self, **kwargs) -> str:
        href = self.get_url(**kwargs)
        disabled = self._is_disabled(**kwargs)

        # Icon rendering - supports conditional icons (for theme toggle)
        if self.icon_alt and self.icon_condition:
            icon_html = f"""
                <span x-show="{self.icon_condition}">{{% heroicon_mini "{self.icon}" %}}</span>
                <span x-show="!({self.icon_condition})">{{% heroicon_mini "{self.icon_alt}" %}}</span>
            """
        elif self.icon:
            icon_html = f'{{% heroicon_mini "{self.icon}" %}}'
        else:
            icon_html = ""

        onclick_attr = f'@click="{self.onclick}"' if self.onclick else ""

        btn_class = "" if self.as_link else "btn"

        # Form-based button (for POST requests like logout)
        if self.method and self.method.lower() == "post":
            return f"""
            <form method="post" hx-boost action="{href}">
                {{% csrf_token %}}
                <button type="submit" {"disabled" if disabled else ""} class="{btn_class} {self.classes}" {onclick_attr}>{icon_html} {self.label}</button>
            </form>
            """

        # HTMX button
        if self.target:
            htmx_attrs = f'hx-get="{href}" hx-target="{self.target}" hx-swap="{self.swap}" hx-push-url="{str(self.push_url).lower()}"'
            if self.hx_trigger:
                htmx_attrs += f' hx-trigger="{self.hx_trigger}"'
            if self.disabled_elt:
                htmx_attrs += f' hx-disabled-elt="{self.disabled_elt}"'
            return f"""
            <button type="button" class="{btn_class} {self.classes}" {"disabled" if disabled else ""} {htmx_attrs} {onclick_attr}>{icon_html} {self.label}</button>
            """

        # Plain button (with optional onclick)
        if self.onclick or not href:
            return f"""
            <button type="button" class="{btn_class} {self.classes}" {"disabled" if disabled else ""} {onclick_attr}>{icon_html} {self.label}</button>
            """

        # Link button
        return f"""
        <a href="{href if not disabled else "#"}" class="{btn_class} {"disabled" if disabled else ""} {self.classes}">{icon_html} {self.label}</a>
        """


class PasswordInput(Input):
    """Password input field with masked input."""

    def render_html(self, **kwargs) -> str:
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        error_class = "input-error" if error else ""
        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <input type="password"
                   id="{self.uid or self.key}"
                   name="{self.key}"
                   placeholder="{self.placeholder}"
                   class="input input-bordered w-full {error_class}"
                   {required_attr} />
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""


class EmailInput(Input):
    """Email input field with browser validation."""

    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        value = "" if value is None else value
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        error_class = "input-error" if error else ""
        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <input type="email"
                   id="{self.uid or self.key}"
                   name="{self.key}"
                   value="{{% verbatim %}}{value}{{% endverbatim %}}"
                   placeholder="{self.placeholder}"
                   class="input input-bordered w-full {error_class}"
                   {required_attr} />
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""

    def clean(self, value):
        if not value:
            return value
        if not isinstance(value, str):
            value = str(value)
        s = value.strip().lower()
        try:
            validate_email(s)
        except ValidationError:
            raise ValueError("Invalid email address.")
        return s[:254]


class PhoneInput(Input):
    """Phone number input field with tel type for mobile keyboard optimization."""

    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        value = "" if value is None else value
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        error_class = "input-error" if error else ""
        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <input type="tel"
                   id="{self.uid or self.key}"
                   name="{self.key}"
                   value="{{% verbatim %}}{value}{{% endverbatim %}}"
                   placeholder="{self.placeholder}"
                   class="input input-bordered w-full {error_class}"
                   {required_attr} />
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""

    def clean(self, value):
        if not value:
            return None
        try:
            phone_no = PhoneNumber.from_string(value)
            if not phone_no.is_valid():
                raise ValueError("Invalid phone number.")
            return phone_no
        except ValueError:
            raise
        except Exception:
            raise ValueError("Invalid phone number format.")


class CheckboxInput(Input):
    """Checkbox input with optional Alpine.js x-model binding.

    Parameters:
        x_model: Alpine.js variable name to bind to (e.g., "isDirectory")
    """

    def __init__(self, x_model: Optional[str] = None, **kwargs):
        super().__init__(**kwargs)
        self.x_model = x_model

    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        checked = "checked" if value else ""
        error_class = "checkbox-error" if error else ""
        x_model_attr = f'x-model="{self.x_model}"' if self.x_model else ""
        return f"""
        <div class="mt-3 {self.classes}">
            <label class="label cursor-pointer justify-start gap-2">
                <input type="checkbox"
                       id="{self.uid or self.key}"
                       name="{self.key}"
                       value="True"
                       class="checkbox {error_class}"
                       {checked}
                       {x_model_attr}
                       {required_attr} />
                <span class="label-text">{self.label}</span>
            </label>
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""

    def clean(self, value):
        """Map standard html checkbox strings to python true/false booleans."""
        if value in ("True", "true", "1", True, "on"):
            return True
        return False


class FileInput(Input):
    """File upload input with optional multiple file support.

    Parameters:
        key: Field name
        label: Display label
        accept: File type filter (e.g., "image/*", ".pdf,.doc")
        multiple: Allow multiple file selection
    """

    def __init__(
        self,
        accept: str = "",
        multiple: bool = False,
        **kwargs):
        super().__init__(**kwargs)
        self.accept = accept
        self.multiple = multiple

    def render_html(self, **kwargs) -> str:
        error = kwargs.get("errors", {}).get(self.key)
        required_attr = "required" if self.required else ""
        accept_attr = f'accept="{self.accept}"' if self.accept else ""
        multiple_attr = "multiple" if self.multiple else ""
        error_class = "file-input-error" if error else ""
        return f"""
        <div class="my-1 {self.classes}">
            <label class="label text-sm font-bold">{self.label}</label>
            <input type="file"
                   id="{self.uid or self.key}"
                   name="{self.key}"
                   class="file-input file-input-bordered w-full {error_class}"
                   {accept_attr}
                   {multiple_attr}
                   {required_attr} />
            {f'<span class="text-error text-sm">{error}</span>' if error else ""}
        </div>"""


class Link(Component):
    """A simple link component for use in forms/cards."""

    def __init__(
        self,
        text: str,
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.text = text

    def render_html(self, **kwargs) -> str:
        return f"""
        <a href="{self.get_url(**kwargs)}" class="link link-primary {self.classes}">{self.text}</a>
        """


class ShowIf(Component):
    """Conditional wrapper that shows/hides children based on an Alpine.js expression.

    Use with a parent form that has x_data defined.

    Parameters:
        condition: Alpine.js expression to evaluate (e.g., "isDirectory", "!isDirectory")
        children: Child components to conditionally show
    """

    def __init__(
        self,
        condition: str | None = None,
        render_cond: Callable = lambda child, kwargs: True,
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.condition = condition
        self.render_cond = render_cond

    def render_html(self, **kwargs) -> str:
        children_html = "\n".join(
            child.render(**kwargs) if self.render_cond(child, kwargs) else ""
            for child in self.children
        )

        x_show = f'x-show="{self.condition}"' if self.condition else ""
        return f"""
        <div {x_show} class="{self.classes}">
            {children_html}
        </div>
        """


class CommaSeparatedInput(Input):
    def render_html(self, **kwargs) -> str:

        value = self.get_value(**kwargs)
        if not value:
            items_json = "[]"
        elif isinstance(value, str):
            itemsList = [i.strip() for i in value.split(",") if i.strip()]
            items_json = html.escape(json.dumps(itemsList))
        elif isinstance(value, list):
            items_json = html.escape(json.dumps([str(i) for i in value]))
        else:
            items_json = "[]"

        error = kwargs.get("errors", {}).get(self.key)
        error_class = "text-error" if error else ""

        return f"""
        <div class="my-2 {self.classes}" x-data="{{ 
                items: {{% verbatim %}}{items_json}{{% endverbatim %}}, 
                newItem: '' 
            }}">
            <label class="label text-sm font-bold {error_class}">{self.label}</label>
            
            <div class="flex flex-wrap gap-2 mb-2">
                <template x-for="(item, index) in items" :key="index">
                    <div class="badge badge-lg border-base-300 gap-1 pl-3 pr-1 py-3 flex items-center">
                        <span x-text="item" class="text-sm"></span>
                        <button type="button" 
                                @click="items.splice(index, 1)" 
                                class="btn btn-ghost btn-xs btn-circle text-base-content/50 hover:text-error">
                            <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>
                        </button>
                    </div>
                </template>
            </div>
            
            <input type="hidden" name="{self.key}" :value="items.join(',')" />
            
            <div class="flex gap-2">
                <input type="text" 
                       x-model="newItem" 
                       @keydown.enter.prevent="if(newItem.trim() && !items.includes(newItem.trim())) {{ items.push(newItem.trim()); newItem = ''; }}" 
                       class="input input-sm input-bordered w-full max-w-xs" 
                       placeholder="Type and press Enter to add..." />
                <button type="button" 
                        @click="if(newItem.trim() && !items.includes(newItem.trim())) {{ items.push(newItem.trim()); newItem = ''; }}" 
                        class="btn btn-sm btn-secondary">
                    Add
                </button>
            </div>
            {f'<span class="text-error text-sm mt-1">{error}</span>' if error else ""}
        </div>
        """

    def clean(self, value):
        if not value:
            return []
        items = [i.strip() for i in str(value).split(",") if i.strip()]
        return items
