package com.doctate.watch.domain

import org.json.JSONArray
import org.json.JSONObject

/**
 * JSON ↔ Kotlin DTO marshallers for the `/api/oneliners` wire format.
 * Server-side source of truth: `common/src/oneliners.rs`. Field
 * names are snake_case to match the Rust serde output.
 *
 * Lives in `domain` (not `net`) because the parsed shape — including the
 * `OnelinerState` sealed interface — is the in-memory model the rest of
 * the app reasons about, not a transport-only concern.
 */
internal fun parseOnelinersResponse(text: String): OnelinersResponse {
    val obj = JSONObject(text)
    val arr = obj.getJSONArray("oneliners")
    val entries = ArrayList<OnelinerEntry>(arr.length())
    for (i in 0 until arr.length()) {
        entries += parseOnelinerEntry(arr.getJSONObject(i))
    }
    return OnelinersResponse(
        asOf = obj.getString("as_of"),
        windowHours = obj.getInt("window_hours"),
        oneliners = entries,
    )
}

internal fun onelinersResponseToJson(response: OnelinersResponse): String {
    val arr = JSONArray()
    for (e in response.oneliners) arr.put(onelinerEntryToJson(e))
    return JSONObject().apply {
        put("as_of", response.asOf)
        put("window_hours", response.windowHours)
        put("oneliners", arr)
    }.toString()
}

private fun parseOnelinerEntry(obj: JSONObject): OnelinerEntry = OnelinerEntry(
    caseId = obj.getString("case_id"),
    oneliner = obj.optJSONObject("oneliner")?.let { onelinerFromJson(it) },
    createdAt = obj.getString("created_at"),
    lastRecordingAt = obj.optStringOrNull("last_recording_at"),
    updatedAt = obj.optStringOrNull("updated_at"),
)

private fun onelinerEntryToJson(entry: OnelinerEntry): JSONObject = JSONObject().apply {
    put("case_id", entry.caseId)
    put("oneliner", entry.oneliner?.let { onelinerToJson(it) } ?: JSONObject.NULL)
    put("created_at", entry.createdAt)
    put("last_recording_at", entry.lastRecordingAt ?: JSONObject.NULL)
    put("updated_at", entry.updatedAt ?: JSONObject.NULL)
}

/** `optString` returns "" for null/missing; we want a real null. */
private fun JSONObject.optStringOrNull(key: String): String? =
    if (has(key) && !isNull(key)) getString(key) else null
