package server

import (
	"net/http"
	"time"

	"galaxy/backend/internal/lobby"
	"galaxy/backend/internal/server/handlers"
	"galaxy/backend/internal/server/httperr"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
)

// AdminGamesHandlers groups the admin-side game-management handlers
// under `/api/v1/admin/games/*`. The current implementation ships real implementations
// backed by `*lobby.Service` and adds the `Create` handler used by the
// new POST /api/v1/admin/games endpoint for public-game creation.
type AdminGamesHandlers struct {
	svc *lobby.Service
	logger *zap.Logger
}

// NewAdminGamesHandlers constructs the handler set. svc may be nil —
// in that case every handler returns 501 not_implemented.
func NewAdminGamesHandlers(svc *lobby.Service, logger *zap.Logger) *AdminGamesHandlers {
	if logger == nil {
		logger = zap.NewNop()
	}
	return &AdminGamesHandlers{svc: svc, logger: logger.Named("http.admin.games")}
}

// List handles GET /api/v1/admin/games.
func (h *AdminGamesHandlers) List() gin.HandlerFunc {
	if h.svc == nil {
		return handlers.NotImplemented("adminGamesList")
	}
	return func(c *gin.Context) {
		page := parsePositiveQueryInt(c.Query("page"), 1)
		pageSize := parsePositiveQueryInt(c.Query("page_size"), 50)
		ctx := c.Request.Context()
		result, err := h.svc.ListAdminGames(ctx, page, pageSize)
		if err != nil {
			respondLobbyError(c, h.logger, "admin games list", ctx, err)
			return
		}
		out := adminGameListWire{
			Items: make([]lobbyGameDetailWire, 0, len(result.Items)),
			Page: result.Page,
			PageSize: result.PageSize,
			Total: result.Total,
		}
		for _, g := range result.Items {
			out.Items = append(out.Items, lobbyGameDetailToWire(g))
		}
		c.JSON(http.StatusOK, out)
	}
}

// Get handles GET /api/v1/admin/games/{game_id}.
func (h *AdminGamesHandlers) Get() gin.HandlerFunc {
	if h.svc == nil {
		return handlers.NotImplemented("adminGamesGet")
	}
	return func(c *gin.Context) {
		gameID, ok := parseGameIDParam(c)
		if !ok {
			return
		}
		ctx := c.Request.Context()
		game, err := h.svc.GetGame(ctx, gameID)
		if err != nil {
			respondLobbyError(c, h.logger, "admin games get", ctx, err)
			return
		}
		c.JSON(http.StatusOK, lobbyGameDetailToWire(game))
	}
}

// Create handles POST /api/v1/admin/games — admin-only public-game
// creation. The body intentionally omits `visibility`; the handler
// hard-codes `visibility=public` and `owner_user_id=NULL`.
func (h *AdminGamesHandlers) Create() gin.HandlerFunc {
	if h.svc == nil {
		return handlers.NotImplemented("adminGamesCreate")
	}
	return func(c *gin.Context) {
		var req adminGameCreateRequestWire
		if err := c.ShouldBindJSON(&req); err != nil {
			httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "request body must be valid JSON")
			return
		}
		enrollmentEndsAt, err := time.Parse(time.RFC3339Nano, req.EnrollmentEndsAt)
		if err != nil {
			httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "enrollment_ends_at must be RFC 3339")
			return
		}
		ctx := c.Request.Context()
		game, err := h.svc.CreateGame(ctx, lobby.CreateGameInput{
			OwnerUserID: nil,
			Visibility: lobby.VisibilityPublic,
			GameName: req.GameName,
			Description: req.Description,
			MinPlayers: req.MinPlayers,
			MaxPlayers: req.MaxPlayers,
			StartGapHours: req.StartGapHours,
			StartGapPlayers: req.StartGapPlayers,
			EnrollmentEndsAt: enrollmentEndsAt,
			TurnSchedule: req.TurnSchedule,
			TargetEngineVersion: req.TargetEngineVersion,
		})
		if err != nil {
			respondLobbyError(c, h.logger, "admin games create", ctx, err)
			return
		}
		c.JSON(http.StatusCreated, lobbyGameDetailToWire(game))
	}
}

// ForceStart handles POST /api/v1/admin/games/{game_id}/force-start.
func (h *AdminGamesHandlers) ForceStart() gin.HandlerFunc {
	if h.svc == nil {
		return handlers.NotImplemented("adminGamesForceStart")
	}
	return func(c *gin.Context) {
		gameID, ok := parseGameIDParam(c)
		if !ok {
			return
		}
		ctx := c.Request.Context()
		updated, err := h.svc.AdminForceStart(ctx, gameID)
		if err != nil {
			respondLobbyError(c, h.logger, "admin games force-start", ctx, err)
			return
		}
		c.JSON(http.StatusAccepted, lobbyGameStateChangeToWire(updated))
	}
}

// ForceStop handles POST /api/v1/admin/games/{game_id}/force-stop.
func (h *AdminGamesHandlers) ForceStop() gin.HandlerFunc {
	if h.svc == nil {
		return handlers.NotImplemented("adminGamesForceStop")
	}
	return func(c *gin.Context) {
		gameID, ok := parseGameIDParam(c)
		if !ok {
			return
		}
		ctx := c.Request.Context()
		updated, err := h.svc.AdminForceStop(ctx, gameID)
		if err != nil {
			respondLobbyError(c, h.logger, "admin games force-stop", ctx, err)
			return
		}
		c.JSON(http.StatusOK, lobbyGameStateChangeToWire(updated))
	}
}

// BanMember handles POST /api/v1/admin/games/{game_id}/ban-member.
func (h *AdminGamesHandlers) BanMember() gin.HandlerFunc {
	if h.svc == nil {
		return handlers.NotImplemented("adminGamesBanMember")
	}
	return func(c *gin.Context) {
		gameID, ok := parseGameIDParam(c)
		if !ok {
			return
		}
		var req adminGameBanMemberRequestWire
		if err := c.ShouldBindJSON(&req); err != nil {
			httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "request body must be valid JSON")
			return
		}
		userID, err := uuid.Parse(req.UserID)
		if err != nil {
			httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "user_id must be a valid UUID")
			return
		}
		ctx := c.Request.Context()
		updated, err := h.svc.AdminBanMember(ctx, gameID, userID, req.Reason)
		if err != nil {
			respondLobbyError(c, h.logger, "admin games ban-member", ctx, err)
			return
		}
		c.JSON(http.StatusOK, lobbyMembershipDetailToWire(updated))
	}
}

// adminGameListWire mirrors `AdminGameList`.
type adminGameListWire struct {
	Items []lobbyGameDetailWire `json:"items"`
	Page int `json:"page"`
	PageSize int `json:"page_size"`
	Total int `json:"total"`
}

// adminGameCreateRequestWire mirrors `AdminGameCreateRequest`.
type adminGameCreateRequestWire struct {
	GameName string `json:"game_name"`
	Description string `json:"description"`
	MinPlayers int32 `json:"min_players"`
	MaxPlayers int32 `json:"max_players"`
	StartGapHours int32 `json:"start_gap_hours"`
	StartGapPlayers int32 `json:"start_gap_players"`
	EnrollmentEndsAt string `json:"enrollment_ends_at"`
	TurnSchedule string `json:"turn_schedule"`
	TargetEngineVersion string `json:"target_engine_version"`
}

// adminGameBanMemberRequestWire mirrors `AdminGameBanMemberRequest`.
type adminGameBanMemberRequestWire struct {
	UserID string `json:"user_id"`
	Reason string `json:"reason"`
}
