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

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

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

pub(super) fn resolve_storage_sub(s: &str) -> StorageSub {
    match s {
        "get" => StorageSub::Get,
        "set" => StorageSub::Set,
        "clear" => StorageSub::Clear,
        _ => StorageSub::Unknown,
    }
}

pub async fn dispatch(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sub = parts.first().copied().unwrap_or("");
    match resolve_storage_sub(sub) {
        StorageSub::Get => get(session, &parts[1..], mode).await,
        StorageSub::Set => set(session, &parts[1..], mode).await,
        StorageSub::Clear => clear(session, &parts[1..], mode).await,
        StorageSub::Unknown => output_err(
            mode,
            &format!(
                "Unknown storage subcommand: {sub}. Use 'get <local|session> <key>', 'set <local|session> <key> <value>', or 'clear <local|session>'"
            ),
        ),
    }
}

fn parse_kind(s: &str) -> Option<StorageKind> {
    match s {
        "local" => Some(StorageKind::Local),
        "session" => Some(StorageKind::Session),
        _ => None,
    }
}

async fn get(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    if parts.is_empty() {
        output_err(mode, "Usage: storage get <local|session> [key]");
        return;
    }
    let kind_str = parts[0];
    let kind = match parse_kind(kind_str) {
        Some(k) => k,
        None => {
            output_err(
                mode,
                &format!("Invalid storage kind: {kind_str}. Use 'local' or 'session'"),
            );
            return;
        }
    };
    let key = parts.get(1).copied();
    match session.storage_get(kind, key).await {
        Ok(val) => {
            let human = val.to_string();
            output_ok(
                mode,
                &human,
                json!({"value": val, "storage": kind_str, "key": key}),
            );
        }
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn set(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    if parts.len() < 3 {
        output_err(mode, "Usage: storage set <local|session> <key> <value>");
        return;
    }
    let kind_str = parts[0];
    let kind = match parse_kind(kind_str) {
        Some(k) => k,
        None => {
            output_err(
                mode,
                &format!("Invalid storage kind: {kind_str}. Use 'local' or 'session'"),
            );
            return;
        }
    };
    let key = parts[1];
    let value = parts[2..].join(" ");
    match session.storage_set(kind, key, &value).await {
        Ok(msg) => output_ok(
            mode,
            &msg,
            json!({"action": "storage_set", "storage": kind_str, "key": key, "value": value}),
        ),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn clear(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let kind_str = parts.first().copied().unwrap_or("").trim();
    if kind_str.is_empty() {
        output_err(mode, "Usage: storage clear <local|session>");
        return;
    }
    let kind = match parse_kind(kind_str) {
        Some(k) => k,
        None => {
            output_err(
                mode,
                &format!("Invalid storage kind: {kind_str}. Use 'local' or 'session'"),
            );
            return;
        }
    };
    match session.storage_clear(kind).await {
        Ok(msg) => output_ok(
            mode,
            &msg,
            json!({"action": "storage_clear", "storage": kind_str}),
        ),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

#[cfg(test)]
mod tests {
    use super::{parse_kind, resolve_storage_sub, StorageSub};
    use night_fury_core::StorageKind;

    #[test]
    fn resolves_known_subcommands() {
        assert_eq!(resolve_storage_sub("get"), StorageSub::Get);
        assert_eq!(resolve_storage_sub("set"), StorageSub::Set);
        assert_eq!(resolve_storage_sub("clear"), StorageSub::Clear);
    }

    #[test]
    fn unknown_subcommand_returns_unknown() {
        assert_eq!(resolve_storage_sub(""), StorageSub::Unknown);
        assert_eq!(resolve_storage_sub("delete"), StorageSub::Unknown);
        assert_eq!(resolve_storage_sub("GET"), StorageSub::Unknown);
    }

    #[test]
    fn parse_kind_accepts_local_and_session() {
        assert!(matches!(parse_kind("local"), Some(StorageKind::Local)));
        assert!(matches!(parse_kind("session"), Some(StorageKind::Session)));
    }

    #[test]
    fn parse_kind_rejects_unknown() {
        assert!(parse_kind("").is_none());
        assert!(parse_kind("Local").is_none()); // case-sensitive
        assert!(parse_kind("cookies").is_none());
    }
}
