package no.svorka.mcms.core

import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl
import okhttp3.OkHttpClient

/**
 * In-memory cookie jar (per host), so we can both replay cookies and read the
 * CSRF cookie value. Sessions are discarded on logout/app-kill.
 *
 * OkHttp invokes the jar from its background dispatcher threads, so every access
 * to [store] is synchronised on this instance.
 */
class SessionCookieJar : CookieJar {
    private val store = mutableMapOf<String, MutableList<Cookie>>()

    @Synchronized
    override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
        val list = store.getOrPut(url.host) { mutableListOf() }
        cookies.forEach { c ->
            list.removeAll { it.name == c.name }
            list.add(c)
        }
    }

    @Synchronized
    override fun loadForRequest(url: HttpUrl): List<Cookie> =
        store[url.host]?.filter { it.matches(url) } ?: emptyList()

    @Synchronized
    fun valueMatching(host: String, base: String): String? =
        store[host]?.firstOrNull { matches(it.name, base) }?.value

    @Synchronized
    fun hasCookie(host: String, base: String): Boolean =
        store[host]?.any { matches(it.name, base) } == true

    @Synchronized
    fun clear(host: String) { store.remove(host) }

    // Modern MCMS builds emit `__Host-`/`__Secure-` prefixed cookies; accept either.
    private fun matches(name: String, base: String) =
        name == base || name == "__Host-$base" || name == "__Secure-$base"
}

/** Session state for one connection: the cookie jar + CSRF token access. */
class SessionStore(private val config: ApiConfiguration) {
    val cookieJar = SessionCookieJar()
    var isAuthenticated = false
        private set

    fun markAuthenticated() { isAuthenticated = true }

    /** CSRF token to echo as the `X-CSRFToken` header. Null before login. */
    fun csrfToken(): String? = config.host?.let { cookieJar.valueMatching(it, config.csrfCookieName) }

    val hasSessionCookie: Boolean
        get() = config.host?.let { cookieJar.hasCookie(it, config.sessionCookieName) } == true

    fun clear() {
        config.host?.let { cookieJar.clear(it) }
        isAuthenticated = false
    }
}

/** Applies a [ServerTrustPolicy] to an OkHttp client builder. */
object ServerTrust {
    fun apply(builder: OkHttpClient.Builder, policy: ServerTrustPolicy) {
        when (policy) {
            // Default platform trust (system CA store). Untrusted/self-signed
            // certificates are intentionally NOT accepted — pin instead.
            is ServerTrustPolicy.System -> Unit
            is ServerTrustPolicy.PinPublicKeySha256 -> {
                val pinner = okhttp3.CertificatePinner.Builder().apply {
                    policy.pins.forEach { add(policy.host, "sha256/$it") }
                }.build()
                builder.certificatePinner(pinner)
            }
        }
    }
}
