//! `impl Routes` for the unified `adom-video-post app` subcommand.
//!
//! Dispatches incoming requests across the storyboard and voiceover
//! sub-route tables behind prefix-based URL rewriting:
//!
//!   /manifest, /state, /video/*, /poster/*, /image/*,
//!   /console-png/*, /final-video/*   → `StoryboardRoutes`
//!   /voiceover/<rest>                 → `VoiceoverRoutes` with
//!                                        `<rest>` passed as the
//!                                        stripped URL
//!
//! Voiceover's /video endpoint serves the configured input (the
//! final_fast_path from the manifest) — it's accessible via
//! `/voiceover/video` in the unified app. The UI's Narrate panel
//! points a `<video>` element at that URL.

use crate::storyboard::routes::StoryboardRoutes;
use crate::voiceover::server::VoiceoverRoutes;
use crate::webapp::{video as webapp_video, RouteOutcome, Routes};
use std::sync::Arc;
use tiny_http::{Method, Request};

const UI_HTML: &str = include_str!("ui.html");
const FAVICON_SVG: &str = include_str!("icon.svg");

pub struct AppRoutes {
    pub storyboard: Arc<StoryboardRoutes>,
    pub voiceover: Arc<VoiceoverRoutes>,
}

impl Routes for AppRoutes {
    fn ui_html(&self) -> &str {
        UI_HTML
    }

    fn favicon_svg(&self) -> &str {
        FAVICON_SVG
    }

    fn handle(&self, request: Request, method: &Method, url: &str) -> RouteOutcome {
        // Special-case /voiceover/video: VoiceoverRoutes's own
        // /video handler reads a hardcoded `cfg.input` path that was
        // captured at app startup. In the unified app, the user may
        // run `POST /concat` after startup to produce the final fast
        // concat, and we want the Narrate phase's video player to
        // pick up the newly-set manifest.final_fast_path without
        // needing a process restart. So intercept the request here,
        // read the current final_fast_path out of the shared
        // manifest, and serve it directly. Fall back to the raw
        // concat if final_fast_path is unset.
        if matches!(method, Method::Get) && url == "/voiceover/video" {
            let path = {
                let m = self.storyboard.manifest.read().unwrap();
                m.final_fast_path
                    .clone()
                    .or_else(|| m.final_raw_path.clone())
            };
            return match path {
                Some(p) if p.exists() => RouteOutcome::Handled(
                    webapp_video::serve_file_range(request, &p, "video/webm"),
                ),
                _ => RouteOutcome::Handled(
                    request
                        .respond(tiny_http::Response::from_string("no final video available — run POST /concat first")
                            .with_status_code(404))
                        .map_err(|e| e.to_string()),
                ),
            };
        }

        // /voiceover/* → strip the prefix and hand off to the
        // voiceover route table, which thinks it's running alone.
        if let Some(rest) = url.strip_prefix("/voiceover") {
            let sub_url = if rest.is_empty() { "/" } else { rest };
            return self.voiceover.handle(request, method, sub_url);
        }

        // Everything else falls through to the storyboard routes.
        // This includes /manifest, /state, /video/:id/*, /poster/:id,
        // /image/:id, /console-png/:id, /final-video/raw|fast.
        self.storyboard.handle(request, method, url)
    }
}