use night_fury_core::{BrowserSession, SessionData};
use serde_json::json;

use night_fury_cli_core::output::{output_err, output_ok, OutputMode};

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(super) enum SessionSub {
    Save,
    Load,
    Unknown,
}

pub(super) fn resolve_session_sub(s: &str) -> SessionSub {
    match s {
        "save" => SessionSub::Save,
        "load" => SessionSub::Load,
        _ => SessionSub::Unknown,
    }
}

pub async fn dispatch(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sub = parts.first().copied().unwrap_or("");
    match resolve_session_sub(sub) {
        SessionSub::Save => save(session, &parts[1..], mode).await,
        SessionSub::Load => load(session, &parts[1..], mode).await,
        SessionSub::Unknown => output_err(
            mode,
            &format!("Unknown session subcommand: {sub}. Use 'save [file]' or 'load <file>'"),
        ),
    }
}

async fn save(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let path = parts
        .first()
        .copied()
        .filter(|s| !s.is_empty())
        .unwrap_or("/tmp/nf-session.json");
    match session.save_session().await {
        Ok(data) => match serde_json::to_string_pretty(&data) {
            Ok(json_str) => match std::fs::write(path, &json_str) {
                Ok(()) => output_ok(
                    mode,
                    &format!("Session saved to {path}"),
                    json!({"action": "session_save", "file": path}),
                ),
                Err(e) => output_err(mode, &format!("Failed to write {path}: {e}")),
            },
            Err(e) => output_err(mode, &format!("Failed to serialize session: {e}")),
        },
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn load(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let path = parts.first().copied().unwrap_or("").trim();
    if path.is_empty() {
        output_err(mode, "Usage: session load <file>");
        return;
    }
    match std::fs::read_to_string(path) {
        Ok(contents) => match serde_json::from_str::<SessionData>(&contents) {
            Ok(data) => match session.restore_session(data).await {
                Ok(msg) => output_ok(mode, &msg, json!({"action": "session_load", "file": path})),
                Err(e) => output_err(mode, &e.to_string()),
            },
            Err(e) => output_err(mode, &format!("Failed to parse {path}: {e}")),
        },
        Err(e) => output_err(mode, &format!("Failed to read {path}: {e}")),
    }
}

#[cfg(test)]
mod tests {
    use super::{resolve_session_sub, SessionSub};

    #[test]
    fn resolves_known_subcommands() {
        assert_eq!(resolve_session_sub("save"), SessionSub::Save);
        assert_eq!(resolve_session_sub("load"), SessionSub::Load);
    }

    #[test]
    fn unknown_subcommand_returns_unknown() {
        assert_eq!(resolve_session_sub(""), SessionSub::Unknown);
        assert_eq!(resolve_session_sub("delete"), SessionSub::Unknown);
    }
}
