package app.closer.crypto

import java.security.MessageDigest
import java.util.Base64
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Computes a SHA-256 commitment hash over the answer payload at submit time.
 *
 * The hash binds the plaintext content to the couple, question, and user so that
 * swapping or mutating the answer after submission is detectable.
 *
 * Input: "v1|{coupleId}|{questionId}|{userId}|{canonicalJson}"
 * Canonical JSON has fixed key order (scaleValue, selectedOptionIds, writtenText)
 * and sorted selectedOptionIds, so the hash is stable across serialisations.
 *
 * Wire format: "sha256:{urlsafe-base64-no-padding}"
 */
@Singleton
class AnswerCommitment @Inject constructor() {

    fun compute(
        coupleId: String,
        questionId: String,
        userId: String,
        writtenText: String?,
        selectedOptionIds: List<String>,
        scaleValue: Int?
    ): String {
        val canonical = canonical(writtenText, selectedOptionIds, scaleValue)
        val input = "v1|$coupleId|$questionId|$userId|$canonical"
        val hash = MessageDigest.getInstance("SHA-256")
            .digest(input.toByteArray(Charsets.UTF_8))
        return "sha256:${Base64.getUrlEncoder().withoutPadding().encodeToString(hash)}"
    }

    /**
     * Produces a stable JSON string with alphabetically ordered keys and
     * sorted selectedOptionIds. Never uses Android's JSONObject so the output
     * is identical on any JVM platform (required for iOS cross-validation).
     */
    internal fun canonical(
        writtenText: String?,
        selectedOptionIds: List<String>,
        scaleValue: Int?
    ): String {
        val ids = selectedOptionIds.sorted().joinToString(",") { "\"${escape(it)}\"" }
        val text = if (writtenText != null) "\"${escape(writtenText)}\"" else "null"
        val scale = scaleValue?.toString() ?: "null"
        return "{\"scaleValue\":$scale,\"selectedOptionIds\":[$ids],\"writtenText\":$text}"
    }

    private fun escape(s: String): String = buildString {
        for (c in s) when (c) {
            '\\' -> append("\\\\")
            '"'  -> append("\\\"")
            '\n' -> append("\\n")
            '\r' -> append("\\r")
            '\t' -> append("\\t")
            else -> append(c)
        }
    }

    companion object {
        const val PREFIX = "sha256:"
    }
}
