//! Interactive prompt functions
//!
//! Provides high-level functions for all prompt types with automatic
//! fallback to stdin when interactive mode isn't available.

use crate::error::{ErrorWrapper, Result};
use chrono::{NaiveDate, Weekday};
use inquire::{
    Confirm, DateSelect, Editor as InquireEditor, MultiSelect, Password, PasswordDisplayMode,
    Select, Text,
};
use std::io::{Read, Write};
use std::process::Command;
use tempfile::NamedTempFile;

/// Prompt for text input
///
/// # Arguments
///
/// * `prompt` - The prompt message
/// * `default` - Optional default value
/// * `placeholder` - Optional placeholder text
///
/// # Example
///
/// ```no_run
/// # use typedialog_core::prompts;
/// let name = prompts::text("Enter name", None, None)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn text(prompt: &str, default: Option<&str>, placeholder: Option<&str>) -> Result<String> {
    let mut text_prompt = Text::new(prompt);

    if let Some(def) = default {
        text_prompt = text_prompt.with_default(def);
    }
    if let Some(ph) = placeholder {
        text_prompt = text_prompt.with_placeholder(ph);
    }

    match text_prompt.prompt() {
        Ok(result) => Ok(result),
        Err(_e) => {
            eprintln!("Note: Interactive mode not available, using stdin input");
            stdin_text(prompt, default)
        }
    }
}

/// Prompt for confirmation (yes/no)
///
/// # Arguments
///
/// * `prompt` - The prompt message
/// * `default` - Optional default value
/// * `formatter` - Optional custom formatter (format: "true_text|false_text", e.g., "sim|não")
///
/// # Example
///
/// ```no_run
/// # use typedialog_core::prompts;
/// let confirmed = prompts::confirm("Continue?", None, None)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn confirm(prompt: &str, default: Option<bool>, _formatter: Option<&str>) -> Result<bool> {
    let mut confirm_prompt = Confirm::new(prompt);

    if let Some(def) = default {
        confirm_prompt = confirm_prompt.with_default(def);
    }

    // Apply default formatter based on inquire's standard
    // Custom formatter from CLI is handled in main.rs after getting the boolean result
    confirm_prompt = confirm_prompt.with_formatter(&|ans| match ans {
        true => "yes".to_owned(),
        false => "no".to_owned(),
    });

    match confirm_prompt.prompt() {
        Ok(result) => Ok(result),
        Err(_e) => {
            eprintln!("Note: Interactive mode not available, using stdin input");
            stdin_confirm(prompt, default)
        }
    }
}

/// Prompt for password input (masked by default)
///
/// # Arguments
///
/// * `prompt` - The prompt message
/// * `with_toggle` - Allow showing/hiding password with Ctrl+R
///
/// # Example
///
/// ```no_run
/// # use typedialog_core::prompts;
/// let password = prompts::password("Enter password", false)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn password(prompt: &str, with_toggle: bool) -> Result<String> {
    let mut password_prompt = Password::new(prompt).with_display_mode(PasswordDisplayMode::Masked);

    if with_toggle {
        password_prompt = password_prompt
            .with_display_toggle_enabled()
            .with_help_message("Press Ctrl+R to toggle password visibility");
    }

    match password_prompt.prompt() {
        Ok(result) => Ok(result),
        Err(_e) => {
            eprintln!("Note: Interactive mode not available, using stdin input");
            stdin_password(prompt)
        }
    }
}

/// Prompt for single selection from a list
///
/// # Arguments
///
/// * `prompt` - The prompt message
/// * `options` - List of options to choose from
/// * `page_size` - Optional number of options per page
/// * `vim_mode` - Enable vim mode navigation
///
/// # Example
///
/// ```no_run
/// # use typedialog_core::prompts;
/// let choice = prompts::select(
///     "Choose role",
///     vec!["Admin".to_string(), "User".to_string()],
///     None,
///     false,
/// )?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn select(
    prompt: &str,
    options: Vec<String>,
    page_size: Option<usize>,
    vim_mode: bool,
) -> Result<String> {
    let mut select = Select::new(prompt, options);

    if let Some(size) = page_size {
        select = select.with_page_size(size);
    }

    if vim_mode {
        select = select.with_vim_mode(true);
    }

    match select.prompt() {
        Ok(result) => Ok(result),
        Err(_e) => {
            eprintln!("Note: Interactive mode not available, using stdin input");
            stdin_select(prompt)
        }
    }
}

/// Prompt for multiple selections from a list
///
/// # Arguments
///
/// * `prompt` - The prompt message
/// * `options` - List of options to choose from
/// * `page_size` - Optional number of options per page
/// * `vim_mode` - Enable vim mode navigation
///
/// # Example
///
/// ```no_run
/// # use typedialog_core::prompts;
/// let choices = prompts::multi_select(
///     "Choose services",
///     vec!["nginx".to_string(), "postgres".to_string()],
///     None,
///     false,
/// )?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn multi_select(
    prompt: &str,
    options: Vec<String>,
    page_size: Option<usize>,
    vim_mode: bool,
) -> Result<Vec<String>> {
    let mut multi_select = MultiSelect::new(prompt, options);

    if let Some(size) = page_size {
        multi_select = multi_select.with_page_size(size);
    }

    if vim_mode {
        multi_select = multi_select.with_vim_mode(true);
    }

    match multi_select.prompt() {
        Ok(results) => Ok(results),
        Err(_e) => {
            eprintln!("Note: Interactive mode not available, using stdin input");
            stdin_multi_select(prompt)
        }
    }
}

/// Prompt for text using an external editor
///
/// # Arguments
///
/// * `prompt` - The prompt message
/// * `file_extension` - Optional file extension for the temp file (for syntax highlighting)
/// * `default_content` - Optional default content (text or loaded from file)
///
/// # Example
///
/// ```no_run
/// # use typedialog_core::prompts;
/// // With literal text template
/// let text = prompts::editor("Enter description", Some("md"), Some("# Title\n\n## Section\n"))?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn editor(
    prompt: &str,
    file_extension: Option<&str>,
    prefix_text: Option<&str>,
) -> Result<String> {
    let mut editor = InquireEditor::new(prompt);

    if let Some(ext) = file_extension {
        editor = editor.with_file_extension(ext);
    }

    if let Some(text) = prefix_text {
        editor = editor.with_predefined_text(text);
    }

    // Formatter to show truncated preview of submitted text
    editor = editor.with_formatter(&|text| {
        if text.is_empty() {
            "<skipped>".to_string()
        } else if text.len() <= 50 {
            text.to_string()
        } else {
            format!("{}...", &text[..47])
        }
    });

    match editor.prompt() {
        Ok(result) => Ok(result),
        Err(_e) => {
            eprintln!("Note: Interactive mode not available, using fallback editor");
            stdin_editor(prompt, prefix_text, file_extension)
        }
    }
}

/// Prompt for date selection using a calendar
///
/// # Arguments
///
/// * `prompt` - The prompt message
/// * `default` - Optional default date (YYYY-MM-DD format)
/// * `min_date` - Optional minimum date (YYYY-MM-DD format)
/// * `max_date` - Optional maximum date (YYYY-MM-DD format)
/// * `week_start` - Week start day (Mon, Tue, Wed, Thu, Fri, Sat, Sun)
///
/// # Example
///
/// ```no_run
/// # use typedialog_core::prompts;
/// let date = prompts::date("Select date", None, None, None, "Mon")?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn date(
    prompt: &str,
    default: Option<&str>,
    min_date: Option<&str>,
    max_date: Option<&str>,
    week_start: &str,
) -> Result<String> {
    let mut date_select = DateSelect::new(prompt);

    if let Some(def) = default {
        if let Ok(parsed) = NaiveDate::parse_from_str(def, "%Y-%m-%d") {
            date_select = date_select.with_default(parsed);
        }
    }

    if let Some(min) = min_date {
        if let Ok(parsed) = NaiveDate::parse_from_str(min, "%Y-%m-%d") {
            date_select = date_select.with_min_date(parsed);
        }
    }

    if let Some(max) = max_date {
        if let Ok(parsed) = NaiveDate::parse_from_str(max, "%Y-%m-%d") {
            date_select = date_select.with_max_date(parsed);
        }
    }

    // Parse week start day
    let weekday = match week_start.to_lowercase().as_str() {
        "mon" => Weekday::Mon,
        "tue" => Weekday::Tue,
        "wed" => Weekday::Wed,
        "thu" => Weekday::Thu,
        "fri" => Weekday::Fri,
        "sat" => Weekday::Sat,
        "sun" => Weekday::Sun,
        _ => Weekday::Mon, // Default to Monday
    };

    date_select = date_select.with_week_start(weekday);

    match date_select.prompt() {
        Ok(result) => Ok(result.format("%Y-%m-%d").to_string()),
        Err(_e) => {
            eprintln!("Note: Interactive mode not available, using stdin input");
            stdin_date(prompt, default)
        }
    }
}

/// Prompt for custom type with validation
///
/// # Arguments
///
/// * `prompt` - The prompt message
/// * `type_name` - Type name (i32, u16, f64, ipv4, ipv6, etc.)
/// * `default` - Optional default value
///
/// # Example
///
/// ```no_run
/// # use typedialog_core::prompts;
/// let port = prompts::custom("Enter port", "u16", None)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn custom(prompt: &str, type_name: &str, default: Option<&str>) -> Result<String> {
    let validator = |input: &str| match type_name {
        "i32" => input.parse::<i32>().is_ok(),
        "i64" => input.parse::<i64>().is_ok(),
        "u16" => input.parse::<u16>().is_ok(),
        "u32" => input.parse::<u32>().is_ok(),
        "u64" => input.parse::<u64>().is_ok(),
        "f32" => input.parse::<f32>().is_ok(),
        "f64" => input.parse::<f64>().is_ok(),
        "ipv4" => input.parse::<std::net::Ipv4Addr>().is_ok(),
        "ipv6" => input.parse::<std::net::Ipv6Addr>().is_ok(),
        _ => true,
    };

    let placeholder_msg = format!("Expected type: {}", type_name);
    let mut text_prompt = Text::new(prompt);

    if let Some(def) = default {
        text_prompt = text_prompt.with_default(def);
    }

    text_prompt = text_prompt.with_placeholder(&placeholder_msg);

    match text_prompt.prompt() {
        Ok(result) => {
            if validator(&result) {
                Ok(result)
            } else {
                Err(ErrorWrapper::validation_failed(format!(
                    "Invalid input for type {}",
                    type_name
                )))
            }
        }
        Err(_e) => {
            eprintln!("Note: Interactive mode not available, using stdin input");
            stdin_custom(prompt, type_name, default, &validator)
        }
    }
}

// ============================================================================
// Stdin Fallback Implementations
// ============================================================================

fn stdin_text(prompt: &str, default: Option<&str>) -> Result<String> {
    println!("{}", prompt);
    if let Some(def) = default {
        print!("[{}]: ", def);
    } else {
        print!(": ");
    }
    std::io::stdout().flush()?;

    let mut input = String::new();
    std::io::stdin().read_line(&mut input)?;

    let trimmed = input.trim();
    if trimmed.is_empty() {
        Ok(default.unwrap_or("").to_string())
    } else {
        Ok(trimmed.to_string())
    }
}

fn stdin_confirm(prompt: &str, default: Option<bool>) -> Result<bool> {
    let default_str = match default {
        Some(true) => "Y/n",
        Some(false) => "y/N",
        None => "y/n",
    };

    println!("{} [{}]: ", prompt, default_str);
    std::io::stdout().flush()?;

    let mut input = String::new();
    std::io::stdin().read_line(&mut input)?;

    match input.trim().to_lowercase().as_str() {
        "y" | "yes" | "true" => Ok(true),
        "n" | "no" | "false" => Ok(false),
        "" => Ok(default.unwrap_or(false)),
        _ => Err(ErrorWrapper::validation_failed("Please answer yes or no")),
    }
}

fn stdin_password(prompt: &str) -> Result<String> {
    // Try rpassword first (requires real TTY), fall back to simple read if not available
    let prompt_with_suffix = format!("{}: ", prompt);

    match rpassword::prompt_password(&prompt_with_suffix) {
        Ok(pwd) => Ok(pwd),
        Err(_) => {
            // Fallback for piped input (no TTY available)
            // Show cursor at the beginning of line
            println!("{}", prompt);
            print!("> ");
            std::io::stdout().flush()?;

            let mut input = String::new();
            std::io::stdin().read_line(&mut input)?;
            Ok(input.trim().to_string())
        }
    }
}

fn stdin_select(prompt: &str) -> Result<String> {
    println!("{}", prompt);
    print!("Enter selection: ");
    std::io::stdout().flush()?;

    let mut input = String::new();
    std::io::stdin().read_line(&mut input)?;

    Ok(input.trim().to_string())
}

fn stdin_multi_select(prompt: &str) -> Result<Vec<String>> {
    println!("{}", prompt);
    print!("Enter selections (comma-separated): ");
    std::io::stdout().flush()?;

    let mut input = String::new();
    std::io::stdin().read_line(&mut input)?;

    let selections: Vec<String> = input
        .trim()
        .split(',')
        .map(|s| s.trim().to_string())
        .filter(|s| !s.is_empty())
        .collect();

    Ok(selections)
}

fn stdin_editor(
    prompt: &str,
    prefix_text: Option<&str>,
    file_extension: Option<&str>,
) -> Result<String> {
    println!("{}", prompt);

    // Try to open a temporary file with the editor
    if let Ok(content) = open_editor_with_temp_file(prefix_text, file_extension) {
        return Ok(content);
    }

    // Fallback to stdin-based editing if temp file approach fails
    println!("\n[Unable to open editor, using stdin mode]");
    println!("Paste your content below (Ctrl+D or Ctrl+Z to finish):");

    // Start with prefix text if provided
    let mut content = if let Some(text) = prefix_text {
        text.replace("\\n", "\n")
    } else {
        String::new()
    };

    if !content.is_empty() {
        println!("\nPre-filled content:");
        println!("{}", content);
        println!();
    }

    print!("> ");
    std::io::stdout().flush()?;

    let mut input = String::new();
    std::io::stdin().read_to_string(&mut input)?;

    // Combine prefix text with user input
    content.push_str(&input);

    Ok(content.trim_end().to_string())
}

/// Open a temporary file with the system editor
fn open_editor_with_temp_file(
    prefix_text: Option<&str>,
    file_extension: Option<&str>,
) -> Result<String> {
    // Create temporary file with appropriate extension
    let mut temp_file = if let Some(ext) = file_extension {
        tempfile::Builder::new()
            .suffix(&format!(".{}", ext))
            .tempfile()
            .map_err(ErrorWrapper::io)?
    } else {
        NamedTempFile::new().map_err(ErrorWrapper::io)?
    };

    // Write prefix text to temp file
    if let Some(text) = prefix_text {
        let content = text.replace("\\n", "\n");
        temp_file
            .write_all(content.as_bytes())
            .map_err(ErrorWrapper::io)?;
        temp_file.flush().map_err(ErrorWrapper::io)?;
    }

    let path = temp_file.path().to_path_buf();

    // Get editor command from environment, with sensible defaults
    let editor = std::env::var("EDITOR")
        .or_else(|_| std::env::var("VISUAL"))
        .unwrap_or_else(|_| {
            if cfg!(windows) {
                "notepad".to_string()
            } else {
                "nano".to_string()
            }
        });

    // Open the file with the editor
    let status = Command::new(&editor)
        .arg(&path)
        .status()
        .map_err(ErrorWrapper::io)?;

    if !status.success() {
        return Err(ErrorWrapper::validation_failed(format!(
            "Editor '{}' exited with error code",
            editor
        )));
    }

    // Read the edited content back
    let content = std::fs::read_to_string(&path).map_err(ErrorWrapper::io)?;

    Ok(content)
}

fn stdin_date(prompt: &str, default: Option<&str>) -> Result<String> {
    println!("{}", prompt);
    if let Some(def) = default {
        print!("[{}] (YYYY-MM-DD): ", def);
    } else {
        print!("(YYYY-MM-DD): ");
    }
    std::io::stdout().flush()?;

    let mut input = String::new();
    std::io::stdin().read_line(&mut input)?;

    let trimmed = input.trim();
    if trimmed.is_empty() {
        Ok(default.unwrap_or("").to_string())
    } else {
        // Validate date format
        NaiveDate::parse_from_str(trimmed, "%Y-%m-%d")?;
        Ok(trimmed.to_string())
    }
}

fn stdin_custom<F>(
    prompt: &str,
    type_name: &str,
    default: Option<&str>,
    validator: &F,
) -> Result<String>
where
    F: Fn(&str) -> bool,
{
    loop {
        println!("{}", prompt);
        if let Some(def) = default {
            print!("[{}]: ", def);
        } else {
            print!(": ");
        }
        std::io::stdout().flush()?;

        let mut input = String::new();
        std::io::stdin().read_line(&mut input)?;

        let trimmed = input.trim();
        if trimmed.is_empty() {
            if let Some(def) = default {
                return Ok(def.to_string());
            }
            continue;
        }

        if validator(trimmed) {
            return Ok(trimmed.to_string());
        } else {
            eprintln!("Invalid input for type {}", type_name);
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_stdlib_integration() {
        // Just verify the types and signatures are correct
        drop(Ok::<String, crate::error::ErrorWrapper>("test".to_string()));
    }
}
