package com.ponzischeme89.memby.data

import androidx.compose.ui.graphics.Color
import com.ponzischeme89.memby.data.model.GatewayPalette
import com.ponzischeme89.memby.ui.theme.MembyPalette

/**
 * Turning the gateway's answer into colours, and nothing else.
 *
 * There is deliberately **no client-side theme rule** here to match — no seasonal
 * calculation, no allowlist, no fallback catalogue. That is the opposite of the choice made
 * for subtitles, intros and Continue Watching, where the rule exists twice because with no
 * gateway there is nobody to ask. The difference is what "nobody to ask" costs: for those,
 * the direct path would behave *differently*, which is a bug. Here it behaves as it always
 * did — the default palette, the one the app shipped with. A television painting itself
 * Halloween orange on the strength of its own clock, while the household's server has the
 * feature switched off, would be the feature failing rather than degrading.
 */

/**
 * Parses `#AARRGGBB` (or `#RRGGBB`) as the gateway writes it.
 *
 * Alpha-first is Android's own order and the reason it is what goes on the wire: this end
 * parses one of these for every colour of every theme change, and the admin console — which
 * parses eight, once — is where the reordering for CSS happens instead.
 *
 * Returns null rather than a colour for anything it cannot read, so the caller can keep the
 * app's own token for that slot. A theme drawn one colour wrong is a blemish; a screen drawn
 * transparent because a hex string had a typo in it is a television nobody can use.
 */
internal fun parseThemeColor(value: String?): Color? {
    val hex = value?.trim()?.removePrefix("#") ?: return null
    if (hex.length != 6 && hex.length != 8) return null
    if (!hex.all { it.isDigit() || it in 'a'..'f' || it in 'A'..'F' }) return null
    val argb = hex.toLongOrNull(16) ?: return null
    // Six digits are opaque. Emby's own artwork colours are written that way and it is the
    // form somebody hand-editing a palette would reach for.
    return Color(if (hex.length == 6) argb or 0xFF000000L else argb)
}

/**
 * The palette to paint with, given what the server sent.
 *
 * [fallback] is the palette currently in force rather than the class defaults, the same
 * distinction [decodeUserPreferences] draws: a response missing a colour must leave that one
 * alone, not silently reset it. That is what lets the gateway grow a token before every
 * television in the house has the release that knows about it — and what makes a partial
 * palette a partial change rather than a half-black screen.
 */
fun GatewayPalette.toMembyPalette(fallback: MembyPalette = MembyPalette()): MembyPalette =
    MembyPalette(
        surface = parseThemeColor(surface) ?: fallback.surface,
        surfaceRaised = parseThemeColor(surfaceRaised) ?: fallback.surfaceRaised,
        accent = parseThemeColor(accent) ?: fallback.accent,
        onSurface = parseThemeColor(onSurface) ?: fallback.onSurface,
        mutedText = parseThemeColor(mutedText) ?: fallback.mutedText,
        quietText = parseThemeColor(quietText) ?: fallback.quietText,
        hairline = parseThemeColor(hairline) ?: fallback.hairline,
        ratingsSurface = parseThemeColor(ratingsSurface) ?: fallback.ratingsSurface,
    )
