use std::{
    net::SocketAddr,
    sync::{Arc, RwLock},
};

use anyhow::{Context, Result};
use axum::extract::DefaultBodyLimit;
use clap::Parser;
use tracing::info;

mod display;
mod images;
mod monitor;
mod routes;
mod system;
mod types;
mod ui;

use display::{initial_display_status, spawn_display_worker};
use monitor::ensure_layout;
use routes::router;
use types::{AppState, Cli, DisplayConfig};

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "info,aster_webui=debug".into()),
        )
        .init();

    let cli = Cli::parse();
    let addr: SocketAddr = cli
        .bind
        .parse()
        .with_context(|| format!("Invalid bind address: {}", cli.bind))?;

    let display_config = DisplayConfig {
        device: cli.device,
        usb: cli.usb,
        simulate: cli.simulate,
        write_only: cli.write_only,
        native_enabled: !cli.disable_display,
    };

    let state = Arc::new(AppState {
        monitor_path: cli.config_dir.join("Monitor3.json"),
        image_dir: cli.config_dir.join("img"),
        display_status: Arc::new(RwLock::new(initial_display_status(&display_config))),
    });

    ensure_layout(&state).await?;
    spawn_display_worker(state.clone(), display_config);

    let app = router(state).layer(DefaultBodyLimit::max(64 * 1024 * 1024));

    info!("Starting aster-webui on {addr}");
    let listener = tokio::net::TcpListener::bind(addr).await?;
    axum::serve(listener, app).await?;
    Ok(())
}
