package gameapi

import (
	"io"
	"net/http"
	"strings"
	"time"

	gamev1 "hyapp.local/api/proto/game/v1"
	userv1 "hyapp.local/api/proto/user/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/gateway-service/internal/auth"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

func (h *Handler) listGames(writer http.ResponseWriter, request *http.Request) {
	if h.gameClient == nil || h.userProfileClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	userID := auth.UserIDFromContext(request.Context())
	userResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	resp, err := h.gameClient.ListGames(request.Context(), &gamev1.ListGamesRequest{
		Meta:           gameMeta(request),
		UserId:         userID,
		Scene:          strings.TrimSpace(request.URL.Query().Get("scene")),
		RoomId:         strings.TrimSpace(request.URL.Query().Get("room_id")),
		RegionId:       userResp.GetUser().GetRegionId(),
		Language:       requestLanguage(request),
		ClientPlatform: httpkit.FirstHeader(request, "X-App-Platform", "X-Platform"),
	})
	httpkit.Write(writer, request, gameListDataFromProto(resp), err)
}

func (h *Handler) listRecentGames(writer http.ResponseWriter, request *http.Request) {
	if h.gameClient == nil || h.userProfileClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	pageSize, ok := recentGamePageSize(request)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	userID := auth.UserIDFromContext(request.Context())
	userResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	// 最近常玩仍传 scene、room、region、language 和 platform，让 game-service 在真实启动记录之外，
	// 继续按当前房间场景和用户区域过滤，避免 App 展示用户已不能进入的游戏。
	resp, err := h.gameClient.ListRecentGames(request.Context(), &gamev1.ListRecentGamesRequest{
		Meta:           gameMeta(request),
		UserId:         userID,
		Scene:          strings.TrimSpace(request.URL.Query().Get("scene")),
		RoomId:         strings.TrimSpace(request.URL.Query().Get("room_id")),
		RegionId:       userResp.GetUser().GetRegionId(),
		Language:       requestLanguage(request),
		ClientPlatform: httpkit.FirstHeader(request, "X-App-Platform", "X-Platform"),
		PageSize:       pageSize,
	})
	httpkit.Write(writer, request, gameListDataFromProto(resp), err)
}

func (h *Handler) getBridgeScript(writer http.ResponseWriter, request *http.Request) {
	if h.gameClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	// 桥接脚本配置是 App 级启动预热信息，不依赖房间和用户区域；保持接口轻量，避免拖慢进 App 主流程。
	resp, err := h.gameClient.GetBridgeScript(request.Context(), &gamev1.GetBridgeScriptRequest{
		Meta: gameMeta(request),
	})
	httpkit.Write(writer, request, gameBridgeScriptDataFromProto(resp), err)
}

func (h *Handler) launchGame(writer http.ResponseWriter, request *http.Request) {
	if h.gameClient == nil || h.userProfileClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	gameID := strings.TrimSpace(request.PathValue("game_id"))
	if gameID == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	var body struct {
		Scene  string `json:"scene"`
		RoomID string `json:"room_id"`
	}
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	userID := auth.UserIDFromContext(request.Context())
	userResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	resp, err := h.gameClient.LaunchGame(request.Context(), &gamev1.LaunchGameRequest{
		Meta:          gameMeta(request),
		UserId:        userID,
		DisplayUserId: userResp.GetUser().GetDisplayUserId(),
		GameId:        gameID,
		Scene:         body.Scene,
		RoomId:        body.RoomID,
		AccessToken:   bearerAccessToken(request),
	})
	httpkit.Write(writer, request, gameLaunchDataFromProto(resp), err)
}

func (h *Handler) handleGameCallback(writer http.ResponseWriter, request *http.Request) {
	if h.gameClient == nil {
		http.Error(writer, "upstream service error", http.StatusBadGateway)
		return
	}
	raw, err := io.ReadAll(request.Body)
	if err != nil {
		http.Error(writer, "invalid request body", http.StatusBadRequest)
		return
	}
	resp, err := h.gameClient.HandleCallback(request.Context(), &gamev1.CallbackRequest{
		Meta:         gameMeta(request),
		PlatformCode: strings.TrimSpace(request.PathValue("platform_code")),
		Operation:    strings.TrimSpace(request.PathValue("operation")),
		RawBody:      raw,
		Headers:      flattenedHeaders(request),
		Query:        flattenedQuery(request),
		RemoteAddr:   request.RemoteAddr,
	})
	if err != nil {
		status := xerr.SpecOf(xerr.ReasonFromGRPC(err)).HTTPStatus
		http.Error(writer, xerr.SpecOf(xerr.ReasonFromGRPC(err)).PublicMessage, status)
		return
	}
	contentType := strings.TrimSpace(resp.GetContentType())
	if contentType == "" {
		contentType = "application/json"
	}
	writer.Header().Set("Content-Type", contentType)
	writer.WriteHeader(http.StatusOK)
	_, _ = writer.Write(resp.GetRawBody())
}

func gameMeta(request *http.Request) *gamev1.RequestMeta {
	return &gamev1.RequestMeta{
		RequestId:     httpkit.RequestIDFromContext(request.Context()),
		Caller:        "gateway-service",
		GatewayNodeId: "",
		ActorUserId:   auth.UserIDFromContext(request.Context()),
		SentAtMs:      time.Now().UnixMilli(),
		AppCode:       appcode.FromContext(request.Context()),
	}
}

func requestLanguage(request *http.Request) string {
	value := httpkit.FirstHeader(request, "X-App-Language", "X-Language")
	if value == "" {
		value = request.Header.Get("Accept-Language")
	}
	if index := strings.Index(value, ","); index >= 0 {
		value = value[:index]
	}
	return strings.TrimSpace(value)
}

func recentGamePageSize(request *http.Request) (int32, bool) {
	if strings.TrimSpace(request.URL.Query().Get("limit")) != "" {
		return httpkit.PositiveInt32Query(request, "limit", 4)
	}
	return httpkit.PositiveInt32Query(request, "page_size", 4)
}

func bearerAccessToken(request *http.Request) string {
	header := strings.TrimSpace(request.Header.Get("Authorization"))
	if !strings.HasPrefix(header, "Bearer ") {
		return ""
	}
	return strings.TrimSpace(strings.TrimPrefix(header, "Bearer "))
}

func flattenedHeaders(request *http.Request) map[string]string {
	headers := make(map[string]string, len(request.Header))
	for key, values := range request.Header {
		if len(values) > 0 {
			headers[key] = values[0]
		}
	}
	return headers
}

func flattenedQuery(request *http.Request) map[string]string {
	query := request.URL.Query()
	result := make(map[string]string, len(query))
	for key, values := range query {
		if len(values) > 0 {
			result[key] = values[0]
		}
	}
	return result
}
