//! Form components with WCAG 2.2 AA accessibility.

use leptos::prelude::*;

/// Text input field with label.
#[component]
pub fn TextInput(
    name: &'static str,
    label: &'static str,
    #[prop(default = "text")] input_type: &'static str,
    #[prop(optional)] placeholder: &'static str,
    #[prop(optional)] help_text: &'static str,
    #[prop(default = false)] required: bool,
    #[prop(optional)] minlength: Option<i32>,
    #[prop(optional)] maxlength: Option<i32>,
    #[prop(optional)] pattern: &'static str,
    #[prop(optional)] autocomplete: &'static str,
    #[prop(optional)] class: &'static str,
    #[prop(into)] value: Signal<String>,
    on_input: Callback<String>,
) -> impl IntoView {
    let input_id = name;
    let help_id = format!("{}-help", name);
    let has_help = !help_text.is_empty();

    view! {
        <div class=format!("space-y-2 {}", class)>
            <label for=input_id class="block text-sm font-medium text-gray-300">
                {label}
                {if required {
                    view! { <span class="text-red-400 ml-1" aria-hidden="true">"*"</span> }.into_any()
                } else {
                    view! { <span class="text-gray-500 ml-1">"(optional)"</span> }.into_any()
                }}
            </label>
            <input
                type=input_type
                id=input_id
                name=name
                placeholder=placeholder
                required=required
                minlength=minlength
                maxlength=maxlength
                pattern=if pattern.is_empty() { None } else { Some(pattern) }
                autocomplete=if autocomplete.is_empty() { None } else { Some(autocomplete) }
                aria-describedby=if has_help { Some(help_id.clone()) } else { None }
                class="input-base"
                prop:value=move || value.get()
                on:input=move |ev| on_input.run(event_target_value(&ev))
            />
            {if has_help {
                view! { <p id=help_id class="text-sm text-gray-400">{help_text}</p> }.into_any()
            } else {
                view! {}.into_any()
            }}
        </div>
    }
}

/// Textarea field with label.
#[component]
pub fn TextArea(
    name: &'static str,
    label: &'static str,
    #[prop(optional)] placeholder: &'static str,
    #[prop(optional)] help_text: &'static str,
    #[prop(default = false)] required: bool,
    #[prop(default = 3)] rows: i32,
    #[prop(optional)] class: &'static str,
    #[prop(into)] value: Signal<String>,
    on_input: Callback<String>,
) -> impl IntoView {
    let input_id = name;
    let help_id = format!("{}-help", name);
    let has_help = !help_text.is_empty();

    view! {
        <div class=format!("space-y-2 {}", class)>
            <label for=input_id class="block text-sm font-medium text-gray-300">
                {label}
                {if required {
                    view! { <span class="text-red-400 ml-1" aria-hidden="true">"*"</span> }.into_any()
                } else {
                    view! { <span class="text-gray-500 ml-1">"(optional)"</span> }.into_any()
                }}
            </label>
            <textarea
                id=input_id
                name=name
                placeholder=placeholder
                required=required
                rows=rows
                aria-describedby=if has_help { Some(help_id.clone()) } else { None }
                class="input-base resize-y"
                prop:value=move || value.get()
                on:input=move |ev| on_input.run(event_target_value(&ev))
            />
            {if has_help {
                view! { <p id=help_id class="text-sm text-gray-400">{help_text}</p> }.into_any()
            } else {
                view! {}.into_any()
            }}
        </div>
    }
}

/// Radio button group.
#[component]
pub fn RadioGroup(
    name: &'static str,
    legend: &'static str,
    options: Vec<(&'static str, &'static str, &'static str)>,
    #[prop(into)] value: Signal<String>,
    on_change: Callback<String>,
) -> impl IntoView {
    view! {
        <fieldset class="space-y-3">
            <legend class="block text-sm font-medium text-gray-300 mb-2">{legend}</legend>
            <div class="space-y-2">
                {options
                    .into_iter()
                    .map(|(val, label, description)| {
                        let val_clone = val.to_string();
                        let is_selected = Signal::derive(move || value.get() == val);
                        view! {
                            <label class="flex items-start space-x-3 cursor-pointer group">
                                <input
                                    type="radio"
                                    name=name
                                    value=val
                                    checked=move || is_selected.get()
                                    on:change=move |_| on_change.run(val_clone.clone())
                                    class="mt-1 w-4 h-4 text-blue-500 bg-gray-700 border-gray-600 focus:ring-blue-500 focus:ring-2"
                                />
                                <div>
                                    <span class="text-white group-hover:text-blue-400 transition-colors">
                                        {label}
                                    </span>
                                    <p class="text-sm text-gray-400">{description}</p>
                                </div>
                            </label>
                        }
                    })
                    .collect_view()}
            </div>
        </fieldset>
    }
}

/// Checkbox input.
#[component]
pub fn Checkbox(
    name: &'static str,
    label: &'static str,
    #[prop(optional)] description: &'static str,
    #[prop(into)] checked: Signal<bool>,
    on_change: Callback<bool>,
) -> impl IntoView {
    let has_description = !description.is_empty();

    view! {
        <label class="flex items-start space-x-3 cursor-pointer group">
            <input
                type="checkbox"
                name=name
                prop:checked=move || checked.get()
                on:change=move |ev| on_change.run(event_target_checked(&ev))
                class="mt-1 w-4 h-4 text-blue-500 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2"
            />
            <div>
                <span class="text-white group-hover:text-blue-400 transition-colors">{label}</span>
                {if has_description {
                    view! { <p class="text-sm text-gray-400">{description}</p> }.into_any()
                } else {
                    view! {}.into_any()
                }}
            </div>
        </label>
    }
}

/// Range slider input.
#[component]
pub fn RangeSlider(
    name: &'static str,
    label: &'static str,
    min: i32,
    max: i32,
    #[prop(default = 1)] step: i32,
    #[prop(into)] value: Signal<i32>,
    on_change: Callback<i32>,
) -> impl IntoView {
    let input_id = name;

    view! {
        <div class="space-y-2">
            <div class="flex justify-between items-center">
                <label for=input_id class="block text-sm font-medium text-gray-300">
                    {label}
                </label>
                <span class="text-sm text-gray-400">{move || value.get()}</span>
            </div>
            <input
                type="range"
                id=input_id
                name=name
                min=min
                max=max
                step=step
                prop:value=move || value.get()
                on:input=move |ev| {
                    if let Ok(val) = event_target_value(&ev).parse::<i32>() {
                        on_change.run(val);
                    }
                }
                class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-500"
            />
            <div class="flex justify-between text-xs text-gray-500">
                <span>{min}</span>
                <span>{max}</span>
            </div>
        </div>
    }
}

/// Primary submit button.
#[component]
pub fn SubmitButton(
    #[prop(default = "Submit")] text: &'static str,
    #[prop(default = "Submitting...")] loading_text: &'static str,
    #[prop(into)] pending: Signal<bool>,
) -> impl IntoView {
    view! {
        <button type="submit" disabled=move || pending.get() class="btn-primary w-full">
            {move || if pending.get() { loading_text } else { text }}
        </button>
    }
}

/// Error alert box.
#[component]
pub fn ErrorAlert(#[prop(into)] message: Signal<Option<String>>) -> impl IntoView {
    view! {
        <Show when=move || message.get().is_some()>
            <div class="error-message" role="alert">
                <p>{move || message.get().unwrap_or_default()}</p>
            </div>
        </Show>
    }
}

/// Success alert box.
#[component]
pub fn SuccessAlert(#[prop(into)] message: Signal<Option<String>>) -> impl IntoView {
    view! {
        <Show when=move || message.get().is_some()>
            <div
                class="p-4 bg-green-900/50 border border-green-500 rounded-lg text-green-200"
                role="alert"
            >
                <p>{move || message.get().unwrap_or_default()}</p>
            </div>
        </Show>
    }
}

/// Color picker component.
#[component]
pub fn ColorPicker(
    #[prop(into)] value: Signal<String>,
    on_change: Callback<String>,
    label: &'static str,
    id: &'static str,
) -> impl IntoView {
    view! {
        <div class="flex items-center gap-3">
            <label for=id class="text-sm font-medium text-gray-300">
                {label}
            </label>
            <input
                type="color"
                id=id
                prop:value=move || value.get()
                on:input=move |ev| on_change.run(event_target_value(&ev))
                class="w-10 h-10 rounded border border-gray-600 cursor-pointer"
            />
            <input
                type="text"
                prop:value=move || value.get()
                on:input=move |ev| on_change.run(event_target_value(&ev))
                class="input-base w-24 text-sm"
                placeholder="#1a1a2e"
            />
        </div>
    }
}

/// Color palette component.
#[component]
pub fn ColorPalette(
    #[prop(into)] value: Signal<String>,
    on_change: Callback<String>,
) -> impl IntoView {
    let colors = [
        "#1a1a2e", "#16213e", "#0f3460", "#e94560", "#533483", "#2c3e50", "#1e8449", "#d35400",
    ];

    view! {
        <div class="flex gap-2 flex-wrap">
            {colors
                .into_iter()
                .map(|color| {
                    let is_selected = Signal::derive(move || value.get() == color);
                    let color_string = color.to_string();
                    view! {
                        <button
                            type="button"
                            class=move || {
                                let base = "w-8 h-8 rounded border-2 transition-transform hover:scale-110";
                                if is_selected.get() {
                                    format!("{} border-white", base)
                                } else {
                                    format!("{} border-transparent", base)
                                }
                            }
                            style=format!("background-color: {}", color)
                            title=color
                            on:click=move |_| on_change.run(color_string.clone())
                        />
                    }
                })
                .collect_view()}
        </div>
    }
}

/// Get the checked state of a checkbox input.
#[cfg(feature = "hydrate")]
fn event_target_checked(ev: &leptos::ev::Event) -> bool {
    use wasm_bindgen::JsCast;
    ev.target()
        .and_then(|t| {
            t.dyn_ref::<web_sys::HtmlInputElement>()
                .map(|el| el.checked())
        })
        .unwrap_or(false)
}

/// Stub for SSR.
#[cfg(not(feature = "hydrate"))]
fn event_target_checked(_ev: &leptos::ev::Event) -> bool {
    false
}
