package aoc2024

import println
import readInput

/*
--- Day 23: LAN Party ---
https://adventofcode.com/2024/day/23
*/
fun main() {

    val inlineTestInput = """
kh-tc
qp-kh
de-cg
ka-co
yn-aq
qp-ub
cg-tb
vc-aq
tb-ka
wh-tc
yn-cg
kh-ub
ta-co
de-co
tc-td
tb-wq
wh-td
ta-ka
td-qp
aq-cg
wq-ub
ub-vc
de-ta
wq-aq
wq-vc
wh-yn
ka-de
kh-ta
co-tc
wh-qp
tb-vc
td-yn
"""

    fun part1(input: List<String>): Int {
        val nodes = HashMap<String, MutableSet<String>>()
        for (i in input) {
            val (n1, n2) = i.split("-")
            val e1 = nodes.getOrPut(n1) { HashSet() }
            val e2 = nodes.getOrPut(n2) { HashSet() }
            e1.add(n2)
            e2.add(n1)
        }
        val threesomes = nodes
            .flatMap {
                it.value.flatMap { i1 ->
                    it.value.filter { it != i1 }.mapNotNull { i2 ->
                        if (nodes[i1]!!.contains(it.key) && nodes[i2]!!.contains(it.key) && nodes[i1]!!.contains(i2)) sortedSetOf(it.key, i1, i2) else null
                    }
                }
            }.map { it.joinToString("-", prefix = "-") }.distinct()

        return threesomes.count { it.contains("-t") }
    }

    fun bronKerbosch(nodes: Map<String, Set<String>>, r: Set<String>, p: Set<String>, x: MutableSet<String>): Set<String> {
        if (p.isEmpty() && x.isEmpty()) {
            return r
        }
        var maxClique = emptySet<String>()
        val ptmp = p.toMutableSet()
        for (v in p) {
            val res = bronKerbosch(nodes, r.plus(v), ptmp.intersect(nodes[v]!!), x.intersect(nodes[v]!!).toMutableSet())
            if (res.size > maxClique.size) {
                maxClique = res
            }
            ptmp.remove(v)
            x.add(v)
        }
        return maxClique
    }

    fun part2(input: List<String>): String {
        val nodes = HashMap<String, MutableSet<String>>()
        for (i in input) {
            val (n1, n2) = i.split("-")
            val e1 = nodes.getOrPut(n1) { HashSet() }
            val e2 = nodes.getOrPut(n2) { HashSet() }
            e1.add(n2)
            e2.add(n1)
        }
        val largestSet = bronKerbosch(nodes, emptySet(), nodes.keys, mutableSetOf())
        return largestSet.asSequence().sorted().joinToString(",")
    }

    // test if implementation meets criteria from the description, like:
    val testInput = inlineTestInput.trim().reader().readLines()
    //val testInput = readInput("aoc2024/Day23_test")
    val testInputPart1Result = part1(testInput)
    println("Part 1 Test: $testInputPart1Result")
    val testInputPart2Result = part2(testInput)
    println("Part 2 Test: $testInputPart2Result")
    check(testInputPart1Result == 7)
    check(testInputPart2Result == "co,de,ka,ta")

    val input = readInput("aoc2024/Day23")
    part1(input).println()
    part2(input).println()
}
