package wang.yaojia.webterm.hostregistry

import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

/**
 * At-rest DTO for a [Host]. Only the raw dial string [baseUrl] is persisted — NOT
 * the derived Origin/WS values — so a stored record can never smuggle a mismatched
 * Origin past the CSWSH defence: [HostEndpoint][wang.yaojia.webterm.wire.HostEndpoint]
 * is re-derived from [baseUrl] on load. `@Serializable` (the serialization compiler
 * plugin generates the serializer; composes with AGP 9's built-in Kotlin).
 */
@Serializable
internal data class PersistedHost(
    val id: String,
    val name: String,
    val baseUrl: String,
    val hasDeviceCert: Boolean = false,
)

/**
 * Pure JSON codec for the persisted host list (shared by [DataStoreHostStore],
 * JVM-testable without a Context). Encode is total; decode is defensive — host
 * records are untrusted at rest:
 *  - a corrupt/undecodable blob → empty list (start clean, never crash);
 *  - a record whose [PersistedHost.baseUrl] no longer validates → dropped
 *    (reconstructed via [Host.create]).
 */
internal object HostCodec {
    private val json = Json {
        ignoreUnknownKeys = true
        isLenient = true
        encodeDefaults = true
    }

    fun encode(hosts: List<Host>): String =
        json.encodeToString(hosts.map { it.toPersisted() })

    fun decode(raw: String?): List<Host> {
        if (raw.isNullOrBlank()) return emptyList()
        val persisted = try {
            json.decodeFromString<List<PersistedHost>>(raw)
        } catch (_: Exception) {
            return emptyList() // corrupted blob at rest → start clean, never crash
        }
        // mapNotNull drops records whose baseUrl fails re-validation (untrusted at rest).
        return persisted.mapNotNull { it.toHost() }
    }

    private fun Host.toPersisted(): PersistedHost =
        PersistedHost(
            id = id,
            name = name,
            baseUrl = endpoint.baseUrl,
            hasDeviceCert = hasDeviceCert,
        )

    private fun PersistedHost.toHost(): Host? =
        Host.create(id = id, name = name, baseUrl = baseUrl, hasDeviceCert = hasDeviceCert)
}
