use axum::{
    extract::ws::{Message, WebSocket, WebSocketUpgrade},
    extract::State,
    response::IntoResponse,
    routing::get,
    Router,
};
use serde::Serialize;
use std::{collections::HashMap, sync::Arc, time::Duration};
use sysinfo::{CpuRefreshKind, Networks, Disks, System};
use tokio::sync::{broadcast, RwLock};
use tower_http::cors::CorsLayer;

// 모니터링할 서비스 목록
const SERVICES_URLS: &[(&str, &str)] = &[
    ("jenkins", "http://jenkins:8080/login"),
    ("portainer", "http://portainer:9000"),
    ("rustfs", "http://rustfs:9001"),
    ("uptime-kuma", "http://uptime-kuma:3001"),
    ("dozzle", "http://dozzle:8080"),
    ("it-tools", "http://it-tools:80"),
    ("ffmpeg-downloader", "http://ffmpeg-gui:3000"),
    ("cloudbeaver", "http://cloudbeaver:8978"),
    ("forgejo", "http://forgejo:3000")
];

/// 시스템 상태 정보 구조체
#[derive(Serialize, Clone, Default)]
struct SystemStats {
    cpu: f32,
    cpu_cores: usize,
    cpu_threads: usize,
    mem_total: f64,
    mem_used: f64,
    mem_percent: f64,
    disk_total: f64,
    disk_free: f64,
    disk_used: f64,
    disk_percent: f64,
    net_sent: u64,
    net_recv: u64,
}

/// 캐시된 상태를 저장하는 구조체
/// 새 WebSocket 클라이언트 연결 시 즉시 현재 상태를 전송하기 위해 사용
struct CachedState {
    system: Option<SystemStats>,
    services: HashMap<String, bool>,
}

/// 앱 전역 상태
struct AppState {
    tx: broadcast::Sender<String>,
    cached: RwLock<CachedState>,
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt().init();

    let (tx, _rx) = broadcast::channel(100);
    
    // 캐시된 상태 초기화
    let cached = RwLock::new(CachedState {
        system: None,
        services: HashMap::new(),
    });
    
    let app_state = Arc::new(AppState { 
        tx: tx.clone(),
        cached,
    });

    // 시스템 모니터링 태스크 시작 (2초 간격)
    let state_sys = app_state.clone();
    tokio::spawn(monitor_system(state_sys));

    // 서비스 모니터링 태스크 시작 (즉시 실행 후 2초 간격)
    let state_svc = app_state.clone();
    tokio::spawn(monitor_services(state_svc));

    let app = Router::new()
        .route("/ws", get(ws_handler))
        .with_state(app_state)
        .layer(CorsLayer::permissive());

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap();
    println!("Listening on 0.0.0.0:8000");
    axum::serve(listener, app).await.unwrap();
}

async fn ws_handler(
    ws: WebSocketUpgrade,
    State(state): State<Arc<AppState>>,
) -> impl IntoResponse {
    ws.on_upgrade(|socket| handle_socket(socket, state))
}

/// WebSocket 클라이언트 연결 처리
/// 연결 즉시 캐시된 시스템/서비스 상태를 전송한 후 실시간 업데이트 수신
async fn handle_socket(mut socket: WebSocket, state: Arc<AppState>) {
    // 연결 즉시 캐시된 상태 전송 (클라이언트가 바로 현재 상태를 볼 수 있도록)
    {
        let cached = state.cached.read().await;
        
        // 시스템 상태 전송
        if let Some(ref system) = cached.system {
            #[derive(Serialize)]
            struct SysMsg<'a> {
                system: &'a SystemStats,
            }
            if let Ok(json) = serde_json::to_string(&SysMsg { system }) {
                let _ = socket.send(Message::Text(json)).await;
            }
        }
        
        // 서비스 상태 전송
        if !cached.services.is_empty() {
            #[derive(Serialize)]
            struct SvcMsg<'a> {
                services: &'a HashMap<String, bool>,
            }
            if let Ok(json) = serde_json::to_string(&SvcMsg { services: &cached.services }) {
                let _ = socket.send(Message::Text(json)).await;
            }
        }
    }

    // 이후 실시간 업데이트 수신
    let mut rx = state.tx.subscribe();
    while let Ok(msg) = rx.recv().await {
        if socket.send(Message::Text(msg)).await.is_err() {
            break;
        }
    }
}

/// 시스템 상태 모니터링 (2초 간격)
async fn monitor_system(state: Arc<AppState>) {
    let mut sys = System::new_all();
    let mut networks = Networks::new_with_refreshed_list();
    let mut disks = Disks::new_with_refreshed_list();
    
    let mut last_total_sent: u64 = 0;
    let mut last_total_recv: u64 = 0;

    // 초기 네트워크 통계 수집
    networks.refresh();
    for (_, data) in &networks {
        last_total_sent += data.total_transmitted();
        last_total_recv += data.total_received();
    }

    loop {
        // 2초 간격으로 시스템 상태 갱신
        tokio::time::sleep(Duration::from_secs(2)).await;

        sys.refresh_cpu_specifics(CpuRefreshKind::everything());
        sys.refresh_memory();
        networks.refresh();
        disks.refresh_list();
        disks.refresh();

        // CPU 사용률
        let cpu_global = sys.global_cpu_info().cpu_usage();
        
        // 메모리 사용량
        let mem_total_gb = sys.total_memory() as f64 / 1024.0 / 1024.0 / 1024.0;
        let mem_used_gb = sys.used_memory() as f64 / 1024.0 / 1024.0 / 1024.0;
        let mem_percent = (sys.used_memory() as f64 / sys.total_memory() as f64) * 100.0;

        // 디스크 사용량 (루트 또는 호스트 마운트 기준)
        let mut disk_total_gb: f64 = 0.0;
        let mut disk_used_gb: f64 = 0.0;
        
        for disk in &disks {
            let mount = disk.mount_point().to_str().unwrap_or("");
            if mount.starts_with("/host_root") || mount == "/" {
                disk_total_gb = disk.total_space() as f64 / 1024.0 / 1024.0 / 1024.0;
                disk_used_gb = (disk.total_space() - disk.available_space()) as f64 / 1024.0 / 1024.0 / 1024.0;
                break;
            }
        }
        let disk_free_gb = disk_total_gb - disk_used_gb;
        let disk_percent = if disk_total_gb > 0.0 { (disk_used_gb / disk_total_gb) * 100.0 } else { 0.0 };

        // 네트워크 속도 계산 (2초 간격 기준)
        let mut curr_total_sent: u64 = 0;
        let mut curr_total_recv: u64 = 0;
        for (_, data) in &networks {
            curr_total_sent += data.total_transmitted();
            curr_total_recv += data.total_received();
        }
        
        let net_sent_speed = curr_total_sent.saturating_sub(last_total_sent) / 2;
        let net_recv_speed = curr_total_recv.saturating_sub(last_total_recv) / 2;
        
        last_total_sent = curr_total_sent;
        last_total_recv = curr_total_recv;

        let stats = SystemStats {
            cpu: (cpu_global * 10.0).round() / 10.0,
            cpu_cores: sys.physical_core_count().unwrap_or(1),
            cpu_threads: sys.cpus().len(),
            mem_total: (mem_total_gb * 10.0).round() / 10.0,
            mem_used: (mem_used_gb * 10.0).round() / 10.0,
            mem_percent: (mem_percent * 10.0).round() / 10.0,
            disk_total: disk_total_gb.round(),
            disk_free: disk_free_gb.round(),
            disk_used: disk_used_gb.round(),
            disk_percent: (disk_percent * 10.0).round() / 10.0,
            net_sent: net_sent_speed,
            net_recv: net_recv_speed,
        };

        // 캐시 업데이트
        {
            let mut cached = state.cached.write().await;
            cached.system = Some(stats.clone());
        }

        // 클라이언트에 브로드캐스트
        #[derive(Serialize)]
        struct SysMsg {
            system: SystemStats,
        }
        if let Ok(json) = serde_json::to_string(&SysMsg { system: stats }) {
            let _ = state.tx.send(json);
        }
    }
}

/// 서비스 상태 모니터링 (즉시 실행 후 2초 간격)
async fn monitor_services(state: Arc<AppState>) {
    let client = reqwest::Client::builder()
        .timeout(Duration::from_millis(1500))
        .danger_accept_invalid_certs(true)
        .build()
        .unwrap_or_default();

    loop {
        // 모든 서비스 상태를 병렬로 체크
        let mut handles = vec![];
        
        for (name, url) in SERVICES_URLS {
            let client_clone = client.clone();
            let name_str = name.to_string();
            let url_str = url.to_string();
            handles.push(tokio::spawn(async move {
                let status = check_service(&client_clone, &url_str).await;
                (name_str, status)
            }));
        }
        
        let mut service_results = HashMap::new();
        for handle in handles {
            if let Ok((name, status)) = handle.await {
                service_results.insert(name, status);
            }
        }

        // 캐시 업데이트
        {
            let mut cached = state.cached.write().await;
            cached.services = service_results.clone();
        }

        // 클라이언트에 브로드캐스트
        #[derive(Serialize)]
        struct SvcMsg {
            services: HashMap<String, bool>,
        }
        if let Ok(json) = serde_json::to_string(&SvcMsg { services: service_results }) {
            let _ = state.tx.send(json);
        }

        // 2초 간격으로 갱신
        tokio::time::sleep(Duration::from_secs(2)).await;
    }
}

/// 개별 서비스 헬스체크
async fn check_service(client: &reqwest::Client, url: &str) -> bool {
    match client.get(url).send().await {
        Ok(res) => {
            // 500번대 에러만 실패로 처리 (리다이렉트, 인증 요구 등은 "살아있음"으로 간주)
            if res.status().as_u16() >= 500 {
                println!("서비스 체크 실패 {}: Status {}", url, res.status());
                false
            } else {
                true
            }
        }
        Err(e) => {
            println!("서비스 체크 에러 {}: {}", url, e);
            false
        }
    }
}
