use crate::api::error::ApiError;
use crate::api::state::AppState;
use axum::extract::Extension;
use axum::Json;
use serde_json::Value;
use std::sync::Arc;

/// Plain-text version string that the client compares against its cached
/// `sysinfo_ver`. Same value the heartbeat handler echoes via the
/// `sysinfo: true` flag.
pub async fn sysinfo_ver(Extension(state): Extension<Arc<AppState>>) -> String {
    state.cfg.sysinfo_ver.clone()
}

/// Bare-string body: `"SYSINFO_UPDATED"` or `"ID_NOT_FOUND"`. The client at
/// /Users/sn0/Desktop/rustdesk/src/hbbs_http/sync.rs:212 does a literal
/// `==` comparison on these — do not wrap in JSON.
pub async fn sysinfo(
    Extension(state): Extension<Arc<AppState>>,
    Json(payload): Json<Value>,
) -> Result<String, ApiError> {
    let id = payload
        .get("id")
        .and_then(|v| v.as_str())
        .unwrap_or_default();
    let uuid = payload
        .get("uuid")
        .and_then(|v| v.as_str())
        .unwrap_or_default();
    if id.is_empty() || uuid.is_empty() {
        return Err(ApiError::BadRequest("id and uuid required".into()));
    }

    // Tie sysinfo storage to a real rendezvous-registered peer. Without this
    // gate, any caller could populate device_sysinfo for arbitrary IDs.
    let peer = state
        .db
        .get_peer(id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    if peer.is_none() {
        return Ok("ID_NOT_FOUND".to_string());
    }

    let version = parse_version_number(payload.get("version").and_then(|v| v.as_str()));
    state
        .db
        .sysinfo_upsert(id, uuid, &payload.to_string(), &state.cfg.sysinfo_ver, version)
        .await?;
    Ok("SYSINFO_UPDATED".to_string())
}

fn parse_version_number(s: Option<&str>) -> i64 {
    let Some(s) = s else { return 0 };
    // hbb_common encodes "1.4.2" as 1*1_000_000 + 4*1_000 + 2 = 1_004_002.
    let mut parts = s.split('.').map(|p| p.parse::<i64>().unwrap_or(0));
    let major = parts.next().unwrap_or(0);
    let minor = parts.next().unwrap_or(0);
    let patch = parts.next().unwrap_or(0);
    major * 1_000_000 + minor * 1_000 + patch
}
