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

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

pub async fn dispatch(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sub = parts.first().copied().unwrap_or("");
    match sub {
        "text" => get_text(session, &parts[1..], mode).await,
        "attr" | "attribute" => get_attr(session, &parts[1..], mode).await,
        "html" => get_html(session, &parts[1..], mode).await,
        "value" => get_value(session, &parts[1..], mode).await,
        "count" => get_count(session, &parts[1..], mode).await,
        "box" => get_box(session, &parts[1..], mode).await,
        other => output_err(
            mode,
            &format!(
                "Unknown get subcommand: {other}. Use 'text', 'attr', 'html', 'value', 'count', or 'box'"
            ),
        ),
    }
}

async fn get_text(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sel = parts.join(" ");
    let sel = sel.trim();
    if sel.is_empty() {
        output_err(mode, "Usage: get text <css-selector>");
        return;
    }
    match session.get_text(sel).await {
        Ok(text) => output_ok(mode, &text, json!({"text": text, "selector": sel})),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn get_attr(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    if parts.len() < 2 {
        output_err(mode, "Usage: get attr <css-selector> <attribute-name>");
        return;
    }
    let sel = parts[..parts.len() - 1].join(" ");
    let attr = parts[parts.len() - 1];
    match session.get_attribute(sel.trim(), attr).await {
        Ok(val) => output_ok(
            mode,
            &val,
            json!({"value": val, "selector": sel, "attribute": attr}),
        ),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn get_html(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sel = parts.join(" ");
    let sel = sel.trim();
    if sel.is_empty() {
        output_err(mode, "Usage: get html <css-selector>");
        return;
    }
    match session.get_html(sel).await {
        Ok(html) => output_ok(mode, &html, json!({"html": html, "selector": sel})),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn get_value(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sel = parts.join(" ");
    let sel = sel.trim();
    if sel.is_empty() {
        output_err(mode, "Usage: get value <css-selector>");
        return;
    }
    match session.get_value(sel).await {
        Ok(val) => output_ok(mode, &val, json!({"value": val, "selector": sel})),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn get_count(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sel = parts.join(" ");
    let sel = sel.trim();
    if sel.is_empty() {
        output_err(mode, "Usage: get count <css-selector>");
        return;
    }
    match session.get_count(sel).await {
        Ok(count) => output_ok(
            mode,
            &format!("{count}"),
            json!({"count": count, "selector": sel}),
        ),
        Err(e) => output_err(mode, &e.to_string()),
    }
}

async fn get_box(session: &BrowserSession, parts: &[&str], mode: OutputMode) {
    let sel = parts.join(" ");
    let sel = sel.trim();
    if sel.is_empty() {
        output_err(mode, "Usage: get box <css-selector>");
        return;
    }
    match session.get_box(sel).await {
        Ok(b) => {
            let human = format!(
                "x={} y={} w={} h={}",
                b.get("x").and_then(|v| v.as_f64()).unwrap_or(0.0),
                b.get("y").and_then(|v| v.as_f64()).unwrap_or(0.0),
                b.get("width").and_then(|v| v.as_f64()).unwrap_or(0.0),
                b.get("height").and_then(|v| v.as_f64()).unwrap_or(0.0),
            );
            output_ok(mode, &human, json!({"box": b, "selector": sel}));
        }
        Err(e) => output_err(mode, &e.to_string()),
    }
}
