#![allow(unused_imports)]

use super::*;

pub(crate) fn output_path_from_payload(payload: &Value) -> Option<String> {
    payload
        .get("output")
        .and_then(|output| output.get("path"))
        .and_then(Value::as_str)
        .map(ToString::to_string)
        .or_else(|| {
            payload
                .get("output")
                .and_then(|output| output.get("files"))
                .and_then(Value::as_array)
                .and_then(|files| {
                    files
                        .iter()
                        .find(|file| file.get("index").and_then(Value::as_u64) == Some(0))
                })
                .and_then(|file| file.get("path"))
                .and_then(Value::as_str)
                .map(ToString::to_string)
        })
}

pub(crate) struct JobSnapshotInput<'a> {
    pub(crate) id: &'a str,
    pub(crate) command: &'a str,
    pub(crate) provider: &'a str,
    pub(crate) status: &'a str,
    pub(crate) created_at: &'a str,
    pub(crate) metadata: Value,
    pub(crate) output_path: Option<String>,
    pub(crate) outputs: Value,
    pub(crate) error: Value,
}

pub(crate) fn job_snapshot(input: JobSnapshotInput<'_>) -> Value {
    json!({
        "id": input.id,
        "command": input.command,
        "provider": input.provider,
        "status": input.status,
        "created_at": input.created_at,
        "updated_at": chrono_like_now(),
        "metadata": input.metadata,
        "outputs": input.outputs,
        "output_path": input.output_path,
        "error": input.error,
    })
}

pub(crate) fn append_updated_at(metadata: &mut Value) {
    if let Value::Object(object) = metadata {
        object.insert("updated_at".to_string(), json!(chrono_like_now()));
    }
}

pub(crate) fn persist_job(job: &Value) -> Result<(), String> {
    let id = job
        .get("id")
        .and_then(Value::as_str)
        .ok_or_else(|| "Job id is missing.".to_string())?;
    let command = job
        .get("command")
        .and_then(Value::as_str)
        .unwrap_or("images generate");
    let provider = job
        .get("provider")
        .and_then(Value::as_str)
        .unwrap_or("auto");
    let status = job
        .get("status")
        .and_then(Value::as_str)
        .unwrap_or("queued");
    let created_at = job.get("created_at").and_then(Value::as_str);
    let output_path = job
        .get("output_path")
        .and_then(Value::as_str)
        .map(PathBuf::from);
    let mut metadata = job.get("metadata").cloned().unwrap_or_else(|| json!({}));
    append_updated_at(&mut metadata);
    if let Value::Object(object) = &mut metadata {
        if let Some(outputs) = job.get("outputs")
            && !outputs.is_null()
        {
            object.insert(
                "output".to_string(),
                json!({
                    "path": job.get("output_path").cloned().unwrap_or(Value::Null),
                    "files": outputs,
                }),
            );
        }
        if let Some(error) = job.get("error")
            && !error.is_null()
        {
            object.insert("error".to_string(), error.clone());
        }
    }
    upsert_history_job(
        id,
        command,
        provider,
        status,
        output_path.as_deref(),
        created_at,
        metadata,
    )
    .map_err(app_error)
}

pub(crate) fn job_from_payload(
    payload: &Value,
    fallback_id: &str,
    command: &str,
    request: Value,
) -> Value {
    let job_id = payload
        .get("history")
        .and_then(|history| history.get("job_id"))
        .and_then(Value::as_str)
        .unwrap_or(fallback_id);
    let output = payload.get("output").cloned().unwrap_or_else(|| json!({}));
    let output_path = output.get("path").and_then(Value::as_str).or_else(|| {
        output
            .get("files")
            .and_then(Value::as_array)
            .and_then(|files| {
                files
                    .iter()
                    .find(|file| file.get("index").and_then(Value::as_u64) == Some(0))
            })
            .and_then(|file| file.get("path"))
            .and_then(Value::as_str)
    });
    json!({
        "id": job_id,
        "command": command,
        "provider": payload.get("provider").cloned().unwrap_or(Value::Null),
        "status": payload
            .get("status")
            .and_then(Value::as_str)
            .unwrap_or_else(|| if payload.get("ok").and_then(Value::as_bool).unwrap_or(false) { "completed" } else { "failed" }),
        "created_at": chrono_like_now(),
        "updated_at": chrono_like_now(),
        "metadata": request,
        "outputs": output.get("files").cloned().unwrap_or_else(|| json!([])),
        "output_path": output_path,
        "error": payload.get("error").cloned(),
    })
}

pub(crate) fn chrono_like_now() -> String {
    let secs = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs();
    format!("{secs}")
}
