package aoc2025

import println
import readInput

/*
--- Day 11: Reactor ---
https://adventofcode.com/2025/day/11
*/
fun main() {

    val inlineTestInput = """
aaa: you hhh
you: bbb ccc
bbb: ddd eee
ccc: ddd eee fff
ddd: ggg
eee: out
fff: out
ggg: out
hhh: ccc fff iii
iii: out
"""

    val inlineTestInput2 = """
svr: aaa bbb
aaa: fft
fft: ccc
bbb: tty
tty: ccc
ccc: ddd eee
ddd: hub
hub: fff
eee: dac
dac: fff
fff: ggg hhh
ggg: out
hhh: out
"""

    data class Node(val name: String, val children: Set<Node>)

    fun rec(children: HashMap<String, MutableSet<String>>, p: String, visited: MutableSet<String>): Int {
        if (p == "out") return 1
        visited.add(p)
        val res = children[p]!!.sumOf { rec(children, it, visited) }
        visited.remove(p)
        return res
    }

    fun parseInput(input: List<String>): HashMap<String, MutableSet<String>> {
        val children = HashMap<String, MutableSet<String>>()
        for (i in input) {
            val (n, chs) = i.split(": ")
            val ch = chs.split(" ")
            children.getOrPut(n) { HashSet() }.addAll(ch)
        }
        return children
    }

    fun part1(input: List<String>): Int {
        val children = parseInput(input)

        return rec(children, "you", HashSet())
    }

    fun rec2(
        children: HashMap<String, MutableSet<String>>,
        p: String,
        stop: String,
        visited: MutableSet<String> = HashSet(),
        memo: HashMap<String, Long> = HashMap()
    ): Long {
        if (p == stop) return 1L
        val memoVal = memo[p]
        if (memoVal != null) return memoVal
        visited.add(p)
        val res = children[p]?.sumOf { rec2(children, it, stop, visited, memo) } ?: 0L
        visited.remove(p)
        memo[p] = res
        return res
    }

    fun part2(input: List<String>): Long {
        val children = parseInput(input)

        val srvToDac = rec2(children, "svr", "dac")
        val dacToFft = rec2(children, "dac", "fft")
        val fftToOut = rec2(children, "fft", "out")
        val srvToFft = rec2(children, "svr", "fft")
        val fftToDac = rec2(children, "fft", "dac")
        val dacToOut = rec2(children, "dac", "out")

        return srvToDac * dacToFft * fftToOut + srvToFft * fftToDac * dacToOut
    }

    // test if implementation meets criteria from the description, like:
    val testInput = inlineTestInput.trim().reader().readLines()
    val testInput2 = inlineTestInput2.trim().reader().readLines()
    //val testInput = readInput("aoc2025/Day11_test")
    val testInputPart1Result = part1(testInput)
    println("Part 1 Test: $testInputPart1Result")
    val testInputPart2Result = part2(testInput2)
    println("Part 2 Test: $testInputPart2Result")
    check(testInputPart1Result == 5)
    check(testInputPart2Result == 2L)

    val input = readInput("aoc2025/Day11")
    part1(input).println()
    part2(input).println()
}
