//! Form execution logic
//!
//! Handles form execution with various backends and execution modes.

use crate::error::Result;
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;

use super::conditions::evaluate_condition;
use super::translation::{translate_display_item, translate_field_definition};
use super::types::{
    DisplayItem, DisplayMode, FieldDefinition, FormDefinition, FormElement, WhenFalse,
};

#[cfg(feature = "i18n")]
use crate::i18n::I18nBundle;

#[cfg(not(feature = "i18n"))]
type I18nBundle = ();

/// Render a display item with formatting, respecting conditionals
pub fn render_display_item(item: &DisplayItem, results: &HashMap<String, serde_json::Value>) {
    // Check if item should be shown based on conditional
    if let Some(condition) = &item.when {
        if !evaluate_condition(condition, results) {
            // Item condition not met, skip it
            return;
        }
    }

    if !item.should_display() {
        return;
    }

    let default_margin = item.margin_left.unwrap_or(0);
    let border_margin = item.border_margin_left.unwrap_or(default_margin);
    let content_margin = item.content_margin_left.unwrap_or(default_margin);

    let border_margin_str = " ".repeat(border_margin);
    let content_margin_str = " ".repeat(content_margin);

    // Top border line
    if item.border_top.unwrap_or(false) {
        let top_l = item.border_top_l.as_deref().unwrap_or("");
        let top_char = item.border_top_char.as_deref().unwrap_or("═");
        let top_len = item.border_top_len.unwrap_or(60);
        let top_r = item.border_top_r.as_deref().unwrap_or("");
        let top_border = top_char.repeat(top_len);
        println!("{}{}{}{}", border_margin_str, top_l, top_border, top_r);
    }

    // Title
    if let Some(title) = &item.title {
        if !title.is_empty() {
            println!("{}{}", content_margin_str, title);
        }
    }

    // Content
    if let Some(content) = &item.content {
        if !content.is_empty() {
            for line in content.lines() {
                println!("{}{}", content_margin_str, line);
            }
        }
    }

    // Bottom border line
    if item.border_bottom.unwrap_or(false) {
        let bottom_l = item.border_bottom_l.as_deref().unwrap_or("");
        let bottom_char = item.border_bottom_char.as_deref().unwrap_or("═");
        let bottom_len = item.border_bottom_len.unwrap_or(60);
        let bottom_r = item.border_bottom_r.as_deref().unwrap_or("");
        let bottom_border = bottom_char.repeat(bottom_len);
        println!(
            "{}{}{}{}",
            border_margin_str, bottom_l, bottom_border, bottom_r
        );
    }
}

#[cfg(feature = "cli")]
pub fn execute_with_base_dir(
    mut form: FormDefinition,
    _base_dir: &Path,
) -> Result<HashMap<String, serde_json::Value>> {
    let mut results = HashMap::new();

    if let Some(desc) = &form.description {
        println!("\n{}\n{}\n", form.name, desc);
    } else {
        println!("\n{}\n", form.name);
    }

    form.migrate_to_elements();

    for element in &form.elements {
        match element {
            FormElement::Item(item) => {
                render_display_item(item, &results);
            }
            FormElement::Field(field) => {
                if let Some(condition) = &field.when {
                    if !evaluate_condition(condition, &results) {
                        if field.when_false == WhenFalse::Default {
                            if let Some(default) = &field.default {
                                results.insert(field.name.clone(), serde_json::json!(default));
                            }
                        }
                        continue;
                    }
                }

                let value = crate::backends::cli::InquireBackend::new()
                    .execute_field_sync(field, &results)?;
                results.insert(field.name.clone(), value.clone());
            }
        }
    }

    Ok(results)
}

/// Execute a form and collect results (no path resolution - for backwards compatibility)
#[cfg(feature = "cli")]
pub fn execute(form: FormDefinition) -> Result<HashMap<String, serde_json::Value>> {
    execute_with_base_dir(form, Path::new("."))
}

/// Load form from TOML file and execute with proper path resolution
#[cfg(feature = "cli")]
pub fn load_and_execute_from_file(
    path: impl AsRef<Path>,
) -> Result<HashMap<String, serde_json::Value>> {
    let path_ref = path.as_ref();
    let base_dir = path_ref.parent().unwrap_or_else(|| Path::new("."));
    let form = super::parser::load_from_file(path_ref)?;
    execute_with_base_dir(form, base_dir)
}

/// Build ordered element list from form definition.
///
/// Elements are returned in declaration order (array index). No file I/O: group expansion
/// via Nickel imports or `expand_includes` must happen before form execution.
fn build_element_list(form: &FormDefinition) -> Vec<(usize, FormElement)> {
    form.elements.iter().cloned().enumerate().collect()
}

/// Inject default values into results for fields whose `when` condition is false
/// and `when_false = default`. Called after complete-form execution where the backend
/// never sees the hidden fields.
fn apply_when_false_defaults(
    element_list: &[(usize, FormElement)],
    results: &mut HashMap<String, serde_json::Value>,
) {
    for (_, element) in element_list {
        if let FormElement::Field(field) = element {
            if let Some(condition) = &field.when {
                if !evaluate_condition(condition, results) && field.when_false == WhenFalse::Default
                {
                    if let Some(default) = &field.default {
                        results
                            .entry(field.name.clone())
                            .or_insert_with(|| serde_json::json!(default));
                    }
                }
            }
        }
    }
}

/// Recompute visible elements based on current results
///
/// This function rebuilds the element list with lazy loading based on current results,
/// filtering out elements whose conditions don't match. Used for reactive rendering
/// in TUI and Web backends when user input changes selections that affect conditionals.
///
/// # Arguments
///
/// * `form` - The form definition
/// * `base_dir` - Base directory for resolving relative paths
/// * `results` - Current accumulated results (used for condition evaluation)
///
/// # Returns
///
/// A tuple of (filtered items, filtered fields) that should be displayed
/// based on current conditions and lazy loading rules
pub fn recompute_visible_elements(
    form: &FormDefinition,
    _base_dir: &Path,
    results: &HashMap<String, serde_json::Value>,
) -> Result<(Vec<DisplayItem>, Vec<FieldDefinition>)> {
    // Clone and migrate form to ensure elements are populated
    let mut form_clone = form.clone();
    form_clone.migrate_to_elements();

    let element_list = build_element_list(&form_clone);

    // Separate and filter items and fields based on conditions
    let mut visible_items = Vec::new();
    let mut visible_fields = Vec::new();

    for (_, element) in element_list {
        match element {
            FormElement::Item(item) => {
                // Include item if it has no condition, or condition is true
                let should_show = item
                    .when
                    .as_ref()
                    .is_none_or(|cond| evaluate_condition(cond, results));

                if should_show {
                    visible_items.push(*item);
                }
            }
            FormElement::Field(field) => {
                // Include field if it has no condition, or condition is true
                let should_show = field
                    .when
                    .as_ref()
                    .is_none_or(|cond| evaluate_condition(cond, results));

                if should_show {
                    visible_fields.push(*field);
                }
            }
        }
    }

    Ok((visible_items, visible_fields))
}

/// Execute a form using two-phase execution (for sequential CLI-like backends)
///
/// This execution strategy is designed for backends that execute fields sequentially
/// and cannot "go back" to modify previous selections. It uses a two-phase approach:
///
/// **Phase 1:** Identify and execute selector fields (fields that control conditionals)
/// **Phase 2:** With selector values known, rebuild element list and execute remaining fields
///
/// This ensures fragments are only loaded after their controlling conditions are determined.
///
/// # Arguments
///
/// * `form` - The parsed form definition
/// * `backend` - A mutable reference to the form backend implementation
/// * `i18n_bundle` - Optional I18n bundle for translating form content
/// * `base_dir` - Base directory for resolving relative paths in includes
///
/// # Returns
///
/// A HashMap containing all field results (name -> value)
/// Unified form execution with all options (primary implementation)
/// Handles: two-phase execution, display modes, i18n, initial values, base_dir resolution
pub async fn execute_with_backend_complete(
    form: FormDefinition,
    backend: &mut dyn crate::backends::FormBackend,
    i18n_bundle: Option<&I18nBundle>,
    base_dir: &Path,
    initial_values: Option<HashMap<String, serde_json::Value>>,
) -> Result<HashMap<String, serde_json::Value>> {
    use crate::backends::RenderContext;

    // Store initial values for later merging before unwrap_or_default
    let initial_backup = initial_values.clone();
    let mut results: Arc<HashMap<String, serde_json::Value>> =
        Arc::new(initial_values.unwrap_or_default());

    // Initialize backend
    backend.initialize().await?;

    // PHASE 1: Identify selector fields and execute them first
    let selector_field_names = super::conditions::identify_selector_fields(&form);

    for field_name in &selector_field_names {
        if let Some(field) = form.fields.iter().find(|f| &f.name == field_name) {
            let translated_field = translate_field_definition(field, i18n_bundle);
            let context = RenderContext {
                results: Arc::clone(&results),
                locale: None,
            };
            let value = backend.execute_field(&translated_field, &context).await?;
            Arc::make_mut(&mut results).insert(field.name.clone(), value);
        }
    }

    let element_list = build_element_list(&form);

    // Execute remaining fields based on display mode
    if form.display_mode == DisplayMode::Complete {
        // Complete mode: pass all fields to backend for complete form display
        let items: Vec<&DisplayItem> = element_list
            .iter()
            .filter_map(|(_, e)| match e {
                FormElement::Item(item) => Some(item.as_ref()),
                _ => None,
            })
            .collect();

        let fields: Vec<&FieldDefinition> = element_list
            .iter()
            .filter_map(|(_, e)| match e {
                FormElement::Field(field) => {
                    // Skip selector fields (already executed in Phase 1)
                    if selector_field_names.contains(&field.name) {
                        return None;
                    }
                    Some(field.as_ref())
                }
                _ => None,
            })
            .collect();

        let items_owned: Vec<DisplayItem> = items
            .iter()
            .map(|i| translate_display_item(i, i18n_bundle))
            .collect();
        let fields_owned: Vec<FieldDefinition> = fields
            .iter()
            .map(|f| translate_field_definition(f, i18n_bundle))
            .collect();

        let complete_results = backend
            .execute_form_complete(&form, base_dir, items_owned, fields_owned, initial_backup)
            .await?;
        results = Arc::new(complete_results);
    } else {
        // Field-by-field mode: iterate through element list
        for (_, element) in element_list.iter() {
            match element {
                FormElement::Item(item) => {
                    if let Some(condition) = &item.when {
                        if !evaluate_condition(condition, &results) {
                            continue;
                        }
                    }
                    let context = RenderContext {
                        results: Arc::clone(&results),
                        locale: None,
                    };
                    let translated_item = translate_display_item(item, i18n_bundle);
                    backend
                        .render_display_item(&translated_item, &context)
                        .await?;
                }
                FormElement::Field(field) => {
                    if selector_field_names.contains(&field.name) {
                        continue;
                    }

                    if let Some(condition) = &field.when {
                        if !evaluate_condition(condition, &results) {
                            if field.when_false == WhenFalse::Default {
                                if let Some(default) = &field.default {
                                    Arc::make_mut(&mut results)
                                        .insert(field.name.clone(), serde_json::json!(default));
                                }
                            }
                            continue;
                        }
                    }

                    let context = RenderContext {
                        results: Arc::clone(&results),
                        locale: None,
                    };
                    let translated_field = translate_field_definition(field, i18n_bundle);
                    let value = backend.execute_field(&translated_field, &context).await?;
                    Arc::make_mut(&mut results).insert(field.name.clone(), value);
                }
            }
        }
    }

    // Shutdown backend
    backend.shutdown().await?;

    Ok(Arc::try_unwrap(results).unwrap_or_else(|arc| (*arc).clone()))
}

/// Two-phase execution: selectors first, then dynamic fragments, then remaining fields
pub async fn execute_with_backend_two_phase(
    form: FormDefinition,
    backend: &mut dyn crate::backends::FormBackend,
    i18n_bundle: Option<&I18nBundle>,
    base_dir: &Path,
) -> Result<HashMap<String, serde_json::Value>> {
    execute_with_backend_two_phase_with_defaults(form, backend, i18n_bundle, base_dir, None).await
}

/// Two-phase execution with initial values
pub async fn execute_with_backend_two_phase_with_defaults(
    mut form: FormDefinition,
    backend: &mut dyn crate::backends::FormBackend,
    i18n_bundle: Option<&I18nBundle>,
    _base_dir: &Path,
    initial_values: Option<HashMap<String, serde_json::Value>>,
) -> Result<HashMap<String, serde_json::Value>> {
    use crate::backends::RenderContext;

    let initial_backup = initial_values.clone();
    let mut results: Arc<HashMap<String, serde_json::Value>> =
        Arc::new(initial_values.unwrap_or_default());

    // Migrate legacy format (items/fields) to new unified elements format
    form.migrate_to_elements();

    // Initialize backend
    backend.initialize().await?;

    // PHASE 1: Identify selector fields and execute them first
    let selector_field_names = super::conditions::identify_selector_fields(&form);

    for field_name in &selector_field_names {
        let field_option = form.elements.iter().find_map(|e| {
            if let FormElement::Field(field) = e {
                if &field.name == field_name {
                    Some(field)
                } else {
                    None
                }
            } else {
                None
            }
        });

        if let Some(field) = field_option {
            let translated_field = translate_field_definition(field, i18n_bundle);
            let context = RenderContext {
                results: Arc::clone(&results),
                locale: None,
            };
            let value = backend.execute_field(&translated_field, &context).await?;
            Arc::make_mut(&mut results).insert(field.name.clone(), value);
        }
    }

    let mut element_list = build_element_list(&form);

    // Apply initial_values to field.default for all expanded elements
    // This ensures defaults from nickel-roundtrip input files are shown in the UI
    if let Some(ref init_vals) = initial_backup {
        for (_, element) in element_list.iter_mut() {
            if let FormElement::Field(field) = element {
                if let Some(value) = init_vals.get(&field.name) {
                    if field.default.is_none() {
                        field.default = Some(super::conditions::value_to_string(value));
                    }
                }
            }
        }
    }

    // PHASE 3: Execute remaining fields (non-selectors)
    for (_, element) in element_list.iter() {
        match element {
            FormElement::Item(item) => {
                if let Some(condition) = &item.when {
                    if !evaluate_condition(condition, &results) {
                        continue;
                    }
                }
                let context = RenderContext {
                    results: Arc::clone(&results),
                    locale: None,
                };
                let translated_item = translate_display_item(item, i18n_bundle);
                backend
                    .render_display_item(&translated_item, &context)
                    .await?;
            }
            FormElement::Field(field) => {
                if selector_field_names.contains(&field.name) {
                    continue;
                }

                if let Some(condition) = &field.when {
                    if !evaluate_condition(condition, &results) {
                        if field.when_false == WhenFalse::Default {
                            if let Some(default) = &field.default {
                                Arc::make_mut(&mut results)
                                    .insert(field.name.clone(), serde_json::json!(default));
                            }
                        }
                        continue;
                    }
                }

                let context = RenderContext {
                    results: Arc::clone(&results),
                    locale: None,
                };
                let translated_field = translate_field_definition(field, i18n_bundle);
                let value = backend.execute_field(&translated_field, &context).await?;
                Arc::make_mut(&mut results).insert(field.name.clone(), value);
            }
        }
    }

    // Merge back initial values for fields not provided by user
    if let Some(init_vals) = &initial_backup {
        for (k, v) in init_vals.iter() {
            if !results.contains_key(k) {
                Arc::make_mut(&mut results).insert(k.clone(), v.clone());
            }
        }
    }

    // Shutdown backend
    backend.shutdown().await?;

    Ok(Arc::try_unwrap(results).unwrap_or_else(|arc| (*arc).clone()))
}

/// Execute a form using a specific backend
///
/// This is the primary async form execution function that integrates
/// with the FormBackend trait abstraction, enabling support for multiple
/// rendering backends (CLI, TUI, Web).
///
/// # Arguments
///
/// * `form` - The parsed form definition
/// * `backend` - A mutable reference to the form backend implementation
/// * `i18n_bundle` - Optional I18n bundle for translating form content
/// * `base_dir` - Base directory for resolving relative paths in includes
///
/// # Returns
///
/// A HashMap containing all field results (name -> value)
/// Execute form with i18n support (sin dos fases - este era el que funcionaba)
pub async fn execute_with_backend_i18n(
    form: FormDefinition,
    backend: &mut dyn crate::backends::FormBackend,
    i18n_bundle: Option<&I18nBundle>,
    base_dir: &Path,
) -> Result<HashMap<String, serde_json::Value>> {
    execute_with_backend_i18n_with_defaults(form, backend, i18n_bundle, base_dir, None).await
}

/// Execute form with i18n support and optional initial values
pub async fn execute_with_backend_i18n_with_defaults(
    form: FormDefinition,
    backend: &mut dyn crate::backends::FormBackend,
    i18n_bundle: Option<&I18nBundle>,
    base_dir: &Path,
    initial_values: Option<HashMap<String, serde_json::Value>>,
) -> Result<HashMap<String, serde_json::Value>> {
    use crate::backends::RenderContext;

    // Store initial values for later merging before unwrap_or_default
    let initial_backup = initial_values.clone();
    let mut results: Arc<HashMap<String, serde_json::Value>> =
        Arc::new(initial_values.unwrap_or_default());

    // Initialize backend
    backend.initialize().await?;

    let element_list = build_element_list(&form);

    // Check display mode and execute accordingly
    if form.display_mode == DisplayMode::Complete {
        // Complete mode: show all fields at once
        // Filter items by 'when' condition
        let items: Vec<&DisplayItem> = element_list
            .iter()
            .filter_map(|(_, e)| match e {
                FormElement::Item(item) => {
                    // Evaluate 'when' condition if present
                    if let Some(condition) = &item.when {
                        if !evaluate_condition(condition, &results) {
                            return None;
                        }
                    }
                    Some(item.as_ref())
                }
                _ => None,
            })
            .collect();

        // Filter fields by 'when' condition
        let fields: Vec<&FieldDefinition> = element_list
            .iter()
            .filter_map(|(_, e)| match e {
                FormElement::Field(field) => {
                    // Evaluate 'when' condition if present
                    if let Some(condition) = &field.when {
                        if !evaluate_condition(condition, &results) {
                            return None;
                        }
                    }
                    Some(field.as_ref())
                }
                _ => None,
            })
            .collect();

        let items_owned: Vec<DisplayItem> = items
            .iter()
            .map(|i| translate_display_item(i, i18n_bundle))
            .collect();
        let fields_owned: Vec<FieldDefinition> = fields
            .iter()
            .map(|f| translate_field_definition(f, i18n_bundle))
            .collect();

        let mut complete_results = backend
            .execute_form_complete(&form, base_dir, items_owned, fields_owned, initial_backup)
            .await?;
        apply_when_false_defaults(&element_list, &mut complete_results);
        results = Arc::new(complete_results);
    } else {
        // Field-by-field mode
        for (_, element) in element_list.iter() {
            match element {
                FormElement::Item(item) => {
                    let context = RenderContext {
                        results: Arc::clone(&results),
                        locale: None,
                    };
                    let translated_item = translate_display_item(item, i18n_bundle);
                    backend
                        .render_display_item(&translated_item, &context)
                        .await?;
                }
                FormElement::Field(field) => {
                    if let Some(condition) = &field.when {
                        if !evaluate_condition(condition, &results) {
                            if field.when_false == WhenFalse::Default {
                                if let Some(default) = &field.default {
                                    Arc::make_mut(&mut results)
                                        .insert(field.name.clone(), serde_json::json!(default));
                                }
                            }
                            continue;
                        }
                    }

                    let context = RenderContext {
                        results: Arc::clone(&results),
                        locale: None,
                    };
                    let translated_field = translate_field_definition(field, i18n_bundle);
                    let value = backend.execute_field(&translated_field, &context).await?;
                    Arc::make_mut(&mut results).insert(field.name.clone(), value);
                }
            }
        }
    }

    // Shutdown backend
    backend.shutdown().await?;

    Ok(Arc::try_unwrap(results).unwrap_or_else(|arc| (*arc).clone()))
}

/// Execute a form using a specific backend (backward compatible wrapper)
///
/// This is a convenience wrapper around `execute_with_backend_i18n` that
/// doesn't use i18n translation. For i18n support, use `execute_with_backend_i18n`.
///
/// # Arguments
///
/// * `form` - The parsed form definition
/// * `backend` - A mutable reference to the form backend implementation
///
/// # Returns
///
/// A HashMap containing all field results (name -> value)
pub async fn execute_with_backend(
    form: FormDefinition,
    backend: &mut dyn crate::backends::FormBackend,
) -> Result<HashMap<String, serde_json::Value>> {
    execute_with_backend_i18n(form, backend, None, Path::new(".")).await
}

pub async fn execute_with_backend_from_dir(
    form: FormDefinition,
    backend: &mut dyn crate::backends::FormBackend,
    base_dir: &Path,
) -> Result<HashMap<String, serde_json::Value>> {
    execute_with_backend_i18n(form, backend, None, base_dir).await
}
