package routes

import (
	"dk/internal/actions"
	"dk/internal/components"
	"dk/internal/database"
	"dk/internal/helpers"
	"dk/internal/models/fightlogs"
	"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)
}

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
	}

	// Initialize fight on first view
	if fight.Turn == 0 {
		err := database.Transaction(func() error {
			return database.Update("fights", map[string]any{
				"first_strike": rand.Float32() < 0.5,
				"turn":         1,
			}, "id", fight.ID)
		})
		if err != nil {
			ctx.SendError(500, "Failed to initialize fight")
			return
		}
		fight.FirstStrike = rand.Float32() < 0.5
		fight.Turn = 1
	}

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

	// Get recent fight actions
	lastAction, _ := fightlogs.GetLastAction(fight.ID)

	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"),
		"last_action": lastAction,
	})
}

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 result *actions.FightResult

	switch action {
	case "attack":
		result = actions.HandleAttack(fight, user)
	case "spell":
		spellIDStr := string(ctx.FormValue("spell_id"))
		if spellID, err := strconv.Atoi(spellIDStr); err == nil {
			result = actions.HandleSpell(fight, user, spellID)
		} else {
			result = &actions.FightResult{
				LogAction: func() error { return fightlogs.AddAction(fight.ID, "Invalid spell!") },
			}
		}
	case "run":
		result = actions.HandleRun(fight, user)
	default:
		result = &actions.FightResult{
			LogAction: func() error { return fightlogs.AddAction(fight.ID, "Invalid action!") },
		}
	}

	// Execute the action
	err = actions.ExecuteFightAction(fight.ID, result)
	if err != nil {
		ctx.SendError(500, "Failed to execute fight action")
		return
	}

	// Handle fight end states
	if result.Ended {
		if result.Won {
			sess.SetFlash("success", fmt.Sprintf("Victory! You gained rewards!"))
			ctx.Redirect("/explore", 302)
		} else if result.Victory {
			sess.SetFlash("error", "You have been defeated! You lost some gold and were sent to the nearest town.")
			ctx.Redirect("/town", 302)
		} else {
			// Ran away
			sess.SetFlash("success", "You successfully escaped!")
			ctx.Redirect("/explore", 302)
		}
		return
	}

	// Monster attacks back if fight continues
	if fight.IsActive() && user.HP > 0 {
		monsterResult := actions.HandleMonsterAttack(fight, user)

		// Execute monster action
		err = actions.ExecuteFightAction(fight.ID, monsterResult)
		if err != nil {
			ctx.SendError(500, "Failed to execute monster action")
			return
		}

		// Check if monster action ended fight
		if monsterResult.Ended {
			if monsterResult.Won {
				sess.SetFlash("success", "Victory!")
				ctx.Redirect("/explore", 302)
			} else {
				sess.SetFlash("error", "You have been defeated!")
				ctx.Redirect("/town", 302)
			}
			return
		}
	}

	// Increment turn
	err = database.Transaction(func() error {
		return database.Update("fights", map[string]any{
			"turn": fight.Turn + 1,
		}, "id", fight.ID)
	})
	if err != nil {
		ctx.SendError(500, "Failed to increment turn")
		return
	}

	ctx.Redirect("/fight", 302)
}
