use night_fury_core::BrowserSession;
use serde_json::json;

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

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(super) enum CookieSub {
    Get,
    Set,
    Delete,
    Clear,
    Unknown,
}

pub(super) fn resolve_cookie_sub(s: &str) -> CookieSub {
    match s {
        "get" | "list" => CookieSub::Get,
        "set" => CookieSub::Set,
        "delete" => CookieSub::Delete,
        "clear" => CookieSub::Clear,
        _ => CookieSub::Unknown,
    }
}

pub async fn dispatch(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sub = parts.first().copied().unwrap_or("get");
    match resolve_cookie_sub(sub) {
        CookieSub::Get => get(session, mode).await,
        CookieSub::Set => set(session, &parts[1..], mode).await,
        CookieSub::Delete => delete(session, &parts[1..], mode).await,
        CookieSub::Clear => clear(session, mode).await,
        CookieSub::Unknown => output_err(
            mode,
            &format!(
                "Unknown cookies subcommand: {sub}. Use 'get', 'set <file>', 'delete <name> <domain>', or 'clear'"
            ),
        ),
    }
}

async fn get(session: &BrowserSession, mode: OutputMode) {
    match session.get_cookies().await {
        Ok(cookies) => {
            let count = cookies.len();
            let mut lines = vec![format!("{count} cookies:")];
            for c in &cookies {
                let name = c.get("name").and_then(|v| v.as_str()).unwrap_or("?");
                let value = c.get("value").and_then(|v| v.as_str()).unwrap_or("");
                let domain = c.get("domain").and_then(|v| v.as_str()).unwrap_or("");
                let preview = if value.len() > 40 {
                    format!("{}…", &value[..40])
                } else {
                    value.to_string()
                };
                lines.push(format!("  {name} = {preview}  [{domain}]"));
            }
            let human = lines.join("\n");
            output_ok(mode, &human, json!({"cookies": cookies, "count": count}));
        }
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn set(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let path = parts.first().copied().unwrap_or("").trim();
    if path.is_empty() {
        output_err(mode, "Usage: cookies set <file>");
        return;
    }
    match night_fury_cli_core::cookie_loader::load_cookies(path) {
        Ok(cookies) => match session.set_cookies(cookies).await {
            Ok(msg) => output_ok(mode, &msg, json!({"action": "cookies_set", "file": path})),
            Err(e) => output_err(mode, &e.to_string()),
        },
        Err(e) => output_err(mode, &format!("Error loading {path}: {e}")),
    }
}

async fn delete(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let name = parts.first().copied().unwrap_or("").trim();
    let domain = parts.get(1).copied().unwrap_or("").trim();
    if name.is_empty() {
        output_err(mode, "Usage: cookies delete <name> [domain]");
        return;
    }
    match session.delete_cookie(name, domain).await {
        Ok(msg) => output_ok(
            mode,
            &msg,
            json!({"action": "cookie_delete", "name": name, "domain": domain}),
        ),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn clear(session: &BrowserSession, mode: OutputMode) {
    // Core has no clear_cookies — delete all by getting then deleting each
    match session.get_cookies().await {
        Ok(cookies) => {
            let count = cookies.len();
            let mut errors = vec![];
            for c in &cookies {
                let name = c.get("name").and_then(|v| v.as_str()).unwrap_or("");
                let domain = c.get("domain").and_then(|v| v.as_str()).unwrap_or("");
                if let Err(e) = session.delete_cookie(name, domain).await {
                    errors.push(e.to_string());
                }
            }
            if errors.is_empty() {
                output_ok(
                    mode,
                    &format!("Cleared {count} cookies"),
                    json!({"action": "cookies_clear", "count": count}),
                );
            } else {
                output_err(
                    mode,
                    &format!(
                        "Cleared with {} error(s): {}",
                        errors.len(),
                        errors.join("; ")
                    ),
                );
            }
        }
        Err(e) => output_err(mode, &e.to_string()),
    }
}

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

    #[test]
    fn resolves_known_subcommands() {
        assert_eq!(resolve_cookie_sub("get"), CookieSub::Get);
        assert_eq!(resolve_cookie_sub("list"), CookieSub::Get); // `list` is an alias
        assert_eq!(resolve_cookie_sub("set"), CookieSub::Set);
        assert_eq!(resolve_cookie_sub("delete"), CookieSub::Delete);
        assert_eq!(resolve_cookie_sub("clear"), CookieSub::Clear);
    }

    #[test]
    fn unknown_subcommand_returns_unknown() {
        assert_eq!(resolve_cookie_sub("gett"), CookieSub::Unknown);
        assert_eq!(resolve_cookie_sub(""), CookieSub::Unknown);
        assert_eq!(resolve_cookie_sub("LIST"), CookieSub::Unknown); // resolver is case-sensitive for subs
    }
}
