//! Form parser type definitions
//!
//! Defines the data structures for form definitions, fields, and display items.

use serde::{de, Deserialize, Deserializer, Serialize};

/// Default order for form elements (auto-assigned based on array position)
pub(super) fn default_order() -> usize {
    0
}

/// Deserialize `default` field accepting both string and boolean TOML values
pub(super) fn deserialize_default<'de, D>(
    deserializer: D,
) -> std::result::Result<Option<String>, D::Error>
where
    D: Deserializer<'de>,
{
    use serde_json::Value;
    let value: Option<Value> = Option::deserialize(deserializer)?;
    Ok(value.and_then(|v| match v {
        Value::String(s) => Some(s),
        Value::Bool(b) => Some(b.to_string()),
        Value::Number(n) => Some(n.to_string()),
        _ => None,
    }))
}

/// Form element (can be a display item or a field)
/// Public enum for unified form structure
#[derive(Debug, Clone)]
pub enum FormElement {
    Item(Box<DisplayItem>),
    Field(Box<FieldDefinition>),
}

impl FormElement {
    /// Get as DisplayItem if this is an Item variant
    pub fn as_item(&self) -> Option<&DisplayItem> {
        match self {
            FormElement::Item(item) => Some(item.as_ref()),
            _ => None,
        }
    }

    /// Get mutable reference as DisplayItem if this is an Item variant
    pub fn as_item_mut(&mut self) -> Option<&mut DisplayItem> {
        match self {
            FormElement::Item(item) => Some(item.as_mut()),
            _ => None,
        }
    }

    /// Get as FieldDefinition if this is a Field variant
    pub fn as_field(&self) -> Option<&FieldDefinition> {
        match self {
            FormElement::Field(field) => Some(field.as_ref()),
            _ => None,
        }
    }

    /// Get mutable reference as FieldDefinition if this is a Field variant
    pub fn as_field_mut(&mut self) -> Option<&mut FieldDefinition> {
        match self {
            FormElement::Field(field) => Some(field.as_mut()),
            _ => None,
        }
    }

    /// Check if this is an Item variant
    pub fn is_item(&self) -> bool {
        matches!(self, FormElement::Item(_))
    }

    /// Check if this is a Field variant
    pub fn is_field(&self) -> bool {
        matches!(self, FormElement::Field(_))
    }

    /// Get order value (for sorting)
    pub fn order(&self) -> usize {
        match self {
            FormElement::Item(item) => item.order,
            FormElement::Field(field) => field.order,
        }
    }

    /// Get when condition if present
    pub fn when(&self) -> Option<&str> {
        match self {
            FormElement::Item(item) => item.when.as_deref(),
            FormElement::Field(field) => field.when.as_deref(),
        }
    }
}

impl<'de> Deserialize<'de> for FormElement {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        use serde::de::MapAccess;

        struct ElementVisitor;

        impl<'de> de::Visitor<'de> for ElementVisitor {
            type Value = FormElement;

            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                formatter.write_str("a FormElement with a type field")
            }

            fn visit_map<M>(self, mut map: M) -> std::result::Result<FormElement, M::Error>
            where
                M: MapAccess<'de>,
            {
                let mut fields_map = serde_json::Map::new();
                let mut element_type: Option<String> = None;

                while let Some(key) = map.next_key::<String>()? {
                    if key == "type" {
                        element_type = Some(map.next_value()?);
                    } else {
                        let value: serde_json::Value = map.next_value()?;
                        fields_map.insert(key, value);
                    }
                }

                let element_type = element_type.ok_or_else(|| de::Error::missing_field("type"))?;

                // Add type back to map for deserialization
                fields_map.insert("type".to_string(), serde_json::json!(&element_type));

                // Display item types
                let item_types = [
                    "header",
                    "section",
                    "section_header",
                    "footer",
                    "cta",
                    "group",
                ];

                // Field types
                let field_types = [
                    "text",
                    "confirm",
                    "select",
                    "multiselect",
                    "password",
                    "custom",
                    "editor",
                    "date",
                    "repeatinggroup",
                ];

                if item_types.contains(&element_type.as_str()) {
                    let item: DisplayItem =
                        serde_json::from_value(serde_json::Value::Object(fields_map))
                            .map_err(de::Error::custom)?;
                    Ok(FormElement::Item(Box::new(item)))
                } else if field_types.contains(&element_type.as_str()) {
                    let field: FieldDefinition =
                        serde_json::from_value(serde_json::Value::Object(fields_map))
                            .map_err(de::Error::custom)?;
                    Ok(FormElement::Field(Box::new(field)))
                } else {
                    Err(de::Error::custom(format!(
                        "Unknown element type '{}'. Item types: {}. Field types: {}",
                        element_type,
                        item_types.join(", "),
                        field_types.join(", ")
                    )))
                }
            }
        }

        deserializer.deserialize_map(ElementVisitor)
    }
}

impl Serialize for FormElement {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match self {
            FormElement::Item(item) => item.serialize(serializer),
            FormElement::Field(field) => field.serialize(serializer),
        }
    }
}

/// A display item (header, section, CTA, footer, etc.)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisplayItem {
    /// Item name/identifier (not displayed)
    pub name: String,
    /// Item type/purpose
    #[serde(rename = "type")]
    pub item_type: String,
    /// Content to display (can be literal text or i18n key)
    pub content: Option<String>,
    /// Optional title (can be literal text or i18n key)
    pub title: Option<String>,
    /// Optional template expression (alternative to content, e.g., "Welcome {{ env.USER }}!")
    pub template: Option<String>,
    /// Show border on top
    pub border_top: Option<bool>,
    /// Show border on bottom
    pub border_bottom: Option<bool>,
    /// Left margin (number of spaces) - applies to all content
    pub margin_left: Option<usize>,
    /// Left margin for border lines (overrides margin_left for borders)
    pub border_margin_left: Option<usize>,
    /// Left margin for content/title (overrides margin_left for content)
    pub content_margin_left: Option<usize>,
    /// Optional alignment (left, center, right)
    pub align: Option<String>,
    /// Optional conditional display (e.g., "role == admin")
    pub when: Option<String>,
    /// Optional group name (items in same group are associated/grouped together)
    pub group: Option<String>,
    /// Optional array of file paths to include (for type="group")
    pub includes: Option<Vec<String>>,
    /// Display order (position in form flow)
    #[serde(default = "default_order")]
    pub order: usize,
    /// Character to use for top border (default: "═")
    pub border_top_char: Option<String>,
    /// Length of top border line (default: 60)
    pub border_top_len: Option<usize>,
    /// Character for top-left corner (default: none)
    pub border_top_l: Option<String>,
    /// Character for top-right corner (default: none)
    pub border_top_r: Option<String>,
    /// Character to use for bottom border (default: "═")
    pub border_bottom_char: Option<String>,
    /// Length of bottom border line (default: 60)
    pub border_bottom_len: Option<usize>,
    /// Character for bottom-left corner (default: none)
    pub border_bottom_l: Option<String>,
    /// Character for bottom-right corner (default: none)
    pub border_bottom_r: Option<String>,
    /// Optional flag indicating if content/title are i18n keys
    pub i18n: Option<bool>,
}

impl DisplayItem {
    /// Check if this item should be displayed (any non-empty visible attribute)
    pub(crate) fn should_display(&self) -> bool {
        self.content.as_deref().is_some_and(|c| !c.is_empty())
            || self.title.as_deref().is_some_and(|t| !t.is_empty())
            || self.border_top.unwrap_or(false)
            || self.border_bottom.unwrap_or(false)
    }
}

/// A complete form definition loaded from TOML
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FormDefinition {
    /// Form name/identifier
    pub name: String,
    /// Optional form description
    pub description: Option<String>,
    /// Optional locale override for this form (e.g., "es-ES", "en-US")
    pub locale: Option<String>,
    /// Optional template for pre-processing form (generates prompts dynamically)
    pub template: Option<String>,
    /// Optional path to template for post-processing results
    pub output_template: Option<String>,
    /// Optional i18n prefix for message keys (e.g., "forms.registration")
    pub i18n_prefix: Option<String>,
    /// Display mode: Complete (all fields at once) or FieldByField (one at a time)
    #[serde(default)]
    pub display_mode: DisplayMode,
    /// Unified array of form elements (NEW - preferred format)
    #[serde(default)]
    pub elements: Vec<FormElement>,
    /// Array of display items (headers, sections, CTAs, etc.) - DEPRECATED for serialization
    #[serde(default, skip_serializing)]
    pub items: Vec<DisplayItem>,
    /// Array of form fields - DEPRECATED for serialization
    #[serde(default, skip_serializing)]
    pub fields: Vec<FieldDefinition>,
}

impl FormDefinition {
    /// Migrate legacy items/fields arrays to unified elements array
    /// If elements is already populated, does nothing (already using new format)
    pub fn migrate_to_elements(&mut self) {
        if !self.elements.is_empty() {
            return; // Already using new format
        }

        // Build unified list with order preservation (items before fields to maintain TOML order)
        let mut element_list: Vec<FormElement> = Vec::new();

        // Add items first, preserving insertion order
        for mut item in self.items.drain(..) {
            // Assign order based on position to preserve insertion order
            item.order = element_list.len();
            element_list.push(FormElement::Item(Box::new(item)));
        }

        // Add fields, preserving insertion order after items
        for mut field in self.fields.drain(..) {
            // Assign order based on position to preserve insertion order
            field.order = element_list.len();
            element_list.push(FormElement::Field(Box::new(field)));
        }

        // Assign to elements (already in correct insertion order)
        self.elements = element_list;
    }

    /// Migrate from unified elements back to separate fields and items
    /// Used by backends that still expect separate fields/items arrays
    pub fn migrate_from_elements(&mut self) {
        if self.elements.is_empty() {
            return; // Already using legacy format or empty
        }

        // Separate elements into fields and items
        for element in self.elements.drain(..) {
            match element {
                FormElement::Field(field) => {
                    self.fields.push(*field);
                }
                FormElement::Item(item) => {
                    self.items.push(*item);
                }
            }
        }

        // Clear elements after migration
        self.elements.clear();
    }
}

/// Option with value and optional label for select/multiselect fields
/// Supports both simple string format and object format with value/label
#[derive(Debug, Clone, Serialize)]
pub struct SelectOption {
    /// The value to store when selected
    pub value: String,
    /// Display label (if None, use value)
    pub label: Option<String>,
}

impl SelectOption {
    /// Get the display label, fallback to value if label not provided
    pub fn display_label(&self) -> &str {
        self.label.as_deref().unwrap_or(&self.value)
    }

    /// Convert to simple string for backward compatibility
    pub fn as_string(&self) -> String {
        self.value.clone()
    }
}

/// Custom deserializer for SelectOption that supports both formats:
/// - "simple string" → SelectOption { value: "simple string", label: None }
/// - { value: "x", label: "Display Label" } → SelectOption { value: "x", label: Some("Display Label") }
impl<'de> Deserialize<'de> for SelectOption {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = serde_json::Value::deserialize(deserializer)?;

        match value {
            // Simple string format: "cx11" → {value: "cx11", label: None}
            serde_json::Value::String(s) => Ok(SelectOption {
                value: s,
                label: None,
            }),
            // Object format: {value: "cx11", label: "Shared CPU"} or {value: "cx11"}
            serde_json::Value::Object(obj) => {
                let val = obj
                    .get("value")
                    .and_then(|v| v.as_str())
                    .map(|s| s.to_string())
                    .ok_or_else(|| de::Error::missing_field("value"))?;

                let lbl = obj
                    .get("label")
                    .and_then(|v| v.as_str())
                    .map(|s| s.to_string());

                Ok(SelectOption {
                    value: val,
                    label: lbl,
                })
            }
            _ => Err(de::Error::custom(
                "SelectOption must be a string or object with 'value' field",
            )),
        }
    }
}

/// Support conversion from simple string to SelectOption
impl From<String> for SelectOption {
    fn from(value: String) -> Self {
        SelectOption { value, label: None }
    }
}

impl From<&str> for SelectOption {
    fn from(value: &str) -> Self {
        SelectOption {
            value: value.to_string(),
            label: None,
        }
    }
}

/// A single field in a form
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldDefinition {
    /// Field name (becomes the result key)
    pub name: String,
    /// Field input type
    #[serde(rename = "type")]
    pub field_type: FieldType,
    /// Prompt message (can be literal text or i18n key)
    pub prompt: String,
    /// Optional default value (can contain template expressions like {{ env.USER }})
    /// Accepts both string ("false", "true") and boolean (false, true) TOML values
    #[serde(default, deserialize_with = "deserialize_default")]
    pub default: Option<String>,
    /// Optional placeholder text (can be literal text or i18n key)
    pub placeholder: Option<String>,
    /// Optional options list with value/label (can contain literal text or i18n keys)
    #[serde(default)]
    pub options: Vec<SelectOption>,
    /// Optional reference to another field whose values should filter this field's options
    /// Example: options_from = "detected_languages" means only show options whose values
    /// are present in the detected_languages field's selected values
    #[serde(default)]
    pub options_from: Option<String>,
    /// Optional field requirement flag
    pub required: Option<bool>,
    /// Optional file extension (for editor)
    pub file_extension: Option<String>,
    /// Optional prefix text (for editor)
    pub prefix_text: Option<String>,
    /// Optional page size (for select/multiselect)
    pub page_size: Option<usize>,
    /// Optional vim mode flag (for select/multiselect)
    pub vim_mode: Option<bool>,
    /// Optional display mode for multiselect: "list" (default), "grid", "dropdown", "tags"
    #[serde(default)]
    pub display_mode: Option<String>,
    /// Optional searchable flag for multiselect/dropdown (enables filtering)
    #[serde(default)]
    pub searchable: Option<bool>,
    /// Optional minimum selected items (for multiselect)
    #[serde(default)]
    pub min_selected: Option<usize>,
    /// Optional maximum selected items (for multiselect)
    #[serde(default)]
    pub max_selected: Option<usize>,
    /// Optional custom type name (for custom)
    pub custom_type: Option<String>,
    /// Optional min date (for date)
    pub min_date: Option<String>,
    /// Optional max date (for date)
    pub max_date: Option<String>,
    /// Optional week start day (for date, default: Mon)
    pub week_start: Option<String>,
    /// Display order (position in form flow)
    #[serde(default = "default_order")]
    pub order: usize,
    /// Optional conditional display (e.g., "role == admin", "country != US")
    pub when: Option<String>,
    /// Output behavior when `when` condition is false
    /// - `exclude` (default): field absent from results
    /// - `default`: field injected with its default value into results
    #[serde(default)]
    pub when_false: WhenFalse,
    /// Optional flag indicating if prompt/placeholder/options are i18n keys
    pub i18n: Option<bool>,
    /// Optional semantic grouping for form organization
    #[serde(default)]
    pub group: Option<String>,
    /// Nickel contract/predicate (e.g., "String | std.string.NonEmpty")
    #[serde(default)]
    pub nickel_contract: Option<String>,
    /// Original Nickel field path (e.g., ["user", "name"])
    #[serde(default)]
    pub nickel_path: Option<Vec<String>>,
    /// Original Nickel documentation
    #[serde(default)]
    pub nickel_doc: Option<String>,
    /// Semantic alias for field name (short, human-readable)
    #[serde(default)]
    pub nickel_alias: Option<String>,
    /// Fragment path for RepeatingGroup (defines array element fields)
    #[serde(default)]
    pub fragment: Option<String>,
    /// Minimum items for array (0 = optional)
    #[serde(default)]
    pub min_items: Option<usize>,
    /// Maximum items limit
    #[serde(default)]
    pub max_items: Option<usize>,
    /// Initial number of items to display
    #[serde(default)]
    pub default_items: Option<usize>,
    /// Mark repeating group items as unique (all fields must be different)
    #[serde(default)]
    pub unique: Option<bool>,
    /// Mark this field as the unique key for repeating group (only this field must be different)
    #[serde(default)]
    pub unique_key: Option<bool>,
    /// Mark field value as sensitive (encrypt or redact output)
    #[serde(default)]
    pub sensitive: Option<bool>,
    /// Encryption backend (age, rustyvault, sops)
    #[serde(default)]
    pub encryption_backend: Option<String>,
    /// Encryption config (backend-specific settings: key_file, vault_addr, etc)
    #[serde(default)]
    pub encryption_config: Option<std::collections::HashMap<String, String>>,
}

impl FieldDefinition {
    /// Auto-detect sensitive: true if sensitive=true OR type=password AND sensitive not explicitly false
    pub fn is_sensitive(&self) -> bool {
        match self.sensitive {
            Some(true) => true,
            Some(false) => false,
            None => self.field_type == FieldType::Password,
        }
    }

    /// Get effective encryption backend (field-level > default > "age")
    pub fn effective_encryption_backend(&self, cli_default: Option<&str>) -> String {
        self.encryption_backend
            .clone()
            .or_else(|| cli_default.map(String::from))
            .unwrap_or_else(|| "age".to_string())
    }
}

/// Supported field input types
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum FieldType {
    /// Single-line text input
    Text,
    /// Yes/no confirmation
    Confirm,
    /// Single selection from list
    Select,
    /// Multiple selection from list
    MultiSelect,
    /// Secure password input
    Password,
    /// Custom type parsing
    Custom,
    /// External editor
    Editor,
    /// Date selection
    Date,
    /// Repeating group - dynamic add/remove instances from fragment
    RepeatingGroup,
}

/// Output behavior for a field when its `when` condition evaluates to false
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum WhenFalse {
    /// Field is excluded from results entirely (default)
    #[default]
    Exclude,
    /// Field's `default` value is injected into results without prompting
    Default,
}

/// Form display mode - how fields are presented to user
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DisplayMode {
    /// Show all fields at once (complete form) - default for TUI and Web
    #[default]
    #[serde(alias = "complete", alias = "all")]
    Complete,
    /// Show one field at a time (step by step)
    #[serde(alias = "step")]
    FieldByField,
}
