package handlers

import (
	"strconv"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
	"github.com/tapper/backend/database"
	"github.com/tapper/backend/models"
)

func GetMatchHistory(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(uuid.UUID)

	page, _ := strconv.Atoi(c.Query("page", "1"))
	limit, _ := strconv.Atoi(c.Query("limit", "20"))
	if page < 1 {
		page = 1
	}
	if limit < 1 || limit > 50 {
		limit = 20
	}
	offset := (page - 1) * limit

	var total int64
	database.DB.Model(&models.MatchPlayer{}).Where("user_id = ?", userID).Count(&total)

	var matchPlayers []models.MatchPlayer
	database.DB.Where("user_id = ?", userID).
		Order("id DESC").
		Offset(offset).
		Limit(limit).
		Find(&matchPlayers)

	matchIDs := make([]uuid.UUID, len(matchPlayers))
	for i, mp := range matchPlayers {
		matchIDs[i] = mp.MatchID
	}

	var matches []models.Match
	if len(matchIDs) > 0 {
		database.DB.Where("id IN ?", matchIDs).Find(&matches)
	}

	matchMap := make(map[uuid.UUID]models.Match)
	for _, m := range matches {
		matchMap[m.ID] = m
	}

	var allStats []models.MatchPlayerStats
	if len(matchIDs) > 0 {
		database.DB.Where("match_id IN ? AND user_id = ?", matchIDs, userID).Find(&allStats)
	}
	statsMap := make(map[uuid.UUID]models.MatchPlayerStats)
	for _, s := range allStats {
		statsMap[s.MatchID] = s
	}

	type matchHistoryEntry struct {
		Match  models.Match             `json:"match"`
		Player models.MatchPlayer       `json:"player"`
		Stats  *models.MatchPlayerStats `json:"stats,omitempty"`
	}

	results := make([]matchHistoryEntry, 0, len(matchPlayers))
	for _, mp := range matchPlayers {
		m, ok := matchMap[mp.MatchID]
		if !ok {
			continue
		}
		entry := matchHistoryEntry{
			Match:  m,
			Player: mp,
		}
		if s, ok := statsMap[mp.MatchID]; ok {
			entry.Stats = &s
		}
		results = append(results, entry)
	}

	return c.JSON(fiber.Map{
		"matches": results,
		"total":   total,
		"page":    page,
		"limit":   limit,
	})
}

func GetMatchDetails(c *fiber.Ctx) error {
	matchID, err := uuid.Parse(c.Params("id"))
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid match ID",
		})
	}

	var match models.Match
	if err := database.DB.First(&match, matchID).Error; err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"error": "Match not found",
		})
	}

	var players []models.MatchPlayer
	database.DB.Where("match_id = ?", matchID).
		Preload("User").
		Find(&players)

	var stats []models.MatchPlayerStats
	database.DB.Where("match_id = ?", matchID).Find(&stats)
	statsMap := make(map[uuid.UUID]models.MatchPlayerStats)
	for _, s := range stats {
		statsMap[s.UserID] = s
	}

	type playerWithStats struct {
		models.MatchPlayer
		Stats *models.MatchPlayerStats `json:"stats,omitempty"`
	}

	var team1, team2 []playerWithStats
	for _, p := range players {
		pws := playerWithStats{MatchPlayer: p}
		if s, ok := statsMap[p.UserID]; ok {
			pws.Stats = &s
		}
		if p.Team == 1 {
			team1 = append(team1, pws)
		} else {
			team2 = append(team2, pws)
		}
	}

	return c.JSON(fiber.Map{
		"match": match,
		"team1": team1,
		"team2": team2,
	})
}

func GetMatchLive(c *fiber.Ctx) error {
	matchID, err := uuid.Parse(c.Params("id"))
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid match ID",
		})
	}

	var match models.Match
	if err := database.DB.First(&match, matchID).Error; err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"error": "Match not found",
		})
	}

	var players []models.MatchPlayer
	database.DB.Where("match_id = ?", matchID).
		Preload("User").
		Find(&players)

	var stats []models.MatchPlayerStats
	database.DB.Where("match_id = ?", matchID).Find(&stats)
	statsMap := make(map[uuid.UUID]models.MatchPlayerStats)
	for _, s := range stats {
		statsMap[s.UserID] = s
	}

	var team1, team2 []fiber.Map
	for _, p := range players {
		entry := fiber.Map{
			"user_id":  p.UserID,
			"username": p.User.Username,
			"team":     p.Team,
		}
		if s, ok := statsMap[p.UserID]; ok {
			entry["kills"] = s.Kills
			entry["deaths"] = s.Deaths
			entry["assists"] = s.Assists
			entry["damage"] = s.Damage
			entry["headshots"] = s.Headshots
			entry["mvps"] = s.MVPs
		}
		if p.Team == 1 {
			team1 = append(team1, entry)
		} else {
			team2 = append(team2, entry)
		}
	}

	return c.JSON(fiber.Map{
		"match_id":    match.ID,
		"status":      match.Status,
		"map":         match.Map,
		"team1_name":  match.Team1Name,
		"team2_name":  match.Team2Name,
		"team1_score": match.Team1Score,
		"team2_score": match.Team2Score,
		"team1":       team1,
		"team2":       team2,
		"started_at":  match.StartedAt,
	})
}

func GetMatchConnect(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(uuid.UUID)
	matchID, err := uuid.Parse(c.Params("id"))
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid match ID",
		})
	}

	var mp models.MatchPlayer
	if err := database.DB.Where("match_id = ? AND user_id = ?", matchID, userID).First(&mp).Error; err != nil {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
			"error": "You are not a participant in this match",
		})
	}

	var match models.Match
	if err := database.DB.First(&match, matchID).Error; err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"error": "Match not found",
		})
	}

	if match.ConnectString == nil || *match.ConnectString == "" {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"error":  "Server not ready yet",
			"status": match.Status,
		})
	}

	return c.JSON(fiber.Map{
		"connect_string": *match.ConnectString,
		"status":         match.Status,
	})
}

func LeaveActiveMatch(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(uuid.UUID)
	matchID, err := uuid.Parse(c.Params("id"))
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid match ID",
		})
	}

	var mp models.MatchPlayer
	if err := database.DB.Where("match_id = ? AND user_id = ?", matchID, userID).First(&mp).Error; err != nil {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
			"error": "You are not a participant in this match",
		})
	}

	var match models.Match
	if err := database.DB.First(&match, matchID).Error; err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"error": "Match not found",
		})
	}

	activeStatuses := map[string]bool{
		models.MatchStatusMatched:           true,
		models.MatchStatusServerStarting:    true,
		models.MatchStatusWaitingForPlayers: true,
		models.MatchStatusKnifeRound:        true,
		models.MatchStatusSideSelection:     true,
		models.MatchStatusLive:              true,
	}
	if !activeStatuses[match.Status] {
		return c.Status(fiber.StatusConflict).JSON(fiber.Map{
			"error":  "Match is not active",
			"status": match.Status,
		})
	}

	now := time.Now().UTC()
	if err := database.DB.Model(&models.Match{}).
		Where("id = ? AND status IN ?", matchID, []string{
			models.MatchStatusMatched,
			models.MatchStatusServerStarting,
			models.MatchStatusWaitingForPlayers,
			models.MatchStatusKnifeRound,
			models.MatchStatusSideSelection,
			models.MatchStatusLive,
		}).
		Updates(map[string]interface{}{
			"status":      models.MatchStatusAborted,
			"finished_at": now,
		}).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Failed to leave match",
		})
	}

	return c.JSON(fiber.Map{
		"message": "Match aborted",
		"status":  models.MatchStatusAborted,
	})
}
