package com.ledgrab.android

import android.content.Context
import android.util.Log
import java.security.SecureRandom

/**
 * Persists the per-install API key for the embedded FastAPI server.
 *
 * The server's auth gate ([ledgrab.api.auth]) requires a Bearer token
 * for any non-loopback request when ``auth.api_keys`` is configured.
 * Without a key, LAN clients (phone, laptop) get 401 — which is the
 * server's secure default but breaks the QR-scan workflow.
 *
 * This class generates one key per install (random 32-byte → 64-char
 * hex), persists it to SharedPreferences, and exposes it to:
 *  - [PythonBridge] which sets ``LEDGRAB_AUTH__API_KEYS=android:<key>``
 *    before uvicorn starts.
 *  - [MainActivity] which embeds the key as a URL fragment
 *    (``http://ip:port/#k=<key>``) in the QR. Fragments are never sent
 *    to the server in HTTP requests, so the key doesn't appear in
 *    access logs.
 */
class ApiKeyManager(context: Context) {

    private val prefs = context.applicationContext
        .getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

    // Once we've materialised a key in this process, cache it so
    // subsequent reads don't hit prefs and don't risk re-checking
    // length under contention.
    @Volatile private var cached: String? = null
    private val lock = Any()

    /** Persistent random API key, generated lazily on first access. */
    val apiKey: String
        get() = getOrCreateKey()

    /** Force a new key. Useful if a user thinks the QR was photographed. */
    fun rotate(): String {
        synchronized(lock) {
            val next = generateKey()
            // apply() is fine for rotation — by definition the user
            // initiated this and will see the new QR; the worst case
            // on crash is they need to re-rotate.
            prefs.edit().putString(KEY_API_KEY, next).apply()
            cached = next
            Log.i(TAG, "Rotated API key")
            return next
        }
    }

    private fun getOrCreateKey(): String {
        cached?.let { return it }
        synchronized(lock) {
            // Double-checked under the lock.
            cached?.let { return it }
            val existing = prefs.getString(KEY_API_KEY, null)
            if (existing != null && existing.length >= MIN_KEY_LENGTH) {
                cached = existing
                return existing
            }
            val generated = generateKey()
            // commit() (synchronous disk write) on the FIRST write so
            // the key is durable before MainActivity encodes it into a
            // QR. If the process is killed between QR display and the
            // async write landing, the user's phone would scan a key
            // the server never learned about. Subsequent rotates can
            // safely use apply().
            prefs.edit().putString(KEY_API_KEY, generated).commit()
            cached = generated
            Log.i(TAG, "Generated new API key (length=${generated.length})")
            return generated
        }
    }

    private fun generateKey(): String {
        val bytes = ByteArray(KEY_BYTES)
        SecureRandom().nextBytes(bytes)
        // Hex-encode so the key survives copy/paste, URL fragments, env
        // vars, and YAML config without escaping concerns. Mask to 0xff
        // first — Kotlin's Byte is signed, and `%02x` on a negative
        // Byte sign-extends to an 8-char hex string ("ffffffff" instead
        // of "ff"), which would produce an invalid key.
        return bytes.joinToString("") { "%02x".format(it.toInt() and 0xff) }
    }

    companion object {
        private const val TAG = "ApiKeyManager"
        private const val PREFS_NAME = "ledgrab_auth"
        private const val KEY_API_KEY = "api_key"
        private const val KEY_BYTES = 32
        private const val MIN_KEY_LENGTH = 32

        /** Label used as the LEDGRAB_AUTH__API_KEYS map key. */
        const val LABEL = "android"
    }
}
