//! Per-chamber sync backend handlers. Delegates to `sync_control` for
//! backend orchestration.

use std::sync::Arc;

use axum::{
    extract::{Path as AxumPath, State},
    http::StatusCode,
    response::Json,
};
use serde_json::Value;

use crate::hub::state::{AppState, SseEvent};
use crate::sync_common::SyncBackend;
use crate::sync_control;

pub async fn get_sync(
    State(app): State<Arc<AppState>>,
    AxumPath(id): AxumPath<String>,
) -> Result<Json<Value>, StatusCode> {
    let (path, _) = app.resolve(&id).ok_or(StatusCode::NOT_FOUND)?;
    let summaries = sync_control::summarize_all(&path);
    Ok(Json(
        serde_json::to_value(summaries).unwrap_or(Value::Array(vec![])),
    ))
}

pub async fn post_sync_action(
    State(app): State<Arc<AppState>>,
    AxumPath((id, backend_str, verb)): AxumPath<(String, String, String)>,
) -> Result<Json<Value>, StatusCode> {
    let (path, entry) = app.resolve(&id).ok_or(StatusCode::NOT_FOUND)?;
    let backend = SyncBackend::parse(&backend_str).ok_or(StatusCode::BAD_REQUEST)?;
    // Validate verb before spawning so BAD_REQUEST stays synchronous.
    if !matches!(verb.as_str(), "start" | "stop" | "pull" | "push") {
        return Err(StatusCode::BAD_REQUEST);
    }
    let path_for_task = path.clone();
    let verb_for_task = verb.clone();
    let result = tokio::task::spawn_blocking(move || match verb_for_task.as_str() {
        // `launchctl load -w` returns before the sync daemon has written its
        // pid file, and `unload -w` returns before the daemon has cleared it.
        // Wait for the observable state to settle so the SSE event that fires
        // below sees the settled state — otherwise the hub toggle bounces back
        // because GET /sync reports running=false while the daemon is still
        // booting.
        "start" => {
            let r = sync_control::start(backend, &path_for_task);
            if r.is_ok() {
                let _ = sync_control::wait_for_state(
                    backend,
                    &path_for_task,
                    true,
                    std::time::Duration::from_secs(3),
                );
            }
            r
        }
        "stop" => {
            let r = sync_control::stop(backend, &path_for_task);
            if r.is_ok() {
                let _ = sync_control::wait_for_state(
                    backend,
                    &path_for_task,
                    false,
                    std::time::Duration::from_secs(3),
                );
            }
            r
        }
        "pull" => sync_control::pull(backend, &path_for_task),
        "push" => sync_control::push(backend, &path_for_task),
        _ => unreachable!("verb validated above"),
    })
    .await
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    let _ = app.tx.send(SseEvent::StatusChange {
        chamber_id: entry.id.clone(),
    });
    match result {
        Ok(()) => Ok(Json(serde_json::json!({
            "ok": true,
            "message": format!("{} {}", backend.as_str(), verb),
        }))),
        Err(e) => Ok(Json(serde_json::json!({
            "ok": false,
            "message": e.to_string(),
        }))),
    }
}

#[cfg(test)]
#[path = "../../unit_tests/hub/routes/sync.rs"]
mod tests;
