package game

import (
	"math"

	"github.com/soer/football/internal/entities"
)

const (
	Friction          = 0.99
	Restitution       = 0.8
	StopThreshold     = 0.1
	WorldWidth        = 1280.0
	WorldHeight       = 720.0
	WorldCenterX      = 640.0
	WorldCenterY      = 360.0
	ZoneWidth         = WorldWidth / 3
	GoalieMaxDistance = 80.0
	GoalieDeadzone    = 1.0
)

var (
	GoalLeft  = entities.Vector2{X: 50, Y: WorldCenterY}
	GoalRight = entities.Vector2{X: 1230, Y: WorldCenterY}
)

type World struct {
	Puck    *entities.Puck
	Players []*entities.Player
	Width   float64
	Height  float64
}

func NewWorld() *World {
	return &World{
		Puck:   entities.NewPuck(WorldCenterX, WorldCenterY),
		Width:  WorldWidth,
		Height: WorldHeight,
		Players: []*entities.Player{
			entities.NewPlayer(200, WorldCenterY, entities.TeamRed, entities.RoleDefender),
			entities.NewPlayer(400, WorldCenterY, entities.TeamRed, entities.RoleStriker),
			entities.NewPlayer(GoalLeft.X, GoalLeft.Y, entities.TeamRed, entities.RoleGoalie),
			entities.NewPlayer(1080, WorldCenterY, entities.TeamBlue, entities.RoleDefender),
			entities.NewPlayer(880, WorldCenterY, entities.TeamBlue, entities.RoleStriker),
			entities.NewPlayer(GoalRight.X, GoalRight.Y, entities.TeamBlue, entities.RoleGoalie),
		},
	}
}

func (w *World) CheckGoal() (entities.Team, bool) {
	if w.Puck.Position.X < 0 {
		return entities.TeamBlue, true
	}
	if w.Puck.Position.X > w.Width {
		return entities.TeamRed, true
	}
	return entities.TeamRed, false
}

func (w *World) ResetPositions() {
	w.Puck.Position = entities.Vector2{X: WorldCenterX, Y: WorldCenterY}
	w.Puck.Velocity = entities.Vector2{X: 0, Y: 0}
	for _, p := range w.Players {
		p.Position = p.HomePosition
		p.Velocity = entities.Vector2{X: 0, Y: 0}
	}
}

func (w *World) Update() {
	// 1. Update Puck
	w.Puck.Velocity.X *= Friction
	w.Puck.Velocity.Y *= Friction

	if (w.Puck.Velocity.X*w.Puck.Velocity.X + w.Puck.Velocity.Y*w.Puck.Velocity.Y) < StopThreshold*StopThreshold {
		w.Puck.Velocity.X = 0
		w.Puck.Velocity.Y = 0
	}

	w.Puck.Position.X += w.Puck.Velocity.X
	w.Puck.Position.Y += w.Puck.Velocity.Y

	if w.Puck.Position.X < w.Puck.Radius {
		w.Puck.Position.X = w.Puck.Radius
		w.Puck.Velocity.X *= -Restitution
	} else if w.Puck.Position.X > w.Width-w.Puck.Radius {
		w.Puck.Position.X = w.Width - w.Puck.Radius
		w.Puck.Velocity.X *= -Restitution
	}

	if w.Puck.Position.Y < w.Puck.Radius {
		w.Puck.Position.Y = w.Puck.Radius
		w.Puck.Velocity.Y *= -Restitution
	} else if w.Puck.Position.Y > w.Height-w.Puck.Radius {
		w.Puck.Position.Y = w.Height - w.Puck.Radius
		w.Puck.Velocity.Y *= -Restitution
	}

	// 2. Update Players
	for _, p := range w.Players {
		// AI Target
		target := w.calculateTarget(p, w.Puck.Position)
		w.applySteering(p, target)

		// Anti-clumping (repulsion from teammates)
		for _, other := range w.Players {
			if p == other || p.Team != other.Team {
				continue
			}
			dx := p.Position.X - other.Position.X
			dy := p.Position.Y - other.Position.Y
			distSq := dx*dx + dy*dy
			if distSq < 40*40 && distSq > 0.01 {
				dist := math.Sqrt(distSq)
				p.Velocity.X += (dx / dist) * 0.1
				p.Velocity.Y += (dy / dist) * 0.1
			}
		}

		// Apply movement
		p.Position.X += p.Velocity.X
		p.Position.Y += p.Velocity.Y

		// Boundary clamping
		if p.Position.X < 0 {
			p.Position.X = 0
		} else if p.Position.X > w.Width {
			p.Position.X = w.Width
		}
		if p.Position.Y < 0 {
			p.Position.Y = 0
		} else if p.Position.Y > w.Height {
			p.Position.Y = w.Height
		}
	}
}

func (w *World) applySteering(p *entities.Player, target entities.Vector2) {
	dx := target.X - p.Position.X
	dy := target.Y - p.Position.Y
	dist := math.Sqrt(dx*dx + dy*dy)

	// Dead zone
	if dist < 1.0 {
		p.Velocity.X = 0
		p.Velocity.Y = 0
		return
	}

	// Desired velocity
	desiredX := (dx / dist) * p.MaxSpeed
	desiredY := (dy / dist) * p.MaxSpeed

	// Steering force
	steeringX := desiredX - p.Velocity.X
	steeringY := desiredY - p.Velocity.Y
	steeringDist := math.Sqrt(steeringX*steeringX + steeringY*steeringY)

	if steeringDist > p.Acceleration {
		steeringX = (steeringX / steeringDist) * p.Acceleration
		steeringY = (steeringY / steeringDist) * p.Acceleration
	}

	p.Velocity.X += steeringX
	p.Velocity.Y += steeringY

	// Cap final velocity
	speed := math.Sqrt(p.Velocity.X*p.Velocity.X + p.Velocity.Y*p.Velocity.Y)
	if speed > p.MaxSpeed {
		p.Velocity.X = (p.Velocity.X / speed) * p.MaxSpeed
		p.Velocity.Y = (p.Velocity.Y / speed) * p.MaxSpeed
	}
}

func (w *World) getPuckZone(team entities.Team, puckPos entities.Vector2) int {
	// 0: Defensive, 1: Middle, 2: Offensive
	if team == entities.TeamRed {
		if puckPos.X < ZoneWidth {
			return 0
		}
		if puckPos.X < 2*ZoneWidth {
			return 1
		}
		return 2
	} else {
		if puckPos.X > WorldWidth-ZoneWidth {
			return 0
		}
		if puckPos.X > WorldWidth-2*ZoneWidth {
			return 1
		}
		return 2
	}
}

func (w *World) calculateTarget(p *entities.Player, puckPos entities.Vector2) entities.Vector2 {
	zone := w.getPuckZone(p.Team, puckPos)

	goalPos := GoalLeft
	if p.Team == entities.TeamBlue {
		goalPos = GoalRight
	}

	switch p.Role {
	case entities.RoleDefender:
		switch zone {
		case 0: // Defensive: Intercept puck
			return entities.Vector2{
				X: (puckPos.X + goalPos.X) / 2,
				Y: (puckPos.Y + goalPos.Y) / 2,
			}
		case 1: // Middle: Move towards puck but stay slightly behind it
			return entities.Vector2{
				X: puckPos.X + (goalPos.X-puckPos.X)*0.2,
				Y: puckPos.Y + (goalPos.Y-puckPos.Y)*0.2,
			}
		case 2: // Offensive: Return to Middle Zone
			return p.HomePosition
		}
	case entities.RoleStriker:
		switch zone {
		case 2: // Offensive: Aggressively pursue puck
			return puckPos
		case 1: // Middle: Pursue puck
			return puckPos
		case 0: // Defensive: Return to Middle Zone
			return entities.Vector2{X: WorldCenterX, Y: WorldCenterY}
		}
	case entities.RoleGoalie:
		return w.updateGoalieAI(p, puckPos)
	}
	return p.HomePosition
}

func (w *World) updateGoalieAI(p *entities.Player, puckPos entities.Vector2) entities.Vector2 {
	goalCenter := GoalLeft
	if p.Team == entities.TeamBlue {
		goalCenter = GoalRight
	}

	// Check if puck is behind the goal
	if (p.Team == entities.TeamRed && puckPos.X < goalCenter.X) || (p.Team == entities.TeamBlue && puckPos.X > goalCenter.X) {
		return goalCenter
	}

	dx := puckPos.X - goalCenter.X
	dy := puckPos.Y - goalCenter.Y
	dist := math.Sqrt(dx*dx + dy*dy)

	if dist < GoalieDeadzone {
		return goalCenter
	}

	moveDist := math.Min(dist, GoalieMaxDistance)
	return entities.Vector2{
		X: goalCenter.X + (dx/dist)*moveDist,
		Y: goalCenter.Y + (dy/dist)*moveDist,
	}
}
