package towns

import (
	"fmt"
	"math"
	"slices"
	"sort"

	"dk/internal/database"
	"dk/internal/helpers"
)

// Town represents a town in the game
type Town struct {
	ID       int
	Name     string
	X        int
	Y        int
	InnCost  int
	MapCost  int
	TPCost   int
	ShopList string
}

// New creates a new Town with sensible defaults
func New() *Town {
	return &Town{
		Name:     "",
		X:        0,
		Y:        0,
		InnCost:  50,
		MapCost:  100,
		TPCost:   25,
		ShopList: "",
	}
}

// Validate checks if town has valid values
func (t *Town) Validate() error {
	if t.Name == "" {
		return fmt.Errorf("town name cannot be empty")
	}
	if t.InnCost < 0 {
		return fmt.Errorf("town InnCost cannot be negative")
	}
	if t.MapCost < 0 {
		return fmt.Errorf("town MapCost cannot be negative")
	}
	if t.TPCost < 0 {
		return fmt.Errorf("town TPCost cannot be negative")
	}
	return nil
}

// CRUD operations
func (t *Town) Delete() error {
	return database.Exec("DELETE FROM towns WHERE id = %d", t.ID)
}

func (t *Town) Insert() error {
	id, err := database.Insert("towns", t, "ID")
	if err != nil {
		return err
	}
	t.ID = int(id)
	return nil
}

// Query functions
func Find(id int) (*Town, error) {
	var town Town
	err := database.Get(&town, "SELECT * FROM towns WHERE id = %d", id)
	if err != nil {
		return nil, fmt.Errorf("town with ID %d not found", id)
	}
	return &town, nil
}

func All() ([]*Town, error) {
	var towns []*Town
	err := database.Select(&towns, "SELECT * FROM towns ORDER BY id ASC")
	return towns, err
}

func ByName(name string) (*Town, error) {
	var town Town
	err := database.Get(&town, "SELECT * FROM towns WHERE name = %s COLLATE NOCASE", name)
	if err != nil {
		return nil, fmt.Errorf("town with name '%s' not found", name)
	}
	return &town, nil
}

func ByMaxInnCost(maxCost int) ([]*Town, error) {
	var towns []*Town
	err := database.Select(&towns, "SELECT * FROM towns WHERE inn_cost <= %d ORDER BY id ASC", maxCost)
	return towns, err
}

func ByMaxTPCost(maxCost int) ([]*Town, error) {
	var towns []*Town
	err := database.Select(&towns, "SELECT * FROM towns WHERE tp_cost <= %d ORDER BY id ASC", maxCost)
	return towns, err
}

func ByCoords(x, y int) (*Town, error) {
	var town Town
	err := database.Get(&town, "SELECT * FROM towns WHERE x = %d AND y = %d", x, y)
	if err != nil {
		return nil, nil // Return nil if not found (like original)
	}
	return &town, nil
}

func ExistsAt(x, y int) bool {
	var count int
	err := database.Get(&count, "SELECT COUNT(*) FROM towns WHERE x = %d AND y = %d", x, y)
	return err == nil && count > 0
}

func ByDistance(fromX, fromY, maxDistance int) ([]*Town, error) {
	var towns []*Town
	err := database.Select(&towns, "SELECT * FROM towns")
	if err != nil {
		return nil, err
	}

	maxDistance2 := float64(maxDistance * maxDistance)
	var result []*Town

	for _, town := range towns {
		if town.DistanceFromSquared(fromX, fromY) <= maxDistance2 {
			result = append(result, town)
		}
	}

	// Sort by distance, then by ID
	sort.Slice(result, func(i, j int) bool {
		distI := result[i].DistanceFromSquared(fromX, fromY)
		distJ := result[j].DistanceFromSquared(fromX, fromY)
		if distI == distJ {
			return result[i].ID < result[j].ID
		}
		return distI < distJ
	})

	return result, nil
}

// Helper methods
func (t *Town) GetShopItems() []int {
	return helpers.StringToInts(t.ShopList)
}

func (t *Town) SetShopItems(items []int) {
	t.ShopList = helpers.IntsToString(items)
}

func (t *Town) HasShopItem(itemID int) bool {
	return slices.Contains(t.GetShopItems(), itemID)
}

func (t *Town) DistanceFromSquared(x, y int) float64 {
	dx := float64(t.X - x)
	dy := float64(t.Y - y)
	return dx*dx + dy*dy // Return squared distance for performance
}

func (t *Town) DistanceFrom(x, y int) float64 {
	return math.Sqrt(t.DistanceFromSquared(x, y))
}

func (t *Town) IsStartingTown() bool {
	return t.X == 0 && t.Y == 0
}

func (t *Town) CanAffordInn(gold int) bool {
	return gold >= t.InnCost
}

func (t *Town) CanAffordMap(gold int) bool {
	return gold >= t.MapCost
}

func (t *Town) CanAffordTeleport(gold int) bool {
	return gold >= t.TPCost
}

func (t *Town) HasShop() bool {
	return len(t.GetShopItems()) > 0
}

func (t *Town) GetPosition() (int, int) {
	return t.X, t.Y
}

func (t *Town) SetPosition(x, y int) {
	t.X = x
	t.Y = y
}
