package dayseven

import (
	"advent-of-code/internal/registry"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var (
	rulePattern      = regexp.MustCompile(`^(\w+ \w+) bags contain (.+)\.$`)
	containedPattern = regexp.MustCompile(`(\d+) (\w+ \w+) bags?`)
)

type bagCount struct {
	count int
	bag   string
}

func init() {
	registry.Register("2020D7", ParseInput, PartOne, PartTwo)
}

func buildForwardGraph(data []string) map[string][]bagCount {
	forwardGraph := make(map[string][]bagCount)

	for _, line := range data {
		matches := rulePattern.FindStringSubmatch(line)
		if len(matches) != 3 {
			continue
		}

		containerBag := matches[1]
		containedString := matches[2]

		if containedString == "no other bags" {
			forwardGraph[containerBag] = []bagCount{}
			continue
		}

		containedMatches := containedPattern.FindAllStringSubmatch(containedString, -1)
		for _, match := range containedMatches {
			if len(match) >= 3 {
				count, _ := strconv.Atoi(match[1])
				containedBag := match[2]
				forwardGraph[containerBag] = append(forwardGraph[containerBag], bagCount{count: count, bag: containedBag})
			}
		}
	}

	return forwardGraph
}

func ParseInput(filepath string) []string {
	content, _ := os.ReadFile(filepath)
	return strings.Split(string(content), "\n")
}

func PartOne(data []string) int {
	forwardGraph := buildForwardGraph(data)
	reverseGraph := make(map[string][]string)

	for container, contained := range forwardGraph {
		for _, child := range contained {
			reverseGraph[child.bag] = append(reverseGraph[child.bag], container)
		}
	}

	visited := make(map[string]bool)
	queue := reverseGraph["shiny gold"]

	for len(queue) > 0 {
		current := queue[0]
		queue = queue[1:]

		if visited[current] {
			continue
		}
		visited[current] = true

		for _, parent := range reverseGraph[current] {
			if !visited[parent] {
				queue = append(queue, parent)
			}
		}
	}

	return len(visited)
}

func PartTwo(data []string) int {
	forwardGraph := buildForwardGraph(data)
	var countIndividualBags func(string) int

	countIndividualBags = func(bag string) int {
		total := 0
		for _, child := range forwardGraph[bag] {
			total += child.count + child.count*countIndividualBags(child.bag)
		}
		return total
	}

	return countIndividualBags("shiny gold")
}
