use crate::button::ButtonLabel;
use crate::text::{
    LocalizedTextFormat, TypographyStyle, set_localized_text, set_localized_text_format,
    set_plain_text,
};
use bevy::prelude::*;
use bevy_localization::{Localization, TextKey};

pub const SELECT_CHEVRON_CLOSED: &str = "⌄";
pub const SELECT_CHEVRON_OPEN: &str = "⌃";

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SelectOptionState {
    pub entity: Entity,
    pub value: String,
    pub text: String,
    pub localized_text: Option<TextKey>,
    pub localized_text_format: Option<LocalizedTextFormat>,
    pub disabled: bool,
}

#[derive(Component, Debug, Clone)]
pub struct Select {
    pub name: String,
    pub value: String,
    pub initial_value: String,
    pub options: Vec<SelectOptionState>,
    pub panel: Entity,
    pub trigger: Entity,
    pub chevron_glyph: Entity,
    pub open: bool,
    pub disabled: bool,
}

#[derive(Message, Debug, Clone, PartialEq, Eq)]
pub struct SelectValueChangedMessage {
    pub entity: Entity,
    pub name: String,
    pub value: String,
}

#[derive(Component, Debug, Clone)]
pub(crate) struct SelectTrigger {
    pub(crate) select: Entity,
}

#[derive(Component, Debug, Clone)]
pub(crate) struct SelectOptionButton {
    pub(crate) select: Entity,
    pub(crate) value: String,
}

#[derive(Component, Debug, Clone)]
pub struct SelectPanel;

#[derive(Component, Debug, Clone)]
pub(crate) struct SelectOptionIndicator;

#[derive(Component, Debug, Clone)]
pub(crate) struct SelectChevron;

#[derive(Component, Debug, Clone)]
pub(crate) struct SelectChevronGlyph;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddSelectOption {
    pub name: String,
    pub value: String,
    pub text: String,
    pub localized_text: Option<TextKey>,
    pub localized_text_format: Option<LocalizedTextFormat>,
    pub disabled: bool,
}

#[derive(Component, Debug, Clone, PartialEq)]
pub struct AddSelect {
    pub name: String,
    pub value: String,
    pub options: Vec<AddSelectOption>,
    pub class: Option<String>,
    pub trigger_class: Option<String>,
    pub label_class: Option<String>,
    pub label_typography: TypographyStyle,
    pub panel_class: Option<String>,
    pub chevron_class: Option<String>,
    pub option_class: Option<String>,
    pub indicator_class: Option<String>,
    pub disabled: bool,
}

impl Default for AddSelect {
    fn default() -> Self {
        Self {
            name: String::new(),
            value: String::new(),
            options: Vec::new(),
            class: None,
            trigger_class: None,
            label_class: None,
            label_typography: TypographyStyle::default(),
            panel_class: None,
            chevron_class: None,
            option_class: None,
            indicator_class: None,
            disabled: false,
        }
    }
}

pub fn default_select_node() -> Node {
    Node::default()
}

pub fn selected_option(select: &Select) -> Option<&SelectOptionState> {
    select
        .options
        .iter()
        .find(|option| option.value == select.value)
}

pub fn trigger_label_entity(
    trigger_labels: &Query<&ButtonLabel>,
    select: &Select,
) -> Option<Entity> {
    trigger_labels
        .get(select.trigger)
        .ok()
        .map(|label| label.entity)
}

pub fn sync_select_label(
    commands: &mut Commands,
    localization: Option<&Localization>,
    label_entity: Entity,
    option: Option<&SelectOptionState>,
    fallback_value: &str,
) {
    let Some(option) = option else {
        set_plain_text(commands, label_entity, fallback_value);
        return;
    };

    if let (Some(localization), Some(format)) = (localization, option.localized_text_format.clone())
    {
        set_localized_text_format(commands, label_entity, localization, format);
        return;
    }
    if let (Some(localization), Some(key)) = (localization, option.localized_text) {
        set_localized_text(commands, label_entity, localization, key);
        return;
    }
    set_plain_text(commands, label_entity, option.text.clone());
}
