use anyhow::Result;

use crate::menu_utils::{ConfirmResult, FzfResult, FzfSelectable, FzfWrapper, Header, MenuCursor};
use crate::ui::catppuccin::{colors, format_back_icon, format_icon_colored, fzf_mocha_args};
use crate::ui::nerd_font::NerdFont;
use crate::ui::preview::PreviewBuilder;

use super::editors;
use super::state::EditState;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Flow {
    Continue,
    Exit,
}

/// Menu action types
#[derive(Debug, Clone)]
pub enum MenuAction {
    EditName,
    EditDescription,
    EditLaunchCommand,
    EditSavePath,
    Save,
    Back,
}

impl std::fmt::Display for MenuAction {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            MenuAction::EditName => write!(f, "edit_name"),
            MenuAction::EditDescription => write!(f, "edit_description"),
            MenuAction::EditLaunchCommand => write!(f, "edit_launch_command"),
            MenuAction::EditSavePath => write!(f, "edit_save_path"),
            MenuAction::Save => write!(f, "save"),
            MenuAction::Back => write!(f, "back"),
        }
    }
}

/// Menu item with display text, preview, and action
#[derive(Debug, Clone)]
pub struct MenuItem {
    display: String,
    preview: String,
    action: MenuAction,
}

impl MenuItem {
    fn new(display: String, preview: String, action: MenuAction) -> Self {
        Self {
            display,
            preview,
            action,
        }
    }
}

impl FzfSelectable for MenuItem {
    fn fzf_display_text(&self) -> String {
        self.display.clone()
    }

    fn fzf_key(&self) -> String {
        self.action.to_string()
    }

    fn fzf_preview(&self) -> crate::menu::protocol::FzfPreview {
        crate::menu::protocol::FzfPreview::Text(self.preview.clone())
    }
}

/// Run the main edit menu loop
pub fn run_edit_menu(game_name: &str, state: &mut EditState) -> Result<()> {
    let mut cursor = MenuCursor::new();

    loop {
        let menu_items = build_menu_items(state);

        let mut builder = FzfWrapper::builder()
            .header(Header::fancy(&format!("Editing: {}", game_name)))
            .prompt("Select property to edit")
            .args(fzf_mocha_args());

        if let Some(index) = cursor.initial_index(&menu_items) {
            builder = builder.initial_index(index);
        }

        let selection = builder.select_padded(menu_items.clone())?;

        match selection {
            FzfResult::Selected(item) => {
                cursor.update(&item, &menu_items);
                if handle_menu_action(item.action, state)? == Flow::Exit {
                    return Ok(());
                }
            }
            FzfResult::Cancelled => {
                if handle_cancel(state)? == Flow::Exit {
                    return Ok(());
                }
            }
            _ => return Ok(()),
        }
    }
}

fn handle_menu_action(action: MenuAction, state: &mut EditState) -> Result<Flow> {
    match action {
        MenuAction::EditName => {
            if editors::edit_name(state)? {
                state.mark_dirty();
            }
        }
        MenuAction::EditDescription => {
            if editors::edit_description(state)? {
                state.mark_dirty();
            }
        }
        MenuAction::EditLaunchCommand => {
            if editors::edit_launch_command(state)? {
                state.mark_dirty();
            }
        }
        MenuAction::EditSavePath => {
            if state.installation_index.is_some() {
                if editors::edit_save_path(state)? {
                    state.mark_dirty();
                }
            } else {
                println!(
                    "{} No installation found for this game on this device.",
                    char::from(NerdFont::Warning)
                );
            }
        }
        MenuAction::Save => {
            if state.is_dirty() {
                state.save()?;
            } else {
                println!("{} No changes to save.", char::from(NerdFont::Info));
            }
            // Stay in menu after saving
        }
        MenuAction::Back => {
            if state.is_dirty() {
                if confirm_discard_changes()? {
                    println!(
                        "{} Exited without saving changes.",
                        char::from(NerdFont::Info)
                    );
                    return Ok(Flow::Exit);
                }
            } else {
                return Ok(Flow::Exit);
            }
        }
    }
    Ok(Flow::Continue)
}

/// Handle cancel/escape from main menu
fn handle_cancel(state: &EditState) -> Result<Flow> {
    if state.is_dirty() {
        if confirm_discard_changes()? {
            println!(
                "{} Exited without saving changes.",
                char::from(NerdFont::Info)
            );
            return Ok(Flow::Exit);
        }
    } else {
        return Ok(Flow::Exit);
    }
    Ok(Flow::Continue)
}

/// Confirm discarding unsaved changes
fn confirm_discard_changes() -> Result<bool> {
    let result = FzfWrapper::builder()
        .confirm("You have unsaved changes. Exit without saving?")
        .yes_text("Exit Without Saving")
        .no_text("Go Back")
        .confirm_dialog()?;

    Ok(result == ConfirmResult::Yes)
}

/// Build menu items based on current state
fn build_menu_items(state: &EditState) -> Vec<MenuItem> {
    let game = state.game();
    let installation = state.installation();

    let mut items = Vec::new();

    // Name
    items.push(MenuItem::new(
        format!(
            "{} Name: {}",
            format_icon_colored(NerdFont::Edit, colors::BLUE),
            game.name.0
        ),
        format!(
            "Current name: {}\n\nEdit the game's name in games.toml",
            game.name.0
        ),
        MenuAction::EditName,
    ));

    // Description
    let desc_display = game.description.as_deref().unwrap_or("<not set>");
    items.push(MenuItem::new(
        format!(
            "{} Description: {}",
            format_icon_colored(NerdFont::Info, colors::TEAL),
            desc_display
        ),
        format!(
            "Current description: {}\n\nEdit the game's description in games.toml",
            desc_display
        ),
        MenuAction::EditDescription,
    ));

    // Launch Command
    let game_cmd = game.launch_command.as_deref();
    let inst_cmd = installation.and_then(|i| i.launch_command.as_deref());

    let (effective_cmd, cmd_source) = if let Some(cmd) = inst_cmd {
        (cmd, "installations.toml (device-specific override)")
    } else if let Some(cmd) = game_cmd {
        (cmd, "games.toml (shared)")
    } else {
        ("<not set>", "not configured")
    };

    let launch_preview = PreviewBuilder::new()
        .header(NerdFont::Rocket, "Launch Command")
        .field("Effective command", effective_cmd)
        .field("Source", cmd_source)
        .blank()
        .separator()
        .blank()
        .field("games.toml", game_cmd.unwrap_or("<not set>"))
        .field("installations.toml", inst_cmd.unwrap_or("<not set>"))
        .blank()
        .subtext("The installation-specific command overrides the shared command if both are set.")
        .build_string();

    items.push(MenuItem::new(
        format!(
            "{} Launch Command: {}",
            format_icon_colored(NerdFont::Rocket, colors::GREEN),
            effective_cmd
        ),
        launch_preview,
        MenuAction::EditLaunchCommand,
    ));

    // Save Path (only if installation exists)
    if let Some(inst) = installation {
        let save_path_str = inst
            .save_path
            .to_tilde_string()
            .unwrap_or_else(|_| inst.save_path.as_path().to_string_lossy().to_string());

        let save_preview = PreviewBuilder::new()
            .header(NerdFont::Folder, "Save Path")
            .field("Path", &save_path_str)
            .blank()
            .subtext("Edit the save path in installations.toml (device-specific)")
            .build_string();

        items.push(MenuItem::new(
            format!(
                "{} Save Path: {}",
                format_icon_colored(NerdFont::Folder, colors::LAVENDER),
                save_path_str
            ),
            save_preview,
            MenuAction::EditSavePath,
        ));
    }

    // Actions
    items.push(MenuItem::new(
        format!(
            "{} Save",
            format_icon_colored(NerdFont::Check, colors::GREEN)
        ),
        "Save all changes (stay in menu)".to_string(),
        MenuAction::Save,
    ));

    items.push(MenuItem::new(
        format!("{} Back", format_back_icon()),
        "Return to game menu (warns if unsaved changes)".to_string(),
        MenuAction::Back,
    ));

    items
}
