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

#[derive(Parser)]
pub struct Args {
    /// Path to a .step or .stp file.
    pub file: PathBuf,

    #[arg(long, default_value_t = DEFAULT_PORT)]
    pub port: u16,

    /// Skip opening the Hydrogen webview tab (for headless test runs).
    #[arg(long)]
    pub no_open: bool,

    #[arg(long, default_value = "STEP Viewer")]
    pub tab_name: String,

    #[arg(long, default_value = "mdi:cube-outline")]
    pub display_icon: String,
}

pub fn run(args: Args) -> Result<()> {
    let file = args.file.canonicalize().map_err(|e| {
        anyhow!(
            "{} does not exist: {e}",
            args.file.display()
        )
    })?;

    let ext = file
        .extension()
        .and_then(|s| s.to_str())
        .map(|s| s.to_ascii_lowercase());
    if !matches!(ext.as_deref(), Some("step") | Some("stp")) {
        err(format!(
            "{} is not a .step / .stp file",
            file.display()
        ));
        hint("adom-step is STEP-only; use the canonical 3D viewer for other formats");
        return Err(anyhow!("not a STEP file"));
    }

    let bytes = std::fs::read(&file).map_err(|e| anyhow!("read {}: {e}", file.display()))?;
    ok(format!(
        "converting {} ({} KB) via service-step2glb...",
        file.display(),
        bytes.len() / 1024
    ));

    let result = match convert::convert_step_bytes(&bytes) {
        Ok(r) => r,
        Err(e) => {
            err(format!("conversion failed: {e}"));
            return Err(e);
        }
    };

    let label = if result.cache_hit {
        "cache HIT"
    } else {
        "cache MISS"
    };
    ok(format!(
        "{label} — GLB at {} ({} KB, {} meshes, {} nodes, {} ms)",
        result.glb_path.display(),
        result.size_bytes / 1024,
        result.meshes,
        result.nodes,
        result.duration_ms
    ));

    let label = file
        .file_name()
        .and_then(|s| s.to_str())
        .unwrap_or("model.step")
        .to_string();

    let config = ServerConfig {
        port: args.port,
        initial_glb_path: Some(result.glb_path.clone()),
        initial_source_label: Some(label),
        initial_source_type: Some("local-file".to_string()),
    };
    let state = ServerState::new(config);

    let shutdown_state = state.clone();
    ctrlc::set_handler(move || {
        eprintln!("\nreceived SIGINT, shutting down adom-step");
        let _ = &shutdown_state;
        std::process::exit(0);
    })?;

    if !args.no_open {
        // Open in a background thread after the server is up so the
        // webview's first load resolves.
        let port = args.port;
        let tab_name = args.tab_name.clone();
        let icon = args.display_icon.clone();
        std::thread::spawn(move || {
            std::thread::sleep(std::time::Duration::from_millis(400));
            if let Err(e) =
                crate::cli::open::open_webview_tab(port, &tab_name, &icon)
            {
                eprintln!("warn: failed to open webview tab: {e}");
            }
        });
    }

    ok(format!("adom-step running on port {}", args.port));
    server::run(state)?;
    Ok(())
}