package monsters

import (
	"fmt"

	nigiri "git.sharkk.net/Sharkk/Nigiri"
)

// Monster represents a monster in the game
type Monster struct {
	ID      int    `json:"id"`
	Name    string `json:"name" db:"required"`
	MaxHP   int    `json:"max_hp"`
	MaxDmg  int    `json:"max_dmg"`
	Armor   int    `json:"armor"`
	Level   int    `json:"level" db:"index"`
	MaxExp  int    `json:"max_exp"`
	MaxGold int    `json:"max_gold"`
	Immune  int    `json:"immune" db:"index"`
}

// Immunity constants
const (
	ImmuneNone  = 0
	ImmuneHurt  = 1
	ImmuneSleep = 2
)

// Global store
var store *nigiri.BaseStore[Monster]

// Init sets up the Nigiri store and indices
func Init(collection *nigiri.Collection) {
	store = nigiri.NewBaseStore[Monster]()

	// Register custom indices
	store.RegisterIndex("byLevel", nigiri.BuildIntGroupIndex(func(m *Monster) int {
		return m.Level
	}))

	store.RegisterIndex("byImmunity", nigiri.BuildIntGroupIndex(func(m *Monster) int {
		return m.Immune
	}))

	store.RegisterIndex("allByLevel", nigiri.BuildSortedListIndex(func(a, b *Monster) bool {
		if a.Level == b.Level {
			return a.ID < b.ID
		}
		return a.Level < b.Level
	}))

	store.RebuildIndices()
}

// GetStore returns the monsters store
func GetStore() *nigiri.BaseStore[Monster] {
	if store == nil {
		panic("monsters store not initialized - call Initialize first")
	}
	return store
}

// Creates a new Monster with sensible defaults
func New() *Monster {
	return &Monster{
		Name:    "",
		MaxHP:   10,
		MaxDmg:  5,
		Armor:   0,
		Level:   1,
		MaxExp:  10,
		MaxGold: 5,
		Immune:  ImmuneNone,
	}
}

// Validate checks if monster has valid values
func (m *Monster) Validate() error {
	if m.Name == "" {
		return fmt.Errorf("monster name cannot be empty")
	}
	if m.MaxHP < 1 {
		return fmt.Errorf("monster MaxHP must be at least 1")
	}
	if m.Level < 1 {
		return fmt.Errorf("monster Level must be at least 1")
	}
	if m.Immune < ImmuneNone || m.Immune > ImmuneSleep {
		return fmt.Errorf("invalid immunity type: %d", m.Immune)
	}
	return nil
}

// CRUD operations
func (m *Monster) Save() error {
	if m.ID == 0 {
		id, err := store.Create(m)
		if err != nil {
			return err
		}
		m.ID = id
		return nil
	}
	return store.Update(m.ID, m)
}

func (m *Monster) Delete() error {
	store.Remove(m.ID)
	return nil
}

// Insert with ID assignment
func (m *Monster) Insert() error {
	id, err := store.Create(m)
	if err != nil {
		return err
	}
	m.ID = id
	return nil
}

// Query functions
func Find(id int) (*Monster, error) {
	monster, exists := store.Find(id)
	if !exists {
		return nil, fmt.Errorf("monster with ID %d not found", id)
	}
	return monster, nil
}

func All() ([]*Monster, error) {
	return store.AllSorted("allByLevel"), nil
}

func ByLevel(level int) ([]*Monster, error) {
	return store.GroupByIndex("byLevel", level), nil
}

func ByLevelRange(minLevel, maxLevel int) ([]*Monster, error) {
	var result []*Monster
	for level := minLevel; level <= maxLevel; level++ {
		monsters := store.GroupByIndex("byLevel", level)
		result = append(result, monsters...)
	}
	return result, nil
}

func ByImmunity(immunityType int) ([]*Monster, error) {
	return store.GroupByIndex("byImmunity", immunityType), nil
}

// Helper methods
func (m *Monster) IsHurtImmune() bool {
	return m.Immune == ImmuneHurt
}

func (m *Monster) IsSleepImmune() bool {
	return m.Immune == ImmuneSleep
}

func (m *Monster) HasImmunity() bool {
	return m.Immune != ImmuneNone
}

func (m *Monster) ImmunityName() string {
	switch m.Immune {
	case ImmuneNone:
		return "None"
	case ImmuneHurt:
		return "Hurt Spells"
	case ImmuneSleep:
		return "Sleep Spells"
	default:
		return "Unknown"
	}
}

func (m *Monster) DifficultyRating() float64 {
	if m.Level == 0 {
		return 0
	}
	return float64(m.MaxHP+m.MaxDmg+m.Armor) / float64(m.Level)
}

func (m *Monster) ExpPerHP() float64 {
	if m.MaxHP == 0 {
		return 0
	}
	return float64(m.MaxExp) / float64(m.MaxHP)
}

func (m *Monster) GoldPerHP() float64 {
	if m.MaxHP == 0 {
		return 0
	}
	return float64(m.MaxGold) / float64(m.MaxHP)
}

// Legacy compatibility functions (will be removed later)
func LoadData(dataPath string) error {
	// No longer needed - Nigiri handles this
	return nil
}

func SaveData(dataPath string) error {
	// No longer needed - Nigiri handles this
	return nil
}
