//! Hydrogen tab lifecycle via `adom-cli hydrogen workspace`.

use base64::Engine;
use std::process::Command;

const WEBVIEW_PANEL_TYPE: &str = "adom/a1b2c3d4-0031-4000-a000-000000000031";

/// Substitute the current port into VSCODE_PROXY_URI so the webview
/// loads via the Coder proxy (same-origin with hydrogen.adom.inc).
/// Falls back to 127.0.0.1 for local testing when the env var is not
/// set — `adom-mouser app --no-tab` + `$ open http://127.0.0.1:8789`.
pub fn proxy_url(port: u16) -> String {
    match std::env::var("VSCODE_PROXY_URI") {
        Ok(tmpl) => tmpl.replace("{{port}}", &port.to_string()),
        Err(_) => format!("http://127.0.0.1:{}/", port),
    }
}

fn icon_data_url(icon_svg: &str) -> String {
    let b64 = base64::engine::general_purpose::STANDARD.encode(icon_svg.as_bytes());
    format!("data:image/svg+xml;base64,{}", b64)
}

pub fn add_webview_tab(name: &str, url: &str, icon_svg: &str) -> Result<(), String> {
    // Find the focused leaf we should attach to.
    let leaf = focused_leaf_for_tab()?;
    let initial_state = serde_json::json!({ "url": url }).to_string();
    let icon = icon_data_url(icon_svg);
    let out = Command::new("adom-cli")
        .args([
            "hydrogen",
            "workspace",
            "add-tab",
            "--panel-id",
            &leaf,
            "--panel-type",
            WEBVIEW_PANEL_TYPE,
            "--display-name",
            name,
            "--display-icon",
            &icon,
            "--initial-state",
            &initial_state,
        ])
        .output()
        .map_err(|e| format!("adom-cli add-tab: {}", e))?;
    if !out.status.success() {
        return Err(format!(
            "adom-cli add-tab failed: {}",
            String::from_utf8_lossy(&out.stderr)
        ));
    }
    Ok(())
}

pub fn remove_webview_tab(name: &str) -> Result<(), String> {
    let out = Command::new("adom-cli")
        .args(["hydrogen", "workspace", "remove-tab", "--name", name])
        .output()
        .map_err(|e| format!("adom-cli remove-tab: {}", e))?;
    if !out.status.success() {
        return Err(format!(
            "adom-cli remove-tab failed: {}",
            String::from_utf8_lossy(&out.stderr)
        ));
    }
    Ok(())
}

/// Pick a leaf panel that is NOT the VS Code editor, so we don't cover
/// the user's code view. Walk the workspace tree, find nodes with
/// `type: "leaf"`, check whether any tab in the leaf is a VS Code
/// editor (`...eeee-4000-a000-...`). Return the first non-editor
/// leaf's id. Falls back to the focused panel id if nothing else fits.
fn focused_leaf_for_tab() -> Result<String, String> {
    let out = Command::new("adom-cli")
        .args(["hydrogen", "workspace", "get"])
        .output()
        .map_err(|e| format!("adom-cli workspace get: {}", e))?;
    if !out.status.success() {
        return Err(format!("adom-cli workspace get failed: {}", String::from_utf8_lossy(&out.stderr)));
    }
    let v: serde_json::Value = serde_json::from_slice(&out.stdout)
        .map_err(|e| format!("parse workspace JSON: {}", e))?;

    let mut leaves: Vec<LeafInfo> = Vec::new();
    walk_leaves(&v, &mut leaves);

    // Prefer leaves that contain no VS Code editor tabs.
    let non_editor: Vec<&LeafInfo> = leaves.iter().filter(|l| !l.has_editor).collect();
    if let Some(l) = non_editor.first() {
        return Ok(l.id.clone());
    }
    if let Some(id) = v.get("focusedPanelId").and_then(|v| v.as_str()) {
        return Ok(id.to_string());
    }
    leaves
        .first()
        .map(|l| l.id.clone())
        .ok_or_else(|| "no panels found in workspace".into())
}

struct LeafInfo {
    id: String,
    has_editor: bool,
}

fn walk_leaves(v: &serde_json::Value, out: &mut Vec<LeafInfo>) {
    if let Some(obj) = v.as_object() {
        if obj.get("type").and_then(|v| v.as_str()) == Some("leaf") {
            if let Some(id) = obj.get("id").and_then(|v| v.as_str()) {
                let tabs = obj.get("tabs").and_then(|v| v.as_array());
                let has_editor = tabs
                    .map(|arr| {
                        arr.iter().any(|t| {
                            let pt = t.get("panelType").and_then(|v| v.as_str()).unwrap_or("");
                            // VS Code editor panel UUID has "-eeee-4000-a000-"
                            // or "-0030-4000-a000-". Web View is "-0031-".
                            pt.contains("eeee-4000") || pt.contains("0030-4000")
                        })
                    })
                    .unwrap_or(false);
                out.push(LeafInfo { id: id.to_string(), has_editor });
            }
        }
        for (_, child) in obj {
            walk_leaves(child, out);
        }
    } else if let Some(arr) = v.as_array() {
        for c in arr {
            walk_leaves(c, out);
        }
    }
}