package actions

import (
	"dk/internal/control"
	"dk/internal/models/fights"
	"dk/internal/models/monsters"
	"dk/internal/models/towns"
	"dk/internal/models/users"
	"fmt"
	"math"
	"math/rand"
)

type Direction int

const (
	North Direction = iota
	East
	South
	West
)

func (d Direction) String() string {
	switch d {
	case North:
		return "North"
	case East:
		return "East"
	case South:
		return "South"
	case West:
		return "West"
	default:
		return "Unknown"
	}
}

func Move(user *users.User, dir Direction) (string, int, int, error) {
	control := control.Get()

	newX, newY := user.GetPosition()
	switch dir {
	case North:
		newY++
	case East:
		newX++
	case South:
		newY--
	case West:
		newX--
	}

	if !control.IsWithinWorldBounds(newX, newY) {
		return user.Currently, user.X, user.Y, fmt.Errorf("You've hit the edge of the world.")
	}

	if towns.ExistsAt(newX, newY) {
		return "In Town", newX, newY, nil
	}

	// 33% chance to start a fight when moving to non-town location
	if rand.Float32() < 0.33 {
		monster, err := getRandomMonsterByDistance(newX, newY)
		if err != nil {
			return "Exploring", newX, newY, nil // Fall back to exploring if monster selection fails
		}

		// Create new fight record
		fight := fights.New(user.ID, monster.ID)
		fight.MonsterHP = monster.MaxHP
		fight.MonsterMaxHP = monster.MaxHP
		fight.MonsterImmune = monster.Immune

		err = fight.Insert()
		if err != nil {
			return "Exploring", newX, newY, nil // Fall back if fight creation fails
		}

		user.FightID = fight.ID
		return "Fighting", newX, newY, nil
	}

	return "Exploring", newX, newY, nil
}

// getRandomMonsterByDistance selects a random monster based on distance from origin
func getRandomMonsterByDistance(x, y int) (*monsters.Monster, error) {
	// Calculate Manhattan distance from origin
	distance := int(math.Abs(float64(x)) + math.Abs(float64(y)))

	// Determine level range based on distance (every 5 units increases level)
	minLevel := 1 + (distance / 5)
	maxLevel := minLevel + 2 // 3-level range

	// Cap the levels at reasonable bounds
	if minLevel > 50 {
		minLevel = 50
	}
	if maxLevel > 50 {
		maxLevel = 50
	}
	if minLevel < 1 {
		minLevel = 1
	}

	// Get monsters in the level range
	availableMonsters, err := monsters.ByLevelRange(minLevel, maxLevel)
	if err != nil || len(availableMonsters) == 0 {
		// Fallback to level 1 monsters if no monsters found
		availableMonsters, err = monsters.ByLevel(1)
		if err != nil || len(availableMonsters) == 0 {
			return nil, fmt.Errorf("no monsters available")
		}
	}

	// Pick a random monster from available ones
	randomIndex := rand.Intn(len(availableMonsters))
	return availableMonsters[randomIndex], nil
}
