use std::sync::Arc;
use axum::extract::{Path, State};
use axum::Json;
use serde::Deserialize;
use uuid::Uuid;
use oxios_gateway::message::IncomingMessage;
use oxios_kernel::{CronJob, Priority};
use crate::error::AppError;
use crate::server::AppState;
#[derive(Debug, Deserialize)]
pub struct CreateCronJobRequest {
pub name: String,
pub schedule: String,
pub goal: String,
#[serde(default)]
pub constraints: Vec<String>,
#[serde(default)]
pub acceptance_criteria: Vec<String>,
#[serde(default = "default_toolchain")]
pub toolchain: String,
#[serde(default)]
pub priority: Priority,
}
fn default_toolchain() -> String {
"default".into()
}
pub(crate) async fn handle_cron_jobs_list(
state: State<Arc<AppState>>,
) -> Result<Json<serde_json::Value>, AppError> {
let jobs = state.kernel.infra.list_crons();
Ok(Json(serde_json::json!({ "jobs": jobs })))
}
pub(crate) async fn handle_cron_job_create(
state: State<Arc<AppState>>,
Json(body): Json<CreateCronJobRequest>,
) -> Result<Json<serde_json::Value>, AppError> {
let mut job = CronJob::new(body.name, body.schedule, body.goal);
job.constraints = body.constraints;
job.acceptance_criteria = body.acceptance_criteria;
job.toolchain = body.toolchain;
job.priority = body.priority;
let id = state
.kernel
.infra
.add_cron(job)
.await
.map_err(|e| AppError::Internal(e.to_string()))?;
Ok(Json(serde_json::json!({ "id": id })))
}
pub(crate) async fn handle_cron_job_get(
state: State<Arc<AppState>>,
Path(id): Path<Uuid>,
) -> Result<Json<CronJob>, AppError> {
state
.kernel
.infra
.get_cron(id)
.map(Json)
.ok_or_else(|| AppError::NotFound(format!("Cron job {} not found", id)))
}
pub(crate) async fn handle_cron_job_delete(
state: State<Arc<AppState>>,
Path(id): Path<Uuid>,
) -> Result<Json<serde_json::Value>, AppError> {
state
.kernel
.unschedule(&id.to_string())
.await
.map_err(|e| AppError::Internal(e.to_string()))?;
Ok(Json(serde_json::json!({ "deleted": id })))
}
pub(crate) async fn update_cron_job(
state: State<Arc<AppState>>,
Path(id): Path<Uuid>,
Json(body): Json<serde_json::Value>,
) -> Result<Json<serde_json::Value>, AppError> {
let update: oxios_kernel::CronJobUpdate =
serde_json::from_value(body).map_err(|e| AppError::BadRequest(e.to_string()))?;
state
.kernel
.infra
.update_cron(id, update)
.await
.map_err(|e| AppError::Internal(e.to_string()))?;
Ok(Json(serde_json::json!({ "updated": id })))
}
pub(crate) async fn handle_cron_job_trigger(
state: State<Arc<AppState>>,
Path(id): Path<Uuid>,
) -> Result<Json<serde_json::Value>, AppError> {
let job = state
.kernel
.infra
.trigger_cron(id)
.map_err(|e| AppError::Internal(e.to_string()))?;
tracing::info!(job_id = %id, job_name = %job.name, "Triggering cron job");
let mut msg = IncomingMessage::new("cron", "system", &job.goal);
msg.metadata
.insert("toolchain".to_string(), job.toolchain.clone());
let response = state
.channel
.send_and_wait(msg)
.await
.map_err(|e| AppError::Internal(e.to_string()))?;
let success = response
.metadata
.get("evaluation_passed")
.and_then(|v| v.parse().ok())
.unwrap_or(false);
let summary = response
.metadata
.get("output")
.cloned()
.unwrap_or_else(|| response.content.clone());
state
.kernel
.infra
.complete_cron(id, success, summary.clone())
.await;
Ok(Json(serde_json::json!({
"job_id": id,
"success": success,
"summary": summary,
})))
}