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

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

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(super) enum RouteSub {
    Block,
    Fulfill,
    Capture,
    List,
    Clear,
    Unknown,
}

pub(super) fn resolve_route_sub(s: &str) -> RouteSub {
    match s {
        "block" => RouteSub::Block,
        "fulfill" => RouteSub::Fulfill,
        "capture" => RouteSub::Capture,
        "list" => RouteSub::List,
        "clear" => RouteSub::Clear,
        _ => RouteSub::Unknown,
    }
}

pub async fn dispatch(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sub = parts.first().copied().unwrap_or("");
    match resolve_route_sub(sub) {
        RouteSub::Block => block(session, &parts[1..], mode).await,
        RouteSub::Fulfill => fulfill(session, &parts[1..], mode).await,
        RouteSub::Capture => capture(session, &parts[1..], mode).await,
        RouteSub::List => list(session, mode).await,
        RouteSub::Clear => clear(session, mode).await,
        RouteSub::Unknown => output_err(
            mode,
            &format!(
                "Unknown route subcommand: {sub}. Use 'block', 'fulfill', 'capture', 'list', or 'clear'"
            ),
        ),
    }
}

async fn block(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let pattern = parts.first().copied().unwrap_or("").trim();
    if pattern.is_empty() {
        output_err(mode, "Usage: route block <pattern>");
        return;
    }
    match session.add_route(pattern, RouteAction::Block).await {
        Ok(msg) => output_ok(
            mode,
            &msg,
            json!({"action": "route_block", "pattern": pattern}),
        ),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn fulfill(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    if parts.len() < 3 {
        output_err(mode, "Usage: route fulfill <pattern> <status> <body>");
        return;
    }
    let pattern = parts[0];
    let status: u16 = match parts[1].parse() {
        Ok(s) => s,
        Err(_) => {
            output_err(
                mode,
                "Usage: route fulfill <pattern> <status> <body> — status must be an integer",
            );
            return;
        }
    };
    let body = parts[2..].join(" ");
    let action = RouteAction::Fulfill {
        status,
        headers: vec![],
        body: body.clone(),
    };
    match session.add_route(pattern, action).await {
        Ok(msg) => output_ok(
            mode,
            &msg,
            json!({"action": "route_fulfill", "pattern": pattern, "status": status, "body": body}),
        ),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn capture(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let pattern = parts.first().copied().unwrap_or("").trim();
    if pattern.is_empty() {
        output_err(mode, "Usage: route capture <pattern>");
        return;
    }
    match session.capture_responses(pattern).await {
        Ok(msg) => output_ok(
            mode,
            &msg,
            json!({"action": "route_capture", "pattern": pattern}),
        ),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn list(session: &BrowserSession, mode: OutputMode) {
    match session.get_captured_responses().await {
        Ok(responses) => {
            let count = responses.len();
            let mut lines = vec![format!("{count} captured response(s):")];
            for r in &responses {
                lines.push(format!("  [{}] {}", r.status, r.url));
            }
            let human = lines.join("\n");
            let data = responses
                .iter()
                .map(|r| {
                    json!({
                        "url": r.url,
                        "status": r.status,
                        "body": r.body,
                    })
                })
                .collect::<Vec<_>>();
            output_ok(mode, &human, json!({"responses": data, "count": count}));
        }
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn clear(session: &BrowserSession, mode: OutputMode) {
    match session.clear_routes().await {
        Ok(msg) => output_ok(mode, &msg, json!({"action": "route_clear"})),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

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

    #[test]
    fn resolves_known_subcommands() {
        assert_eq!(resolve_route_sub("block"), RouteSub::Block);
        assert_eq!(resolve_route_sub("fulfill"), RouteSub::Fulfill);
        assert_eq!(resolve_route_sub("capture"), RouteSub::Capture);
        assert_eq!(resolve_route_sub("list"), RouteSub::List);
        assert_eq!(resolve_route_sub("clear"), RouteSub::Clear);
    }

    #[test]
    fn unknown_subcommand_returns_unknown() {
        assert_eq!(resolve_route_sub(""), RouteSub::Unknown);
        assert_eq!(resolve_route_sub("continue"), RouteSub::Unknown); // core has ContinueWith but cli doesn't expose yet
    }
}
