package main

import (
	"fmt"
	"sort"
	"strings"

	"aoc/helper"
)

type rules map[[2]int]struct{}
type updates []update
type update []int

func main() {
	order_rules, page_updates := getOrdersAndUpdate()

	// part 1
	sum := 0
	for _, update := range page_updates {
		if update.validateRules(order_rules) {
			sum += update.getMiddleNumber()
		}
	}
	fmt.Printf("Summed updates: %d\n", sum)

	// part 2
	sum = 0
	for _, update := range page_updates {
		if update.validateRules(order_rules) {
			continue
		}

		update.reorderByRules(order_rules)
		if update.validateRules(order_rules) {
			sum += update.getMiddleNumber()
		}
	}
	fmt.Printf("Sum fixed updates: %d\n", sum)
}

func (u update) reorderByRules(r rules) update {
	sort.Slice(
		u,
		func(i, j int) bool {
			pair := [2]int{u[i], u[j]}
			if _, exists := r[pair]; exists {
				return true
			}
			pair = [2]int{u[j], u[i]}
			if _, exists := r[pair]; exists {
				return false
			}
			return false
		},
	)
	return u
}

func (u update) validateRules(r rules) bool {
	for i := 0; i < len(u)-1; i++ {
		// check if a rule exists for current page pair (this + next)
		pair := [2]int{u[i+1], u[i]}
		if _, exists := r[pair]; exists {
			return false
		}
	}
	return true
}

func (u update) getMiddleNumber() int {
	return u[len(u)/2]
}

func getOrdersAndUpdate() (rules, updates) {
	data := helper.GetLines()

	// parse rules
	rules := rules{}
	for data.Scan() {
		line := data.Text()

		if len(line) <= 1 {
			// empty line, leave the rest for the update parser
			break
		}

		parts := strings.Split(line, "|")
		page := helper.ToInt(parts[0])
		after := helper.ToInt(parts[1])

		rules[[2]int{page, after}] = struct{}{}
	}

	// parse updates
	updates := updates{}
	for data.Scan() {
		line := data.Text()
		parts := strings.Split(line, ",")

		update := update{}
		for _, part := range parts {
			update = append(update, helper.ToInt(part))
		}

		updates = append(updates, update)
	}

	return rules, updates
}