package com.doctate.watch.domain

import org.json.JSONObject
import java.time.Instant

/**
 * On-disk representation of a single case. Wire-format identical to
 * `doctate-desktop::CaseMarker` (Rust) — same field names (snake_case),
 * same RFC3339 timestamps, same internally-tagged OnelinerState. A marker
 * file written by either client should round-trip through the other.
 *
 * Kept disjoint from [CaseEntry] so the in-memory UI representation can
 * use Long epoch-ms (faster to format) without dragging the wire format
 * along. Conversion happens in [DiskCaseStore] at the IO boundary.
 */
internal data class CaseMarker(
    val caseId: String,
    val createdAtRfc: String,
    val lastActivityAtRfc: String,
    val syncedToServer: Boolean,
    val oneliner: OnelinerState?,
) {
    fun toJson(): String = JSONObject().apply {
        put("case_id", caseId)
        put("created_at", createdAtRfc)
        put("last_activity_at", lastActivityAtRfc)
        put("synced_to_server", syncedToServer)
        put("oneliner", oneliner?.let { onelinerToJson(it) } ?: JSONObject.NULL)
    }.toString(2)

    fun toEntry(): CaseEntry = CaseEntry(
        caseId = caseId,
        createdAt = Instant.parse(createdAtRfc).toEpochMilli(),
        lastActivityAt = Instant.parse(lastActivityAtRfc).toEpochMilli(),
        oneliner = oneliner,
        syncedToServer = syncedToServer,
    )

    companion object {
        fun fromJson(text: String): CaseMarker {
            val obj = JSONObject(text)
            return CaseMarker(
                caseId = obj.getString("case_id"),
                createdAtRfc = obj.getString("created_at"),
                lastActivityAtRfc = obj.getString("last_activity_at"),
                syncedToServer = obj.optBoolean("synced_to_server", false),
                oneliner = obj.optJSONObject("oneliner")?.let { onelinerFromJson(it) },
            )
        }
    }
}

/** Encode `OnelinerState` as the same internally-tagged JSON the server emits. */
internal fun onelinerToJson(state: OnelinerState): JSONObject = when (state) {
    is OnelinerState.Ready -> JSONObject()
        .put("kind", "ready")
        .put("text", state.text)
        .put("generated_at", state.generatedAt)
    is OnelinerState.Empty -> JSONObject()
        .put("kind", "empty")
        .put("generated_at", state.generatedAt)
    is OnelinerState.Error -> JSONObject()
        .put("kind", "error")
        .put("generated_at", state.generatedAt)
    is OnelinerState.Manual -> JSONObject()
        .put("kind", "manual")
        .put("text", state.text)
        .put("set_at", state.setAt)
}

/** Decode an internally-tagged `OnelinerState`. Unknown kinds raise IllegalArgumentException. */
internal fun onelinerFromJson(obj: JSONObject): OnelinerState =
    when (val kind = obj.getString("kind")) {
        "ready" -> OnelinerState.Ready(
            text = obj.getString("text"),
            generatedAt = obj.getString("generated_at"),
        )
        "empty" -> OnelinerState.Empty(generatedAt = obj.getString("generated_at"))
        "error" -> OnelinerState.Error(generatedAt = obj.getString("generated_at"))
        "manual" -> OnelinerState.Manual(
            text = obj.getString("text"),
            setAt = obj.getString("set_at"),
        )
        else -> throw IllegalArgumentException("unknown OnelinerState kind: $kind")
    }
