package no.svorka.mcms.core

import kotlinx.serialization.Serializable
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import java.net.URI

/** How to evaluate the server's TLS certificate. */
@Serializable
sealed class ServerTrustPolicy {
    /** Default platform trust evaluation (system CA store). The only default. */
    @Serializable
    data object System : ServerTrustPolicy()

    /**
     * Trust only if the leaf public-key SHA-256 (base64) matches one of these.
     * This is the secure way to talk to a known box with a private/self-signed
     * cert: pin its key rather than disabling validation. No UI wires this up
     * yet — set it programmatically if a deployment needs it.
     */
    @Serializable
    data class PinPublicKeySha256(val pins: List<String>, val host: String) : ServerTrustPolicy()
}

/**
 * Connection configuration for the MCMS REST API. URL-driven so the same build
 * works direct or via reverse proxy. `baseUrl` includes the `/api` prefix and is
 * always HTTPS, e.g. `https://mcms.lab.svorka.net/api`.
 */
@Serializable
data class ApiConfiguration(
    val baseUrl: String,
    val databaseId: String? = null,
    val refererOverride: String? = null,
    val sessionCookieName: String = "sessionid",
    val csrfCookieName: String = "csrftoken",
    val trustPolicy: ServerTrustPolicy = ServerTrustPolicy.System,
    val timeoutSeconds: Long = 30,
) {
    val refererValue: String get() = refererOverride ?: baseUrl

    /**
     * Host as OkHttp parses it — must match the cookie jar's `HttpUrl.host` so the
     * CSRF/session cookie lookup in [SessionStore] resolves against the same key.
     */
    val host: String? get() = baseUrl.toHttpUrlOrNull()?.host

    companion object {
        /**
         * Normalises a user-entered URL and **enforces HTTPS**: defaults a missing
         * scheme to `https`, rejects anything that isn't `https`, rejects embedded
         * credentials, trims a trailing slash, and defaults the path to `/api` when
         * none is given. Returns null if it isn't a usable HTTPS URL.
         */
        fun normalizedBaseUrl(raw: String): String? {
            val trimmed = raw.trim()
            if (trimmed.isEmpty()) return null
            // Default to https:// when the user omits the scheme (e.g. "mcms.lab/api").
            val withScheme =
                if (Regex("^[a-zA-Z][a-zA-Z0-9+.-]*://").containsMatchIn(trimmed)) trimmed
                else "https://$trimmed"
            val uri = runCatching { URI(withScheme) }.getOrNull() ?: return null
            if (uri.scheme?.lowercase() != "https") return null   // TLS only
            if (uri.userInfo != null) return null                 // no credentials in the URL
            val host = uri.host?.takeIf { it.isNotEmpty() } ?: return null
            var path = uri.path ?: ""
            while (path.endsWith("/")) path = path.dropLast(1)
            if (path.isEmpty()) path = "/api"
            val port = if (uri.port >= 0) ":${uri.port}" else ""
            return "https://$host$port$path"
        }
    }
}
