//! HTTP management API mounted in-process alongside hbbs's rendezvous
//! listeners. The router is wired in via `src/rendezvous_server.rs`'s outer
//! `tokio::select!`. M1 covers auth + heartbeat + sysinfo; later milestones
//! add address book, audit, OIDC, etc.

pub mod ab;
pub mod admin;
pub mod audit;
pub mod auth;
pub mod devices_cli;
pub mod email;
pub mod error;
pub mod groups;
pub mod heartbeat;
pub mod http_proxy;
pub mod middleware;
pub mod oidc;
pub mod pagination;
pub mod peers;
pub mod plugin_sign;
pub mod record;
pub mod state;
pub mod strategy;
pub mod sysinfo;
pub mod twofa;
pub mod users;

pub use state::AppState;

use axum::extract::Extension;
use axum::routing::{delete, get, post, put};
use axum::Router;
use hbb_common::{log, ResultType};
use std::net::SocketAddr;
use std::sync::Arc;

pub fn router(state: Arc<AppState>) -> Router {
    let app = Router::new()
        // M1: auth + heartbeat + sysinfo
        .route(
            "/api/login-options",
            get(auth::login_options).head(auth::login_options_head),
        )
        .route("/api/login", post(auth::login))
        .route("/api/currentUser", post(auth::current_user))
        .route("/api/logout", post(auth::logout))
        .route("/api/heartbeat", post(heartbeat::heartbeat))
        .route("/api/sysinfo_ver", post(sysinfo::sysinfo_ver))
        .route("/api/sysinfo", post(sysinfo::sysinfo))
        // M2: address book — modern (shared + personal)
        .route("/api/ab/settings", post(ab::settings::settings))
        .route("/api/ab/personal", post(ab::profiles::personal))
        .route(
            "/api/ab/shared/profiles",
            post(ab::profiles::shared_profiles),
        )
        .route("/api/ab/peers", post(ab::peers::list))
        .route("/api/ab/tags/:guid", post(ab::tags::list))
        .route("/api/ab/peer/add/:guid", post(ab::peers::add))
        .route("/api/ab/peer/update/:guid", put(ab::peers::update))
        .route("/api/ab/peer/:guid", delete(ab::peers::delete))
        .route("/api/ab/tag/add/:guid", post(ab::tags::add))
        .route("/api/ab/tag/rename/:guid", put(ab::tags::rename))
        .route("/api/ab/tag/update/:guid", put(ab::tags::update))
        .route("/api/ab/tag/:guid", delete(ab::tags::delete))
        // M2: address book — legacy single-blob fallback
        .route(
            "/api/ab",
            get(ab::legacy::get).post(ab::legacy::put),
        )
        // M2: group / users / peers panel
        .route(
            "/api/device-group/accessible",
            get(groups::accessible),
        )
        .route("/api/users", get(users::list))
        .route("/api/peers", get(peers::list))
        // M3: audit
        .route("/api/audit/conn", post(audit::conn::conn))
        .route("/api/audit/file", post(audit::file::file))
        .route("/api/audit/alarm", post(audit::alarm::alarm))
        .route("/api/audit", put(audit::note::note))
        // M3: session recording upload
        .route("/api/record", post(record::record))
        // M4: TOTP enrollment (admin-only)
        .route("/api/2fa/enroll", post(twofa::enroll))
        .route("/api/2fa/unenroll", post(twofa::unenroll))
        // M4: rustdesk --assign target
        .route("/api/devices/cli", post(devices_cli::assign))
        // M4: plugin signing (no auth — protocol-level)
        .route("/lic/web/api/plugin-sign", post(plugin_sign::plugin_sign))
        // M4: OIDC device-flow login
        .route("/api/oidc/auth", post(oidc::auth::auth))
        .route("/api/oidc/auth-query", get(oidc::poll::auth_query))
        .route("/oidc/callback", get(oidc::callback::callback));
    // M5: admin dashboard (HTMX + embedded HTML). Merged BEFORE the
    // Extension layer so the merged router carries the shared state.
    let app = match admin::build(state.clone()) {
        Some(admin_router) => app.merge(admin_router),
        None => app,
    };
    app.layer(Extension(state))
}

pub async fn serve(addr: SocketAddr, state: Arc<AppState>) -> ResultType<()> {
    log::info!("HTTP API listening on {}", addr);
    let app = router(state);
    // Share the same router with the rendezvous-TCP HttpProxyRequest path so
    // both transports route through the exact same handlers.
    http_proxy::install_router(app.clone());
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await?;
    Ok(())
}
