package handlers

import (
	"time"

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

type joinQueueRequest struct {
	GameMode string `json:"game_mode"`
}

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

	var req joinQueueRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid request body",
		})
	}

	if req.GameMode == "" {
		req.GameMode = "competitive"
	}
	if req.GameMode != "competitive" && req.GameMode != "casual" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid game mode. Must be 'competitive' or 'casual'",
		})
	}

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

	if user.SteamID == nil || *user.SteamID == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "You must link your Steam account before queueing",
		})
	}

	ban := services.HasActiveGameBan(userID)
	if ban != nil {
		resp := fiber.Map{
			"error":  "You cannot queue while banned",
			"reason": ban.Reason,
			"type":   ban.Type,
		}
		if ban.ExpiresAt != nil {
			resp["expires_at"] = ban.ExpiresAt
		}
		return c.Status(fiber.StatusForbidden).JSON(resp)
	}

	penalty := services.HasActivePenalty(userID)
	if penalty != nil {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
			"error":      "You have an active cooldown",
			"reason":     penalty.Reason,
			"expires_at": penalty.ExpiresAt,
		})
	}

	var existing models.QueueEntry
	if err := database.DB.Where("user_id = ?", userID).First(&existing).Error; err == nil {
		return c.Status(fiber.StatusConflict).JSON(fiber.Map{
			"error":     "Already in queue",
			"game_mode": existing.GameMode,
			"joined_at": existing.JoinedAt,
		})
	}

	var activeMatchPlayer models.MatchPlayer
	if err := database.DB.
		Joins("JOIN matches ON matches.id = match_players.match_id").
		Where("match_players.user_id = ? AND matches.status IN (?, ?, ?, ?, ?, ?)",
			userID,
			models.MatchStatusMatched,
			models.MatchStatusServerStarting,
			models.MatchStatusWaitingForPlayers,
			models.MatchStatusKnifeRound,
			models.MatchStatusSideSelection,
			models.MatchStatusLive,
		).First(&activeMatchPlayer).Error; err == nil {
		return c.Status(fiber.StatusConflict).JSON(fiber.Map{
			"error":    "You are already in an active match",
			"match_id": activeMatchPlayer.MatchID,
		})
	}

	entry := models.QueueEntry{
		UserID:   userID,
		GameMode: req.GameMode,
		JoinedAt: time.Now().UTC(),
	}

	if err := database.DB.Create(&entry).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Failed to join queue",
		})
	}

	return c.Status(fiber.StatusCreated).JSON(fiber.Map{
		"message":   "Joined queue",
		"game_mode": entry.GameMode,
		"joined_at": entry.JoinedAt,
	})
}

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

	result := database.DB.Where("user_id = ?", userID).Delete(&models.QueueEntry{})
	if result.RowsAffected == 0 {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"error": "Not currently in queue",
		})
	}

	return c.JSON(fiber.Map{
		"message": "Left queue",
	})
}

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

	var entry models.QueueEntry
	if err := database.DB.Where("user_id = ?", userID).First(&entry).Error; err != nil {
		var mp models.MatchPlayer
		if err := database.DB.
			Joins("JOIN matches ON matches.id = match_players.match_id").
			Where("match_players.user_id = ? AND matches.status IN (?, ?, ?, ?, ?, ?)",
				userID,
				models.MatchStatusMatched,
				models.MatchStatusServerStarting,
				models.MatchStatusWaitingForPlayers,
				models.MatchStatusKnifeRound,
				models.MatchStatusSideSelection,
				models.MatchStatusLive,
			).First(&mp).Error; err == nil {
			return c.JSON(fiber.Map{
				"in_queue": false,
				"matched":  true,
				"match_id": mp.MatchID,
			})
		}

		return c.JSON(fiber.Map{
			"in_queue": false,
			"matched":  false,
		})
	}

	var totalInQueue int64
	database.DB.Model(&models.QueueEntry{}).Where("game_mode = ?", entry.GameMode).Count(&totalInQueue)

	waitTime := time.Since(entry.JoinedAt).Seconds()

	return c.JSON(fiber.Map{
		"in_queue":       true,
		"game_mode":      entry.GameMode,
		"joined_at":      entry.JoinedAt,
		"wait_seconds":   int(waitTime),
		"players_in_queue": totalInQueue,
		"matched":        false,
	})
}
