package roomapi

import (
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
	"log/slog"
	"net/http"
	"time"

	roomv1 "hyapp.local/api/proto/room/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/logx"
	"hyapp/pkg/roomid"
	"hyapp/pkg/tencentrtc"
	"hyapp/pkg/xerr"
	"hyapp/services/gateway-service/internal/auth"
)

// issueTencentRTCToken signs Tencent RTC credentials only after room-service confirms presence.
func (h *Handler) issueTencentRTCToken(writer http.ResponseWriter, request *http.Request) {
	var body struct {
		RoomID string `json:"room_id"`
	}

	if !httpkit.Decode(writer, request, &body) {
		return
	}
	if !roomid.ValidStringID(body.RoomID) {
		// Token signing repeats room_id validation to fail closed on historical or dirty rooms.
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}

	userID := auth.UserIDFromContext(request.Context())
	if userID <= 0 {
		// Auth middleware normally blocks this; the handler keeps a defensive guard before signing.
		httpkit.WriteError(writer, request, http.StatusUnauthorized, httpkit.CodeUnauthorized, "unauthorized")
		return
	}
	logCtx := logx.With(request.Context(), slog.String("request_id", httpkit.RequestIDFromContext(request.Context())), slog.String("app_code", appcode.FromContext(request.Context())))

	rtcConfig := h.tencentRTCTokenConfig()
	if err := tencentrtc.ValidateConfig(rtcConfig); err != nil {
		logx.Error(logCtx, "gateway_rtc_token_config_error", err, slog.Int64("user_id", userID), slog.String("room_id", body.RoomID))
		httpkit.WriteError(writer, request, http.StatusInternalServerError, httpkit.CodeInternalError, "internal error")
		return
	}

	if h.roomGuardClient == nil {
		// Without room-service presence, gateway cannot safely decide RTC room entry.
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}

	guardResp, err := h.roomGuardClient.VerifyRoomPresence(request.Context(), &roomv1.VerifyRoomPresenceRequest{
		RoomId:    body.RoomID,
		UserId:    userID,
		RequestId: httpkit.RequestIDFromContext(request.Context()),
		AppCode:   appcode.FromContext(request.Context()),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	if guardResp == nil || !guardResp.GetPresent() {
		h.writeRTCPresenceDenied(writer, request, body.RoomID, userID, guardResp)
		return
	}

	token, err := h.generateTencentRTCToken(userID, body.RoomID)
	if err != nil {
		logx.Error(logCtx, "gateway_rtc_token_sign_failed", err, slog.Int64("user_id", userID), slog.String("room_id", body.RoomID))
		httpkit.WriteError(writer, request, http.StatusInternalServerError, httpkit.CodeInternalError, "internal error")
		return
	}

	logx.Info(logCtx, "gateway_rtc_token_issued",
		slog.Int64("user_id", userID),
		slog.String("rtc_user_id", token.RTCUserID),
		slog.String("room_id", body.RoomID),
		slog.String("str_room_id", token.StrRoomID),
		slog.Int64("room_version", guardResp.GetRoomVersion()),
	)
	httpkit.WriteOK(writer, request, token)
}

func (h *Handler) writeRTCPresenceDenied(writer http.ResponseWriter, request *http.Request, targetRoomID string, userID int64, guardResp *roomv1.VerifyRoomPresenceResponse) {
	reason := "not_in_room"
	roomVersion := int64(0)
	if guardResp != nil {
		reason = guardResp.GetReason()
		roomVersion = guardResp.GetRoomVersion()
	}

	logCtx := logx.With(request.Context(), slog.String("request_id", httpkit.RequestIDFromContext(request.Context())), slog.String("app_code", appcode.FromContext(request.Context())))
	logx.Warn(logCtx, "gateway_rtc_token_denied", slog.Int64("user_id", userID), slog.String("room_id", targetRoomID), slog.Int64("room_version", roomVersion), slog.String("reason", reason))
	if reason == "room_not_found" {
		httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
		return
	}
	if reason == "room_closed" {
		httpkit.WriteError(writer, request, http.StatusConflict, string(xerr.RoomClosed), "room closed")
		return
	}

	httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
}

var TimeNow = defaultTimeNow

func defaultTimeNow() time.Time {
	return time.Now().UTC()
}

func (h *Handler) tencentRTCTokenConfig() tencentrtc.TokenConfig {
	return h.rtcTokenConfig
}

func (h *Handler) generateTencentRTCToken(userID int64, roomID string) (tencentrtc.TokenResult, error) {
	// JoinRoom 内嵌签发和独立 /rtc/token 使用同一套签名策略，避免两条入口出现 RTC 身份漂移。
	return tencentrtc.GenerateToken(h.tencentRTCTokenConfig(), userID, roomID, TimeNow())
}
