package day14

import (
	"fmt"
	"strconv"
	"strings"
)

type Robot struct {
	px, py int // position
	vx, vy int // velocity
}

func Part1(input string) int {
	// Parse the input
	lines := strings.Split(input, "\n")
	var robots []Robot
	for _, line := range lines {
		robot := ParseRobot(line)
		robots = append(robots, robot)
	}

	// Define grid size
	gridWidth := 101
	gridHeight := 103

	// Update robot positions for 100 seconds
	for i := 0; i < 100; i++ {
		for j := range robots {
			robots[j].UpdatePosition(gridWidth, gridHeight)
		}
	}

	// Count the robots in each quadrant
	q1, q2, q3, q4 := CountRobotsInQuadrants(robots, gridWidth, gridHeight)
	fmt.Printf("Quadrant counts: Q1=%d, Q2=%d, Q3=%d, Q4=%d\n", q1, q2, q3, q4)

	// Calculate the safety factor
	safetyFactor := CalculateSafetyFactor(q1, q2, q3, q4)
	fmt.Printf("Safety Factor: %d\n", safetyFactor)
	return safetyFactor
}

func Part2(input string) int {
	// Parse the input
	lines := strings.Split(input, "\n")
	var robots []Robot
	for _, line := range lines {
		robot := ParseRobot(line)
		robots = append(robots, robot)
	}

	// Define grid size
	gridWidth := 101
	gridHeight := 103

	// Update robot positions for 100 seconds
	for i := 0; i < 6285; i++ {
		for j := range robots {
			robots[j].UpdatePosition(gridWidth, gridHeight)
		}
	}

	DisplayGrid(robots, gridWidth, gridHeight)
	return 2
}

// ParseRobot takes a line of input and converts it to a Robot struct
func ParseRobot(line string) Robot {
	parts := strings.Split(line, " ")
	posParts := strings.Split(parts[0][2:], ",") // Extract p=x,y and split
	velParts := strings.Split(parts[1][2:], ",") // Extract v=x,y and split

	px, _ := strconv.Atoi(posParts[0])
	py, _ := strconv.Atoi(posParts[1])
	vx, _ := strconv.Atoi(velParts[0])
	vy, _ := strconv.Atoi(velParts[1])

	return Robot{px, py, vx, vy}
}

// UpdatePosition updates the position of a robot, considering the wrap-around
func (r *Robot) UpdatePosition(gridWidth, gridHeight int) {
	r.px = (r.px + r.vx + gridWidth) % gridWidth
	r.py = (r.py + r.vy + gridHeight) % gridHeight
}

// CountRobotsInQuadrants counts the number of robots in each of the four quadrants
func CountRobotsInQuadrants(robots []Robot, width, height int) (int, int, int, int) {
	midX := width / 2
	midY := height / 2

	q1, q2, q3, q4 := 0, 0, 0, 0

	for _, r := range robots {
		if r.px == midX || r.py == midY {
			// Skip robots on the middle line
			continue
		}
		if r.px < midX && r.py < midY {
			q1++ // Top-left quadrant
		} else if r.px > midX && r.py < midY {
			q2++ // Top-right quadrant
		} else if r.px < midX && r.py > midY {
			q3++ // Bottom-left quadrant
		} else if r.px > midX && r.py > midY {
			q4++ // Bottom-right quadrant
		}
	}

	return q1, q2, q3, q4
}

// CalculateSafetyFactor multiplies the number of robots in each quadrant
func CalculateSafetyFactor(q1, q2, q3, q4 int) int {
	return q1 * q2 * q3 * q4
}

// DisplayGrid displays the current state of the grid
func DisplayGrid(robots []Robot, width, height int) {
	grid := make([][]rune, height)
	for i := range grid {
		grid[i] = make([]rune, width)
		for j := range grid[i] {
			grid[i][j] = '.'
		}
	}
	for _, r := range robots {
		grid[r.py][r.px] = '#'
	}
	for _, row := range grid {
		fmt.Println(string(row))
	}
}

// FindEasterEgg determines the fewest number of seconds that must elapse for the robots to display the Easter egg
func FindEasterEgg(robots []Robot, width, height int) int {
	smallestArea := width * height
	bestTime := 0
	for t := 6285; t < 6286; t++ { // Large upper limit to search
		// Update robot positions
		for i := range robots {
			robots[i].UpdatePosition(width, height)
		}
		// Calculate the bounding box of all robot positions
		minX, minY := width, height
		maxX, maxY := 0, 0
		for _, r := range robots {
			if r.px < minX {
				minX = r.px
			}
			if r.px > maxX {
				maxX = r.px
			}
			if r.py < minY {
				minY = r.py
			}
			if r.py > maxY {
				maxY = r.py
			}
		}

		area := (maxX - minX + 1) * (maxY - minY + 1)
		if area < smallestArea {
			smallestArea = area
			bestTime = t + 1
			fmt.Printf("Time: %d\n", bestTime)
			DisplayGrid(robots, width, height)
		}
	}
	return bestTime
}
