package main

import (
	"fmt"
	"math"
	"strings"

	"github.com/alexchao26/advent-of-code-go/cast"
	"github.com/alexchao26/advent-of-code-go/mathy"
	"github.com/alexchao26/advent-of-code-go/util"
)

func main() {
	min, max := travelingSalesman(util.ReadFile("./input.txt"))
	fmt.Printf("Part1: %d\nPart2: %d\n", min, max)
}

func travelingSalesman(input string) (int, int) {
	graph := newGraphFromInput(input)

	min := math.MaxInt32
	max := 0
	for k := range graph {
		dfsMin, dfsMax := dfsTotalDistance(graph, k, map[string]bool{k: true})
		min = mathy.MinInt(min, dfsMin)
		max = mathy.MaxInt(max, dfsMax)
	}

	return min, max
}

func dfsTotalDistance(graph mapGraph, entry string, visited map[string]bool) (min, max int) {
	// if all nodes have been visited, return a zero length
	if len(visited) == len(graph) {
		return 0, 0
	}

	minDistance := math.MaxInt32
	maxDistance := 0

	for k := range graph {
		if !visited[k] {
			visited[k] = true

			weight := graph[entry][k]
			minRecurse, maxRecurse := dfsTotalDistance(graph, k, visited)
			minDistance = mathy.MinInt(minDistance, weight+minRecurse)
			maxDistance = mathy.MaxInt(maxDistance, weight+maxRecurse)

			// backtrack
			// delete to so length of visited is accurate
			delete(visited, k)
		}
	}

	return minDistance, maxDistance
}

type mapGraph map[string]map[string]int

func newGraphFromInput(input string) (graph mapGraph) {
	graph = make(mapGraph)
	for _, line := range strings.Split(input, "\n") {
		parts := strings.Split(line, " ")
		start, end := parts[0], parts[2]
		weight := cast.ToInt(parts[4])

		// ensure nested map exists
		if graph[start] == nil {
			graph[start] = make(map[string]int)
		}
		if graph[end] == nil {
			graph[end] = make(map[string]int)
		}

		// set weight in both directions
		graph[start][end] = weight
		graph[end][start] = weight
	}
	return graph
}
