package routes

import (
	"dk/internal/actions"
	"dk/internal/components"
	"dk/internal/helpers"
	"dk/internal/models/fights"
	"dk/internal/models/monsters"
	"dk/internal/models/spells"
	"dk/internal/models/users"
	"fmt"
	"math/rand"
	"strconv"

	sushi "git.sharkk.net/Sharkk/Sushi"
	"git.sharkk.net/Sharkk/Sushi/auth"
)

func RegisterFightRoutes(app *sushi.App) {
	group := app.Group("/fight")
	group.Use(auth.RequireAuth("/login"))
	group.Use(requireFighting())

	group.Get("/", showFight)
	group.Post("/", handleFightAction)
}

// requireFighting middleware ensures the user is in a fight
func requireFighting() sushi.Middleware {
	return func(ctx sushi.Ctx, next func()) {
		user := ctx.GetCurrentUser()
		if user == nil {
			ctx.SendError(401, "Not authenticated")
			return
		}

		userModel := user.(*users.User)
		if !userModel.IsFighting() {
			ctx.Redirect("/")
			return
		}

		next()
	}
}

func showFight(ctx sushi.Ctx) {
	sess := ctx.GetCurrentSession()
	user := ctx.GetCurrentUser().(*users.User)

	fight, err := fights.Find(user.FightID)
	if err != nil {
		ctx.SendError(404, "Fight not found")
		return
	}

	monster, err := monsters.Find(fight.MonsterID)
	if err != nil {
		ctx.SendError(404, "Monster not found for fight")
		return
	}

	// If turn 0, determine first strike and advance to turn 1
	if fight.Turn == 0 {
		// 50% chance user goes first
		fight.FirstStrike = rand.Float32() < 0.5
		fight.Turn = 1
		fight.Save()
	}

	monHpPct := helpers.ClampPct(float64(fight.MonsterHP), float64(fight.MonsterMaxHP), 0, 100)

	monHpColor := ""
	if monHpPct < 35 {
		monHpColor = "danger"
	} else if monHpPct < 75 {
		monHpColor = "warning"
	}

	spellMap := helpers.NewOrderedMap[int, *spells.Spell]()
	if user.Spells != "" {
		for _, id := range user.GetSpellIDs() {
			if spell, err := spells.Find(id); err == nil {
				spellMap.Set(id, spell)
			}
		}
	}

	components.RenderPage(ctx, "Fighting", "fight/fight.html", map[string]any{
		"fight":      fight,
		"user":       user,
		"monster":    monster,
		"mon_hppct":  monHpPct,
		"mon_hpcol":  monHpColor,
		"spells":     spellMap.ToSlice(),
		"action":     sess.GetFlashMessage("action"),
		"mon_action": sess.GetFlashMessage("mon_action"),
	})
}

func handleFightAction(ctx sushi.Ctx) {
	sess := ctx.GetCurrentSession()
	user := ctx.GetCurrentUser().(*users.User)

	fight, err := fights.Find(user.FightID)
	if err != nil {
		ctx.SetContentType("text/plain")
		ctx.SetBodyString("Fight not found")
		return
	}

	action := string(ctx.FormValue("action"))
	var userAction string

	switch action {
	case "attack":
		actions.HandleAttack(fight, user)
		userAction = fight.GetLastAction()
	case "spell":
		spellIDStr := string(ctx.FormValue("spell_id"))
		if spellID, err := strconv.Atoi(spellIDStr); err == nil {
			actions.HandleSpell(fight, user, spellID)
			userAction = fight.GetLastAction()
		}
	case "run":
		actions.HandleRun(fight, user)
		userAction = fight.GetLastAction()

		// If successfully ran away, redirect to explore
		if fight.RanAway {
			user.Currently = "Exploring"
			user.Save()
			sess.SetFlash("success", "You successfully escaped!")
			ctx.Redirect("/explore", 302)
			return
		}
	default:
		fight.AddAction("Invalid action!")
		userAction = "Invalid action!"
	}

	// Flash user action
	sess.SetFlash("action", userAction)

	// Check if fight ended due to user action
	if fight.Victory {
		if fight.Won {
			// Player won
			sess.SetFlash("success", fmt.Sprintf("Victory! You gained %d gold and %d experience!", fight.RewardGold, fight.RewardExp))
			sess.DeleteFlash("action")
			sess.DeleteFlash("mon_action")
			ctx.Redirect("/explore", 302)
		} else {
			// Player lost
			sess.SetFlash("error", "You have been defeated! You lost some gold and were sent to the nearest town.")
			sess.DeleteFlash("action")
			sess.DeleteFlash("mon_action")
			ctx.Redirect("/town", 302)
		}
		return
	}

	// Monster attacks back if fight is still active
	if fight.IsActive() && user.HP > 0 {
		actions.HandleMonsterAttack(fight, user)

		// Check if fight ended due to monster attack
		if fight.Victory {
			if fight.Won {
				sess.SetFlash("success", fmt.Sprintf("Victory! You gained %d gold and %d experience!", fight.RewardGold, fight.RewardExp))
				sess.DeleteFlash("action")
				sess.DeleteFlash("mon_action")
				ctx.Redirect("/explore", 302)
			} else {
				sess.SetFlash("error", "You have been defeated! You lost some gold and were sent to the nearest town.")
				sess.DeleteFlash("action")
				sess.DeleteFlash("mon_action")
				ctx.Redirect("/town", 302)
			}
			return
		}

		monsterAction := fight.GetLastAction()
		sess.SetFlash("mon_action", monsterAction)
	}

	fight.IncrementTurn()
	fight.Save()
	user.Save()

	// Redirect back to fight page
	ctx.Redirect("/fight", 302)
}
