//! Shared HTTP + webview infrastructure for every `adom-video-post` subcommand
//! that exposes a web UI (voiceover, storyboard, future review apps).
//!
//! The pattern is: a subcommand implements the `Routes` trait — returning
//! its embedded UI HTML and handling app-specific routes — and calls
//! `run_webapp(cfg, routes)`. `run_webapp` starts a tiny_http server,
//! handles built-in routes (UI, favicon, /console, /eval, /shutdown),
//! delegates unknown routes to the app, opens a Hydrogen webview tab
//! pointed at the server, and blocks until /shutdown fires. On return
//! the webview tab is removed and the instance registry entry is cleaned
//! up.
//!
//! Built-in routes all apps get for free:
//!
//!   GET  /                     embedded UI HTML with EVAL_CLIENT injected
//!   GET  /index.html           same
//!   GET  /favicon.svg          shared favicon
//!   GET  /favicon.ico          shared favicon
//!   GET  /console              tail the in-app log buffer
//!   POST /console              append a log message (UI → server)
//!   DELETE /console            clear the log buffer
//!   POST /eval                 queue a hot-patch JS snippet (app-creator §7b)
//!   GET  /eval/pending         UI long-polls this for pending snippets
//!   POST /eval/:id/result      UI posts the snippet's return value back
//!   POST /shutdown             exit the server cleanly
//!   GET  /shutdown             same, convenient for curl
//!
//! Apps define their own /state shape, /video routes, and any
//! app-specific command endpoints — anything that isn't in the list
//! above falls through to `Routes::handle`.

pub mod console;
pub mod eval;
pub mod icons;
pub mod registry;
pub mod server;
pub mod tab;
pub mod video;

use std::sync::Arc;
use tiny_http::{Method, Request};

/// Per-app configuration consumed by `run_webapp`.
pub struct WebappConfig {
    pub port: u16,
    pub tab_name: String,
    pub panel_id_override: Option<String>,
    pub display_icon: String,
    /// Short identifier of the subcommand for the instance registry
    /// ("voiceover", "storyboard", etc).
    pub subcommand: String,
    /// Human-readable title shown in `adom-video-post list`. Usually the
    /// input filename or manifest title.
    pub title: String,
    /// When true, start the HTTP server but skip the Hydrogen webview
    /// tab open. Useful when the user is reviewing the app in pup
    /// and explicitly does not want a Hydrogen tab to appear, or for
    /// AI sessions that drive the page via browser_eval.
    pub no_open: bool,
}

/// Outcome returned from `Routes::handle`. Because tiny_http's `Request`
/// is a single-owner value, the app either responds to it (and returns
/// `Handled`) or returns it back so the caller can fall through to a
/// 404 (`NotHandled`).
pub enum RouteOutcome {
    Handled(Result<(), String>),
    NotHandled(Request),
}

/// Trait every webapp-backed subcommand implements. The server loop
/// calls `ui_html` at startup to serve GET /, and `handle` for any
/// request not claimed by a built-in route.
pub trait Routes: Send + Sync {
    /// The embedded UI HTML. A `<!-- EVAL_CLIENT -->` marker in the
    /// returned string is replaced with the shared eval_client.js at
    /// serve time so every UI automatically gets the hot-patch channel.
    fn ui_html(&self) -> &str;

    /// The SVG bytes served at `GET /favicon.svg`. Hydrogen reads the
    /// loaded page's `<link rel="icon">` after page-load and writes it
    /// into `panelState.displayIcon`, overriding whatever placeholder
    /// was passed via `--display-icon`. So the stable, post-load tab
    /// icon is whatever this method returns — which means each
    /// subcommand controls its own tab icon via this favicon, not via
    /// the placeholder (see hydrogen-tab-icons skill). Must be a
    /// self-contained, square, monochrome `#e6edf3` SVG per the Adom
    /// brand rule in `gallia/skills/brand/SKILL.md`.
    fn favicon_svg(&self) -> &str;

    /// Handle a request the built-in router did not claim. On
    /// `NotHandled` the caller responds with a 404.
    fn handle(&self, request: Request, method: &Method, url: &str) -> RouteOutcome;
}

/// Run a webapp: start the HTTP server on `cfg.port`, register the
/// instance, open the Hydrogen webview tab, block until /shutdown,
/// then clean up. Returns on clean shutdown or on a hard bind error.
pub fn run_webapp(cfg: WebappConfig, routes: Arc<dyn Routes>) -> Result<(), String> {
    server::serve(cfg, routes)
}