package com.correx.core.config

import java.nio.file.Files

/**
 * Serializes a [ProjectProfile] back to TOML text that [ProjectProfileLoader] reads identically:
 * the invariant is `load(write(profile)) == profile` for every field the loader understands
 * (about, conventions, commands).
 *
 * Like [CorrexConfigWriter] this **regenerates** the file from the profile object — it does not
 * preserve hand-written comments. Empty sections are skipped: a blank `about` and an empty
 * `conventions`/`commands` produce no output for that part (the loader reconstructs the defaults
 * from their absence), so a [ProjectProfile.isEmpty] profile serializes to an empty string.
 */
object ProjectProfileWriter {

    /** Renders [profile] as TOML. String values escape `\` and `"`; empty sections are skipped. */
    fun serialize(profile: ProjectProfile): String {
        val b = StringBuilder()

        if (profile.about.isNotBlank()) {
            b.append("about = ").append(str(profile.about)).append('\n')
        }

        if (profile.conventions.isNotEmpty()) {
            b.append("conventions = ").append(list(profile.conventions)).append('\n')
        }

        val flatCommands = profile.commands.filterKeys { '.' !in it }
        val scopedCommands = profile.commands.filterKeys { '.' in it }
            .entries.groupBy({ it.key.substringBefore('.') }, { it.key.substringAfter('.') to it.value })
        if (flatCommands.isNotEmpty()) {
            if (b.isNotEmpty()) b.append('\n')
            b.append("[commands]\n")
            flatCommands.forEach { (key, value) ->
                b.append(key).append(" = ").append(str(value)).append('\n')
            }
        }
        scopedCommands.forEach { (toolchain, commands) ->
            if (b.isNotEmpty()) b.append('\n')
            b.append("[commands.").append(toolchain).append("]\n")
            commands.forEach { (alias, value) ->
                b.append(alias).append(" = ").append(str(value)).append('\n')
            }
        }

        return b.toString()
    }

    /**
     * Writes [profile] to `<workspaceRoot>/.correx/project.toml`, creating the `.correx/` directory
     * if it is missing. Overwrites any existing file (the writer owns the file once a save happens).
     */
    fun write(workspaceRoot: String, profile: ProjectProfile) {
        val path = ProjectProfileLoader.profilePath(workspaceRoot)
        Files.createDirectories(path.parent)
        Files.writeString(path, serialize(profile))
    }

    private fun str(v: String): String = "\"" + v.replace("\\", "\\\\").replace("\"", "\\\"") + "\""

    private fun list(v: List<String>): String = "[" + v.joinToString(", ") { str(it) } + "]"
}

/**
 * Appends [text] to this profile's conventions, de-duplicated by exact string match. A blank
 * [text] is ignored (returns the profile unchanged). Used when promoting an idea-board entry into
 * the curated profile so repeated promotions of the same text don't pile up duplicate conventions.
 */
fun ProjectProfile.withConvention(text: String): ProjectProfile {
    val trimmed = text.trim()
    if (trimmed.isBlank() || trimmed in conventions) return this
    return copy(conventions = conventions + trimmed)
}
