package fights

import (
	"fmt"
	"time"

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

// Fight represents a fight, past or present
type Fight struct {
	ID            int           `json:"id"`
	UserID        int           `json:"user_id" db:"index"`
	MonsterID     int           `json:"monster_id" db:"index"`
	MonsterHP     int           `json:"monster_hp"`
	MonsterMaxHP  int           `json:"monster_max_hp"`
	MonsterSleep  int           `json:"monster_sleep"`
	MonsterImmune int           `json:"monster_immune"`
	UberDamage    int           `json:"uber_damage"`
	UberDefense   int           `json:"uber_defense"`
	FirstStrike   bool          `json:"first_strike"`
	Turn          int           `json:"turn"`
	RanAway       bool          `json:"ran_away"`
	Victory       bool          `json:"victory"`
	Won           bool          `json:"won"`
	RewardGold    int           `json:"reward_gold"`
	RewardExp     int           `json:"reward_exp"`
	Actions       []ActionEntry `json:"actions"`
	Created       int64         `json:"created"`
	Updated       int64         `json:"updated"`
}

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

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

	// Register custom indices
	store.RegisterIndex("byUserID", nigiri.BuildIntGroupIndex(func(f *Fight) int {
		return f.UserID
	}))

	store.RegisterIndex("byMonsterID", nigiri.BuildIntGroupIndex(func(f *Fight) int {
		return f.MonsterID
	}))

	store.RegisterIndex("activeFights", nigiri.BuildFilteredIntGroupIndex(
		func(f *Fight) bool {
			return !f.RanAway && !f.Victory
		},
		func(f *Fight) int {
			return f.UserID
		},
	))

	store.RegisterIndex("allByCreated", nigiri.BuildSortedListIndex(func(a, b *Fight) bool {
		if a.Created != b.Created {
			return a.Created > b.Created // DESC
		}
		return a.ID > b.ID // DESC
	}))

	store.RegisterIndex("allByUpdated", nigiri.BuildSortedListIndex(func(a, b *Fight) bool {
		if a.Updated != b.Updated {
			return a.Updated > b.Updated // DESC
		}
		return a.ID > b.ID // DESC
	}))

	store.RebuildIndices()
}

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

// New creates a new Fight with sensible defaults
func New(userID, monsterID int) *Fight {
	now := time.Now().Unix()
	return &Fight{
		UserID:        userID,
		MonsterID:     monsterID,
		MonsterHP:     0,
		MonsterMaxHP:  0,
		MonsterSleep:  0,
		MonsterImmune: 0,
		UberDamage:    0,
		UberDefense:   0,
		FirstStrike:   false,
		Turn:          0,
		RanAway:       false,
		Victory:       false,
		Won:           false,
		RewardGold:    0,
		RewardExp:     0,
		Actions:       make([]ActionEntry, 0),
		Created:       now,
		Updated:       now,
	}
}

// Validate checks if fight has valid values
func (f *Fight) Validate() error {
	if f.UserID <= 0 {
		return fmt.Errorf("fight UserID must be positive")
	}
	if f.MonsterID <= 0 {
		return fmt.Errorf("fight MonsterID must be positive")
	}
	if f.Turn < 0 {
		return fmt.Errorf("fight Turn cannot be negative")
	}
	if f.MonsterHP < 0 {
		return fmt.Errorf("fight MonsterHP cannot be negative")
	}
	if f.Created <= 0 {
		return fmt.Errorf("fight Created timestamp must be positive")
	}
	if f.Updated <= 0 {
		return fmt.Errorf("fight Updated timestamp must be positive")
	}
	return nil
}

// CRUD operations
func (f *Fight) Save() error {
	f.Updated = time.Now().Unix()
	if f.ID == 0 {
		id, err := store.Create(f)
		if err != nil {
			return err
		}
		f.ID = id
		return nil
	}
	return store.Update(f.ID, f)
}

func (f *Fight) Delete() error {
	store.Remove(f.ID)
	return nil
}

// Insert with ID assignment
func (f *Fight) Insert() error {
	f.Updated = time.Now().Unix()
	id, err := store.Create(f)
	if err != nil {
		return err
	}
	f.ID = id
	return nil
}

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

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

func ByUserID(userID int) ([]*Fight, error) {
	return store.GroupByIndex("byUserID", userID), nil
}

func ByMonsterID(monsterID int) ([]*Fight, error) {
	return store.GroupByIndex("byMonsterID", monsterID), nil
}

func ActiveByUserID(userID int) ([]*Fight, error) {
	return store.GroupByIndex("activeFights", userID), nil
}

func Active() ([]*Fight, error) {
	result := store.FilterByIndex("allByCreated", func(f *Fight) bool {
		return !f.RanAway && !f.Victory
	})
	return result, nil
}

func Recent(within time.Duration) ([]*Fight, error) {
	cutoff := time.Now().Add(-within).Unix()

	result := store.FilterByIndex("allByCreated", func(f *Fight) bool {
		return f.Created >= cutoff
	})

	return result, nil
}

// Helper methods
func (f *Fight) CreatedTime() time.Time {
	return time.Unix(f.Created, 0)
}

func (f *Fight) UpdatedTime() time.Time {
	return time.Unix(f.Updated, 0)
}

func (f *Fight) IsActive() bool {
	return !f.RanAway && !f.Victory
}

func (f *Fight) IsComplete() bool {
	return f.RanAway || f.Victory
}

func (f *Fight) GetStatus() string {
	if f.Won {
		return "Won"
	}
	if f.Victory && !f.Won {
		return "Lost"
	}
	if f.RanAway {
		return "Ran Away"
	}
	return "Active"
}

func (f *Fight) GetMonsterHealthPercent() float64 {
	if f.MonsterMaxHP <= 0 {
		return 0.0
	}
	return float64(f.MonsterHP) / float64(f.MonsterMaxHP) * 100.0
}

func (f *Fight) IsMonsterSleeping() bool {
	return f.MonsterSleep > 0
}

func (f *Fight) IsMonsterImmune() bool {
	return f.MonsterImmune > 0
}

func (f *Fight) HasUberBonus() bool {
	return f.UberDamage > 0 || f.UberDefense > 0
}

func (f *Fight) GetDuration() time.Duration {
	return time.Unix(f.Updated, 0).Sub(time.Unix(f.Created, 0))
}

func (f *Fight) EndFight(victory bool) {
	f.Victory = victory
	f.RanAway = !victory
	f.Updated = time.Now().Unix()
}

func (f *Fight) WinFight(goldReward, expReward int) {
	f.Victory = true
	f.Won = true
	f.RanAway = false
	f.RewardGold = goldReward
	f.RewardExp = expReward
	f.Updated = time.Now().Unix()
}

func (f *Fight) LoseFight() {
	f.Victory = true
	f.Won = false
	f.RanAway = false
	f.Updated = time.Now().Unix()
}

func (f *Fight) RunAway() {
	f.RanAway = true
	f.Victory = false
	f.Updated = time.Now().Unix()
}

func (f *Fight) IncrementTurn() {
	f.Turn++
	f.Updated = time.Now().Unix()
}

func (f *Fight) SetMonsterHP(hp int) {
	f.MonsterHP = hp
	f.Updated = time.Now().Unix()
}

func (f *Fight) DamageMonster(damage int) {
	f.MonsterHP -= damage
	if f.MonsterHP < 0 {
		f.MonsterHP = 0
	}
	f.Updated = time.Now().Unix()
}
