use std::fs;
use std::io::{Read};
use std::path::{Path, PathBuf};
use std::thread::{self, JoinHandle};

/// Start a simple static file server for `root` on `127.0.0.1:port`.
/// Uses `tiny_http` and serves common content types. Runs in a background thread.
pub fn start_static_server(root: PathBuf, port: u16, verbose: bool) -> std::io::Result<JoinHandle<()>> {
    let addr = format!("127.0.0.1:{}", port);
    let root = root.canonicalize().unwrap_or(root);

    let handle = thread::spawn(move || {
        let server = match tiny_http::Server::http(&addr) {
            Ok(s) => s,
            Err(e) => {
                eprintln!("[serve] failed binding {}: {}", addr, e);
                return;
            }
        };

        eprintln!("Serving http://{}/ (Ctrl-C to quit)", addr);
        if verbose {
            eprintln!("[serve] root: {}", root.display());
        }

        for req in server.incoming_requests() {
            let url = req.url();
            // strip query string
            let path_only = url.split('?').next().unwrap_or("/");
            let mut safe = PathBuf::new();
            for seg in path_only.split('/') {
                if seg.is_empty() || seg == "." { continue; }
                if seg == ".." { continue; }
                safe.push(seg);
            }

            let file_path = root.join(&safe);
            let metadata = fs::metadata(&file_path).ok();
            let file_path = if let Some(m) = metadata {
                if m.is_dir() { file_path.join("index.html") } else { file_path }
            } else {
                // if path seemed like a directory (trailing slash or empty), try index.html
                if path_only.ends_with('/') || path_only == "/" { file_path.join("index.html") } else { file_path }
            };

            // Ensure requested file is under root after canonicalization
            let canonical_ok = match file_path.canonicalize() {
                Ok(canon) => canon.starts_with(&root),
                Err(_) => false,
            };

            if !canonical_ok {
                let _ = req.respond(tiny_http::Response::from_string("Forbidden").with_status_code(403));
                continue;
            }

            match fs::File::open(&file_path) {
                Ok(mut f) => {
                    let mut buf = Vec::new();
                    if f.read_to_end(&mut buf).is_err() {
                        let _ = req.respond(tiny_http::Response::from_string("Internal Server Error").with_status_code(500));
                        continue;
                    }
                    let mime = content_type_for(file_path.as_path());
                    let mut resp = tiny_http::Response::from_data(buf);
                    resp.add_header(tiny_http::Header::from_bytes(&b"Content-Type"[..], mime.as_bytes()).unwrap());
                    let _ = req.respond(resp);
                }
                Err(_) => {
                    let _ = req.respond(tiny_http::Response::from_string("Not Found").with_status_code(404));
                }
            }
        }
    });

    Ok(handle)
}

fn content_type_for(path: &Path) -> &'static str {
    match path.extension().and_then(|s| s.to_str()).unwrap_or("").to_ascii_lowercase().as_str() {
        "html" => "text/html; charset=utf-8",
        "htm" => "text/html; charset=utf-8",
        "css" => "text/css; charset=utf-8",
        "js" => "application/javascript; charset=utf-8",
        "mjs" => "application/javascript; charset=utf-8",
        "json" => "application/json; charset=utf-8",
        "map" => "application/json; charset=utf-8",
        "txt" => "text/plain; charset=utf-8",
        "svg" => "image/svg+xml",
        "png" => "image/png",
        "jpg" | "jpeg" => "image/jpeg",
        "gif" => "image/gif",
        "webp" => "image/webp",
        "ico" => "image/x-icon",
        "wasm" => "application/wasm",
        _ => "application/octet-stream",
    }
}

