//! Thumbnail playground — drag-drop a STEP into a Hydrogen webview, see all
//! 6 × 3 = 18 PNGs populate live. Hits the /thumbnail-batch endpoint via
//! a tiny local proxy (avoids CORS — webview can't talk to the external
//! service directly, has to go through the user's container-proxy URL).

use crate::client;
use crate::ui::{err, hint, ok, CYAN, RESET};
use std::io::Read;
use tiny_http::{Header, Method, Request, Response, Server};

const UI_HTML: &str = include_str!("playground.html");

#[derive(clap::Args, Debug)]
pub struct PlaygroundArgs {
    /// Port for the local HTTP server. 0 = auto-pick a free port.
    #[arg(long, default_value_t = 0)]
    pub port: u16,
    /// Skip opening the Hydrogen webview tab (still prints the proxy URL).
    #[arg(long)]
    pub no_tab: bool,
}

pub fn run(args: PlaygroundArgs) {
    let port = if args.port == 0 { pick_free_port() } else { args.port };
    let bind = format!("127.0.0.1:{port}");
    let server = match Server::http(&bind) {
        Ok(s) => s,
        Err(e) => {
            err(format!("bind {bind}: {e}"));
            std::process::exit(4);
        }
    };

    let url = proxy_url(port);
    if !args.no_tab {
        if let Err(e) = crate::tab::open_webview_tab("step2glb thumb playground", &url) {
            eprintln!("(warning) could not open Hydrogen tab: {e}");
        }
    }
    ok(format!(
        "thumb playground ready at {CYAN}{url}{RESET}  (Ctrl-C to stop)"
    ));

    for req in server.incoming_requests() {
        handle(req);
    }
}

fn handle(mut req: Request) {
    let full = req.url().to_string();
    let path = full.split('?').next().unwrap_or(&full).to_string();
    match (req.method(), path.as_str()) {
        (Method::Get, "/") | (Method::Get, "/index.html") => {
            let resp = Response::from_string(UI_HTML)
                .with_header(Header::from_bytes("Content-Type", "text/html; charset=utf-8").unwrap());
            let _ = req.respond(resp);
        }
        (Method::Post, "/render") => {
            // Body is raw STEP bytes (the page POSTs the file's ArrayBuffer).
            let mut body = Vec::with_capacity(1024 * 64);
            if let Err(e) = req.as_reader().read_to_end(&mut body) {
                let _ = req.respond(
                    Response::from_string(format!("read body: {e}")).with_status_code(400),
                );
                return;
            }
            // Forward to /thumbnail-batch with all-orientations, all-sizes.
            // Result includes 18 base64 PNGs.
            let result = client::thumbnail_batch_bytes(&body, "all", "all", "iso");
            let body_json = match result {
                Ok(r) => serde_json::to_string(&serde_json::json!({
                    "ok": true,
                    "srcKind": r.src_kind,
                    "pose": r.pose,
                    "bboxMm": r.bbox_mm,
                    "loadDurationMs": r.load_duration_ms,
                    "totalDurationMs": r.total_duration_ms,
                    "thumbnailCount": r.thumbnail_count,
                    "renderWarnings": r.render_warnings,
                    "thumbnails": r.thumbnails.iter().map(|t| serde_json::json!({
                        "upAxis": t.up_axis,
                        "size": t.size,
                        "width": t.width,
                        "height": t.height,
                        "sizeBytes": t.size_bytes,
                        "dataB64": t.data_b64,
                    })).collect::<Vec<_>>(),
                })).unwrap_or_default(),
                Err(e) => serde_json::json!({"ok": false, "error": e}).to_string(),
            };
            let resp = Response::from_string(body_json)
                .with_header(Header::from_bytes("Content-Type", "application/json").unwrap());
            let _ = req.respond(resp);
        }
        (Method::Post, "/shutdown") => {
            let _ = req.respond(Response::from_string("bye"));
            std::process::exit(0);
        }
        _ => {
            let _ = req.respond(Response::from_string("not found").with_status_code(404));
        }
    }
}

fn pick_free_port() -> u16 {
    let l = std::net::TcpListener::bind("127.0.0.1:0").expect("bind 0");
    l.local_addr().unwrap().port()
}

fn proxy_url(port: u16) -> String {
    if let Ok(proxy) = std::env::var("VSCODE_PROXY_URI") {
        proxy.replace("{{port}}", &port.to_string())
    } else {
        format!("http://127.0.0.1:{port}/")
    }
}