package fights

import (
	"fmt"
	"time"

	"dk/internal/database"
)

// Fight represents a fight, past or present
type Fight struct {
	ID            int
	UserID        int
	MonsterID     int
	MonsterHP     int
	MonsterMaxHP  int
	MonsterSleep  int
	MonsterImmune int
	UberDamage    int
	UberDefense   int
	FirstStrike   bool
	Turn          int
	RanAway       bool
	Victory       bool
	Won           bool
	RewardGold    int
	RewardExp     int
	Created       int64
	Updated       int64
}

// 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,
		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) Delete() error {
	return database.Exec("DELETE FROM fights WHERE id = %d", f.ID)
}

func (f *Fight) Insert() error {
	f.Updated = time.Now().Unix()
	id, err := database.Insert("fights", f, "ID")
	if err != nil {
		return err
	}
	f.ID = int(id)
	return nil
}

func (f *Fight) Update() error {
	f.Updated = time.Now().Unix()
	return database.Update("fights", map[string]any{
		"user_id":        f.UserID,
		"monster_id":     f.MonsterID,
		"monster_hp":     f.MonsterHP,
		"monster_max_hp": f.MonsterMaxHP,
		"monster_sleep":  f.MonsterSleep,
		"monster_immune": f.MonsterImmune,
		"uber_damage":    f.UberDamage,
		"uber_defense":   f.UberDefense,
		"first_strike":   f.FirstStrike,
		"turn":           f.Turn,
		"ran_away":       f.RanAway,
		"victory":        f.Victory,
		"won":            f.Won,
		"reward_gold":    f.RewardGold,
		"reward_exp":     f.RewardExp,
		"created":        f.Created,
		"updated":        f.Updated,
	}, "id", f.ID)
}

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

func All() ([]*Fight, error) {
	var fights []*Fight
	err := database.Select(&fights, "SELECT * FROM fights ORDER BY created DESC, id DESC")
	return fights, err
}

func ByUserID(userID int) ([]*Fight, error) {
	var fights []*Fight
	err := database.Select(&fights, "SELECT * FROM fights WHERE user_id = %d ORDER BY created DESC, id DESC", userID)
	return fights, err
}

func ByMonsterID(monsterID int) ([]*Fight, error) {
	var fights []*Fight
	err := database.Select(&fights, "SELECT * FROM fights WHERE monster_id = %d ORDER BY created DESC, id DESC", monsterID)
	return fights, err
}

func ActiveByUserID(userID int) ([]*Fight, error) {
	var fights []*Fight
	err := database.Select(&fights, "SELECT * FROM fights WHERE user_id = %d AND ran_away = 0 AND victory = 0 ORDER BY created DESC, id DESC", userID)
	return fights, err
}

func Active() ([]*Fight, error) {
	var fights []*Fight
	err := database.Select(&fights, "SELECT * FROM fights WHERE ran_away = 0 AND victory = 0 ORDER BY created DESC, id DESC")
	return fights, err
}

func Recent(within time.Duration) ([]*Fight, error) {
	cutoff := time.Now().Add(-within).Unix()
	var fights []*Fight
	err := database.Select(&fights, "SELECT * FROM fights WHERE created >= %d ORDER BY created DESC, id DESC", cutoff)
	return fights, err
}

// 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()
}
