// SPDX-FileCopyrightText: 2026 Reactorcoremeltdown
// SPDX-License-Identifier: GPL-3.0-or-later

package space.rcmd.android.sizzle.io

import androidx.compose.ui.graphics.Color
import space.rcmd.android.sizzle.ui.theme.RetroPalette

/**
 * Reads and writes a colour [RetroPalette] as plain, human-editable text — the same
 * `SIZZLE-THEME 1` format the Settings tab exports, so a person can hand-tweak a
 * theme and it round-trips losslessly:
 *
 *     SIZZLE-THEME 1
 *     NAME=Amber CRT
 *     background=#0B0D0E
 *     surface=#15181A
 *     ...
 *
 * Parsing is lenient about individual colour lines (a missing/garbled colour falls
 * back to the default palette's value) but rejects text that isn't a theme at all,
 * so importing a wrong file fails cleanly instead of producing a broken palette.
 */
object ThemeIo {
    const val HEADER = "SIZZLE-THEME 1"

    fun export(p: RetroPalette): String = buildString {
        appendLine(HEADER)
        appendLine("NAME=${p.name}")
        appendLine("background=${hex(p.background)}")
        appendLine("surface=${hex(p.surface)}")
        appendLine("grid=${hex(p.grid)}")
        appendLine("text=${hex(p.text)}")
        appendLine("textDim=${hex(p.textDim)}")
        appendLine("accent=${hex(p.accent)}")
        appendLine("beat=${hex(p.beat)}")
        appendLine("bar=${hex(p.bar)}")
        appendLine("playhead=${hex(p.playhead)}")
        appendLine("danger=${hex(p.danger)}")
    }

    /** Parse theme [text] into a [RetroPalette], or null if it isn't a valid theme. */
    fun parse(text: String): RetroPalette? {
        val map = HashMap<String, String>()
        var sawHeader = false
        text.lineSequence().map { it.trim() }.filter { it.isNotEmpty() }.forEach { line ->
            if (line == HEADER) { sawHeader = true; return@forEach }
            val eq = line.indexOf('=')
            if (eq > 0) map[line.substring(0, eq).trim()] = line.substring(eq + 1).trim()
        }
        if (!sawHeader) return null
        val name = map["NAME"]?.takeIf { it.isNotBlank() } ?: return null
        val d = RetroPalette.AMBER // per-colour fallback
        return RetroPalette(
            name = name,
            background = color(map["background"], d.background),
            surface = color(map["surface"], d.surface),
            grid = color(map["grid"], d.grid),
            text = color(map["text"], d.text),
            textDim = color(map["textDim"], d.textDim),
            accent = color(map["accent"], d.accent),
            beat = color(map["beat"], d.beat),
            bar = color(map["bar"], d.bar),
            playhead = color(map["playhead"], d.playhead),
            danger = color(map["danger"], d.danger),
        )
    }

    private fun hex(c: Color): String =
        "#%02X%02X%02X".format((c.red * 255).toInt(), (c.green * 255).toInt(), (c.blue * 255).toInt())

    /** Parse "#RRGGBB" (or "RRGGBB") into an opaque colour; [fallback] on any error. */
    private fun color(s: String?, fallback: Color): Color {
        val hex = s?.trim()?.removePrefix("#") ?: return fallback
        if (hex.length != 6) return fallback
        val rgb = hex.toLongOrNull(16) ?: return fallback
        return Color(0xFF000000L or (rgb and 0xFFFFFF))
    }
}
