package userapi

import (
	"net/http"
	"strings"

	userv1 "hyapp.local/api/proto/user/v1"
	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/services/gateway-service/internal/auth"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

type cpUserProfileData struct {
	UserID        string `json:"user_id"`
	DisplayUserID string `json:"display_user_id"`
	Username      string `json:"username"`
	Avatar        string `json:"avatar"`
}

type cpGiftSnapshotData struct {
	GiftID           string `json:"gift_id"`
	GiftName         string `json:"gift_name"`
	GiftIconURL      string `json:"gift_icon_url"`
	GiftAnimationURL string `json:"gift_animation_url"`
	GiftCount        int32  `json:"gift_count"`
	GiftValue        int64  `json:"gift_value"`
	BillingReceiptID string `json:"billing_receipt_id"`
}

type cpApplicationData struct {
	ApplicationID string              `json:"application_id"`
	RelationType  string              `json:"relation_type"`
	Status        string              `json:"status"`
	Requester     *cpUserProfileData  `json:"requester,omitempty"`
	Target        *cpUserProfileData  `json:"target,omitempty"`
	RoomID        string              `json:"room_id"`
	RoomRegionID  int64               `json:"room_region_id"`
	Gift          *cpGiftSnapshotData `json:"gift,omitempty"`
	CreatedAtMS   int64               `json:"created_at_ms"`
	UpdatedAtMS   int64               `json:"updated_at_ms"`
	ExpiresAtMS   int64               `json:"expires_at_ms"`
	DecidedAtMS   int64               `json:"decided_at_ms"`
}

type cpRelationshipData struct {
	RelationshipID        string             `json:"relationship_id"`
	RelationType          string             `json:"relation_type"`
	Status                string             `json:"status"`
	Me                    *cpUserProfileData `json:"me,omitempty"`
	Partner               *cpUserProfileData `json:"partner,omitempty"`
	IntimacyValue         int64              `json:"intimacy_value"`
	Level                 int32              `json:"level"`
	CurrentLevelThreshold int64              `json:"current_level_threshold"`
	NextLevelThreshold    int64              `json:"next_level_threshold"`
	NeededForNextLevel    int64              `json:"needed_for_next_level"`
	LevelProgressPercent  int32              `json:"level_progress_percent"`
	MaxLevel              bool               `json:"max_level"`
	BreakupCostCoins      int64              `json:"breakup_cost_coins"`
	FormedAtMS            int64              `json:"formed_at_ms"`
	EndedAtMS             int64              `json:"ended_at_ms"`
	UpdatedAtMS           int64              `json:"updated_at_ms"`
}

type cpRejectRequest struct {
	Reason string `json:"reason"`
}

type cpBreakRelationshipRequest struct {
	CommandID string `json:"command_id"`
}

// listCPApplications 返回当前用户收到或发出的 CP/兄弟/姐妹申请。
func (h *Handler) listCPApplications(writer http.ResponseWriter, request *http.Request) {
	if h.userCPClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	page, pageSize, ok := socialPage(request)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.userCPClient.ListCPApplications(request.Context(), &userv1.ListCPApplicationsRequest{
		Meta:      httpkit.UserMeta(request, ""),
		UserId:    auth.UserIDFromContext(request.Context()),
		Direction: strings.TrimSpace(request.URL.Query().Get("direction")),
		Status:    strings.TrimSpace(request.URL.Query().Get("status")),
		Page:      page,
		PageSize:  pageSize,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	applications := make([]cpApplicationData, 0, len(resp.GetApplications()))
	for _, item := range resp.GetApplications() {
		applications = append(applications, cpApplicationFromProto(item))
	}
	httpkit.WriteOK(writer, request, map[string]any{
		"applications":   applications,
		"total":          resp.GetTotal(),
		"server_time_ms": resp.GetServerTimeMs(),
	})
}

// acceptCPApplication 同意一条申请；同意后同一对用户其他 pending 类型由 user-service 阻断。
func (h *Handler) acceptCPApplication(writer http.ResponseWriter, request *http.Request) {
	if h.userCPClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	applicationID := strings.TrimSpace(request.PathValue("application_id"))
	if applicationID == "" {
		httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
		return
	}
	resp, err := h.userCPClient.AcceptCPApplication(request.Context(), &userv1.AcceptCPApplicationRequest{
		Meta:          httpkit.UserMeta(request, ""),
		UserId:        auth.UserIDFromContext(request.Context()),
		ApplicationId: applicationID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{
		"application":  cpApplicationFromProto(resp.GetApplication()),
		"relationship": cpRelationshipFromProto(resp.GetRelationship()),
	})
}

// rejectCPApplication 拒绝一条申请；拒绝不会删除历史申请，客户端仍可看到 decided 状态。
func (h *Handler) rejectCPApplication(writer http.ResponseWriter, request *http.Request) {
	if h.userCPClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	applicationID := strings.TrimSpace(request.PathValue("application_id"))
	if applicationID == "" {
		httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
		return
	}
	var body cpRejectRequest
	if request.Body != nil && request.ContentLength != 0 && !httpkit.Decode(writer, request, &body) {
		return
	}
	resp, err := h.userCPClient.RejectCPApplication(request.Context(), &userv1.RejectCPApplicationRequest{
		Meta:          httpkit.UserMeta(request, ""),
		UserId:        auth.UserIDFromContext(request.Context()),
		ApplicationId: applicationID,
		Reason:        body.Reason,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{"application": cpApplicationFromProto(resp.GetApplication())})
}

// listCPRelationships 返回当前用户 active 关系；服务端固定限制一个用户只能同时存在一条关系。
func (h *Handler) listCPRelationships(writer http.ResponseWriter, request *http.Request) {
	if h.userCPClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	page, pageSize, ok := socialPage(request)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.userCPClient.ListCPRelationships(request.Context(), &userv1.ListCPRelationshipsRequest{
		Meta:         httpkit.UserMeta(request, ""),
		UserId:       auth.UserIDFromContext(request.Context()),
		RelationType: strings.TrimSpace(request.URL.Query().Get("relation_type")),
		Page:         page,
		PageSize:     pageSize,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	relationships := make([]cpRelationshipData, 0, len(resp.GetRelationships()))
	for _, item := range resp.GetRelationships() {
		relationships = append(relationships, cpRelationshipFromProto(item))
	}
	httpkit.WriteOK(writer, request, map[string]any{
		"relationships": relationships,
		"total":         resp.GetTotal(),
	})
}

// breakCPRelationship 先在 user-service 创建解除占位，再扣钱包，最后确认关系 ended，避免并发请求重复扣费。
func (h *Handler) breakCPRelationship(writer http.ResponseWriter, request *http.Request) {
	if h.userCPClient == nil || h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	relationshipID := strings.TrimSpace(request.PathValue("relationship_id"))
	if relationshipID == "" {
		httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
		return
	}
	var body cpBreakRelationshipRequest
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := strings.TrimSpace(body.CommandID)
	if commandID == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "command_id is required")
		return
	}
	userID := auth.UserIDFromContext(request.Context())
	meta := httpkit.UserMeta(request, "")
	prepare, err := h.userCPClient.PrepareBreakCPRelationship(request.Context(), &userv1.PrepareBreakCPRelationshipRequest{
		Meta:           meta,
		UserId:         userID,
		RelationshipId: relationshipID,
		CommandId:      commandID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	if prepare.GetStatus() == "confirmed" {
		httpkit.WriteOK(writer, request, map[string]any{
			"relationship":          cpRelationshipFromProto(prepare.GetRelationship()),
			"breakup_cost_coins":    prepare.GetBreakupCostCoins(),
			"wallet_transaction_id": prepare.GetWalletTransactionId(),
			"coin_balance_after":    prepare.GetCoinBalanceAfter(),
		})
		return
	}

	walletTransactionID := ""
	coinBalanceAfter := int64(0)
	cost := prepare.GetBreakupCostCoins()
	if cost > 0 {
		walletResp, walletErr := h.walletClient.DebitCPBreakupFee(request.Context(), &walletv1.DebitCPBreakupFeeRequest{
			CommandId:      commandID,
			AppCode:        meta.GetAppCode(),
			UserId:         userID,
			RelationshipId: relationshipID,
			RelationType:   prepare.GetRelationship().GetRelationType(),
			Amount:         cost,
		})
		if walletErr != nil {
			// 钱包失败表示关系不应解除；取消 pending 占位后把真实钱包错误返回给客户端。
			_, _ = h.userCPClient.CancelBreakCPRelationship(request.Context(), &userv1.CancelBreakCPRelationshipRequest{
				Meta:           meta,
				UserId:         userID,
				RelationshipId: relationshipID,
				CommandId:      commandID,
				Reason:         "wallet_debit_failed",
			})
			httpkit.WriteRPCError(writer, request, walletErr)
			return
		}
		walletTransactionID = walletResp.GetTransactionId()
		coinBalanceAfter = walletResp.GetCoinBalanceAfter()
	}
	confirmed, err := h.userCPClient.ConfirmBreakCPRelationship(request.Context(), &userv1.ConfirmBreakCPRelationshipRequest{
		Meta:                meta,
		UserId:              userID,
		RelationshipId:      relationshipID,
		CommandId:           commandID,
		WalletTransactionId: walletTransactionID,
		PaidCoinAmount:      cost,
		CoinBalanceAfter:    coinBalanceAfter,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{
		"relationship":          cpRelationshipFromProto(confirmed.GetRelationship()),
		"breakup_cost_coins":    confirmed.GetBreakupCostCoins(),
		"wallet_transaction_id": confirmed.GetWalletTransactionId(),
		"coin_balance_after":    confirmed.GetCoinBalanceAfter(),
	})
}

func cpApplicationFromProto(item *userv1.CPApplication) cpApplicationData {
	if item == nil {
		return cpApplicationData{}
	}
	return cpApplicationData{
		ApplicationID: item.GetApplicationId(),
		RelationType:  item.GetRelationType(),
		Status:        item.GetStatus(),
		Requester:     cpUserProfileFromProto(item.GetRequester()),
		Target:        cpUserProfileFromProto(item.GetTarget()),
		RoomID:        item.GetRoomId(),
		RoomRegionID:  item.GetRoomRegionId(),
		Gift:          cpGiftSnapshotFromProto(item.GetGift()),
		CreatedAtMS:   item.GetCreatedAtMs(),
		UpdatedAtMS:   item.GetUpdatedAtMs(),
		ExpiresAtMS:   item.GetExpiresAtMs(),
		DecidedAtMS:   item.GetDecidedAtMs(),
	}
}

func cpRelationshipFromProto(item *userv1.CPRelationship) cpRelationshipData {
	if item == nil {
		return cpRelationshipData{}
	}
	return cpRelationshipData{
		RelationshipID:        item.GetRelationshipId(),
		RelationType:          item.GetRelationType(),
		Status:                item.GetStatus(),
		Me:                    cpUserProfileFromProto(item.GetMe()),
		Partner:               cpUserProfileFromProto(item.GetPartner()),
		IntimacyValue:         item.GetIntimacyValue(),
		Level:                 item.GetLevel(),
		CurrentLevelThreshold: item.GetCurrentLevelThreshold(),
		NextLevelThreshold:    item.GetNextLevelThreshold(),
		NeededForNextLevel:    item.GetNeededForNextLevel(),
		LevelProgressPercent:  item.GetLevelProgressPercent(),
		MaxLevel:              item.GetMaxLevel(),
		BreakupCostCoins:      item.GetBreakupCostCoins(),
		FormedAtMS:            item.GetFormedAtMs(),
		EndedAtMS:             item.GetEndedAtMs(),
		UpdatedAtMS:           item.GetUpdatedAtMs(),
	}
}

func cpUserProfileFromProto(item *userv1.CPUserProfile) *cpUserProfileData {
	if item == nil || item.GetUserId() <= 0 {
		return nil
	}
	return &cpUserProfileData{
		UserID:        userIDString(item.GetUserId()),
		DisplayUserID: item.GetDisplayUserId(),
		Username:      item.GetUsername(),
		Avatar:        item.GetAvatar(),
	}
}

func cpGiftSnapshotFromProto(item *userv1.CPGiftSnapshot) *cpGiftSnapshotData {
	if item == nil {
		return nil
	}
	return &cpGiftSnapshotData{
		GiftID:           item.GetGiftId(),
		GiftName:         item.GetGiftName(),
		GiftIconURL:      item.GetGiftIconUrl(),
		GiftAnimationURL: item.GetGiftAnimationUrl(),
		GiftCount:        item.GetGiftCount(),
		GiftValue:        item.GetGiftValue(),
		BillingReceiptID: item.GetBillingReceiptId(),
	}
}
