//! `adom-chipsmith detect <chip-fetcher-dir>` — run detection headlessly and
//! print a JSON report. Boots a transient server in --no-open mode, drives
//! the JS detection pipeline via /eval, then reports + shuts down. Useful
//! for batch validation across an entire chip-fetcher library.

use crate::cli::{err, ok, DEFAULT_PORT};
use crate::server::{self, convert, ServerConfig, ServerState};
use anyhow::{anyhow, Result};
use clap::Parser;
use std::path::PathBuf;
use std::time::Duration;

#[derive(Parser)]
pub struct Args {
    /// Path to a chip-fetcher library directory.
    pub dir: PathBuf,

    /// Print JSON instead of human text.
    #[arg(long)]
    pub json: bool,

    /// Port to bind during the headless run.
    #[arg(long, default_value_t = DEFAULT_PORT)]
    pub port: u16,
}

pub fn run(args: Args) -> Result<()> {
    if !args.dir.is_dir() {
        return Err(anyhow!("not a directory: {}", args.dir.display()));
    }
    let mpn = args.dir.file_name()
        .and_then(|s| s.to_str())
        .ok_or_else(|| anyhow!("can't derive MPN from dir: {}", args.dir.display()))?
        .to_string();

    let mut step_path = args.dir.join(format!("{mpn}.step"));
    if !step_path.exists() { step_path = args.dir.join(format!("{mpn}.STEP")); }
    if !step_path.exists() {
        return Err(anyhow!("no .step file in {}", args.dir.display()));
    }

    let step_bytes = std::fs::read(&step_path).map_err(|e| anyhow!("read STEP: {e}"))?;
    let result = convert::convert_step_bytes(&step_bytes)
        .map_err(|e| anyhow!("conversion failed: {e}"))?;

    let config = ServerConfig {
        port: args.port,
        initial_glb_path: Some(result.glb_path.clone()),
        initial_source_label: Some(format!("{mpn}.step")),
        initial_source_type: Some("chip-fetcher".to_string()),
    };
    let state = ServerState::new(config);

    // Seed spec + footprint from the dir
    let info_path = args.dir.join("info.json");
    if info_path.exists() {
        if let Ok(s) = std::fs::read_to_string(&info_path) {
            if let Ok(v) = serde_json::from_str::<serde_json::Value>(&s) {
                *state.spec.lock().unwrap() = v;
            }
        }
    }
    *state.signoff_dir.lock().unwrap() = Some(args.dir.clone());
    *state.source_step_path.lock().unwrap() = Some(step_path.clone());

    // Spawn server thread
    let server_state = state.clone();
    std::thread::spawn(move || {
        let _ = server::run(server_state);
    });

    // Headless detection requires a JS context — for now, we report what the
    // Rust side knows: GLB stats + spec + footprint pad count. Full live
    // detection requires a webview running the JS. Future: bake detection
    // into Rust once /step-meta delivers the structured B-rep.
    std::thread::sleep(Duration::from_millis(800));

    let spec = state.spec.lock().unwrap().clone();
    let report = serde_json::json!({
        "mpn": mpn,
        "source": format!("{mpn}.step"),
        "step_size_kb": step_bytes.len() / 1024,
        "glb_size_kb": result.size_bytes / 1024,
        "glb_meshes": result.meshes,
        "glb_nodes": result.nodes,
        "glb_cache_hit": result.cache_hit,
        "spec": spec,
        "step_meta_available": match convert::fetch_step_meta(&step_bytes) {
            Ok(Some(_)) => true,
            _ => false,
        },
        "headless_detect_status": "stub — full detection requires live webview; loads STEP + caches GLB + checks /step-meta availability",
    });

    if args.json {
        println!("{}", serde_json::to_string_pretty(&report).unwrap());
    } else {
        ok(format!("MPN: {mpn}"));
        ok(format!("STEP: {} KB → GLB: {} KB ({})", step_bytes.len() / 1024, result.size_bytes / 1024, if result.cache_hit { "cached" } else { "fresh" }));
        if let Some(p) = spec.get("package_family") {
            ok(format!("package: {p}"));
        }
        if let Some(p) = spec.get("pin_count") {
            ok(format!("pin count: {p}"));
        }
        let step_meta_ok = match convert::fetch_step_meta(&step_bytes) {
            Ok(Some(_)) => "yes",
            Ok(None) => "no (service-step2glb /step-meta not deployed)",
            Err(e) => { let _ = e; "error" }
        };
        ok(format!("step-meta available: {step_meta_ok}"));
    }
    Ok(())
}