package userapi

import (
	"net/http"
	"strconv"
	"strings"

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

const (
	adminSalaryAssetType = "ADMIN_SALARY_USD"
	bdSalaryAssetType    = "BD_SALARY_USD"
	bdStatusActive       = "active"
)

type bdLeaderProfileData struct {
	UserID             string `json:"user_id"`
	Role               string `json:"role"`
	RegionID           int64  `json:"region_id"`
	ParentLeaderUserID string `json:"parent_leader_user_id,omitempty"`
	Status             string `json:"status"`
	CreatedByUserID    string `json:"created_by_user_id,omitempty"`
	CreatedAtMS        int64  `json:"created_at_ms"`
	UpdatedAtMS        int64  `json:"updated_at_ms"`
}

func (h *Handler) getBDLeaderCenterOverview(writer http.ResponseWriter, request *http.Request) {
	userID := auth.UserIDFromContext(request.Context())
	profile, ok := h.resolveActiveBDLeader(writer, request, userID)
	if !ok {
		return
	}
	if h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}

	users, err := h.userProfiles(request, []int64{userID})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	salary, err := h.salaryBalance(request, userID, adminSalaryAssetType)
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	bdResp, err := h.userHostClient.ListBDLeaderBDs(request.Context(), &userv1.ListBDLeaderBDsRequest{
		Meta:         httpkit.UserMeta(request, ""),
		LeaderUserId: userID,
		Status:       bdStatusActive,
		PageSize:     100,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	agencyResp, err := h.userHostClient.ListBDLeaderAgencies(request.Context(), &userv1.ListBDLeaderAgenciesRequest{
		Meta:         httpkit.UserMeta(request, ""),
		LeaderUserId: userID,
		Status:       agencyStatusActive,
		PageSize:     100,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	httpkit.WriteOK(writer, request, map[string]any{
		"profile":    users[userID],
		"bd_profile": bdLeaderProfileFromProto(profile),
		"salary":     salary,
		"overview": map[string]any{
			"total_bd":     len(bdResp.GetBdProfiles()),
			"total_agency": len(agencyResp.GetAgencies()),
		},
	})
}

func (h *Handler) getBDCenterOverview(writer http.ResponseWriter, request *http.Request) {
	userID := auth.UserIDFromContext(request.Context())
	profile, ok := h.resolveActiveBD(writer, request, userID)
	if !ok {
		return
	}
	if h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}

	users, err := h.userProfiles(request, []int64{userID})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	// BD Center 的钱包只读取 BD 自己的工资资产，避免沿用 BD Leader 后台工资资产。
	salary, err := h.salaryBalance(request, userID, bdSalaryAssetType)
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	agencyResp, err := h.userHostClient.ListBDAgencies(request.Context(), &userv1.ListBDAgenciesRequest{
		Meta:     httpkit.UserMeta(request, ""),
		BdUserId: userID,
		Status:   agencyStatusActive,
		PageSize: 100,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	httpkit.WriteOK(writer, request, map[string]any{
		"profile":    users[userID],
		"bd_profile": bdLeaderProfileFromProto(profile),
		"salary":     salary,
		"overview": map[string]any{
			"total_agency": len(agencyResp.GetAgencies()),
		},
	})
}

func (h *Handler) listBDLeaderCenterBDs(writer http.ResponseWriter, request *http.Request) {
	userID := auth.UserIDFromContext(request.Context())
	if _, ok := h.resolveActiveBDLeader(writer, request, userID); !ok {
		return
	}
	pageSize := queryPageSize(request, 20)
	resp, err := h.userHostClient.ListBDLeaderBDs(request.Context(), &userv1.ListBDLeaderBDsRequest{
		Meta:         httpkit.UserMeta(request, ""),
		LeaderUserId: userID,
		Status:       bdStatusActive,
		PageSize:     int32(pageSize),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	agencyResp, err := h.userHostClient.ListBDLeaderAgencies(request.Context(), &userv1.ListBDLeaderAgenciesRequest{
		Meta:         httpkit.UserMeta(request, ""),
		LeaderUserId: userID,
		Status:       agencyStatusActive,
		PageSize:     100,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	agencyCounts := make(map[int64]int)
	for _, agency := range agencyResp.GetAgencies() {
		agencyCounts[agency.GetParentBdUserId()]++
	}

	userIDs := make([]int64, 0, len(resp.GetBdProfiles()))
	for _, profile := range resp.GetBdProfiles() {
		userIDs = append(userIDs, profile.GetUserId())
	}
	users, err := h.userProfiles(request, userIDs)
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	items := make([]map[string]any, 0, len(resp.GetBdProfiles()))
	for _, profile := range resp.GetBdProfiles() {
		user := users[profile.GetUserId()]
		items = append(items, map[string]any{
			"user_id":         int64String(profile.GetUserId()),
			"display_user_id": user.DisplayUserID,
			"username":        user.Username,
			"avatar":          user.Avatar,
			"role":            profile.GetRole(),
			"status":          profile.GetStatus(),
			"region_id":       profile.GetRegionId(),
			"agency_count":    agencyCounts[profile.GetUserId()],
		})
	}
	httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": len(items)})
}

func (h *Handler) listBDLeaderCenterAgencies(writer http.ResponseWriter, request *http.Request) {
	userID := auth.UserIDFromContext(request.Context())
	if _, ok := h.resolveActiveBDLeader(writer, request, userID); !ok {
		return
	}
	pageSize := queryPageSize(request, 20)
	resp, err := h.userHostClient.ListBDLeaderAgencies(request.Context(), &userv1.ListBDLeaderAgenciesRequest{
		Meta:         httpkit.UserMeta(request, ""),
		LeaderUserId: userID,
		Status:       agencyStatusActive,
		PageSize:     int32(pageSize),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	userIDs := make([]int64, 0, len(resp.GetAgencies()))
	for _, agency := range resp.GetAgencies() {
		userIDs = append(userIDs, agency.GetOwnerUserId(), agency.GetParentBdUserId())
	}
	users, err := h.userProfiles(request, userIDs)
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	items := make([]map[string]any, 0, len(resp.GetAgencies()))
	for _, agency := range resp.GetAgencies() {
		membersResp, err := h.userHostClient.GetAgencyMembers(request.Context(), &userv1.GetAgencyMembersRequest{
			Meta:     httpkit.UserMeta(request, ""),
			AgencyId: agency.GetAgencyId(),
			Status:   membershipStatusActive,
		})
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return
		}
		owner := users[agency.GetOwnerUserId()]
		parentBD := users[agency.GetParentBdUserId()]
		items = append(items, map[string]any{
			"agency":        hostAgencyFromProto(agency),
			"owner":         owner,
			"parent_bd":     parentBD,
			"host_count":    len(membersResp.GetMemberships()),
			"created_at_ms": agency.GetCreatedAtMs(),
		})
	}
	httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": len(items)})
}

func (h *Handler) listBDCenterAgencies(writer http.ResponseWriter, request *http.Request) {
	userID := auth.UserIDFromContext(request.Context())
	if _, ok := h.resolveActiveBD(writer, request, userID); !ok {
		return
	}
	pageSize := queryPageSize(request, 20)
	resp, err := h.userHostClient.ListBDAgencies(request.Context(), &userv1.ListBDAgenciesRequest{
		Meta:     httpkit.UserMeta(request, ""),
		BdUserId: userID,
		Status:   agencyStatusActive,
		PageSize: int32(pageSize),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	userIDs := make([]int64, 0, len(resp.GetAgencies())+1)
	userIDs = append(userIDs, userID)
	for _, agency := range resp.GetAgencies() {
		userIDs = append(userIDs, agency.GetOwnerUserId())
	}
	users, err := h.userProfiles(request, userIDs)
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	items := make([]map[string]any, 0, len(resp.GetAgencies()))
	for _, agency := range resp.GetAgencies() {
		// Host count is derived from active membership rows so the H5 list reflects current agency size.
		membersResp, err := h.userHostClient.GetAgencyMembers(request.Context(), &userv1.GetAgencyMembersRequest{
			Meta:     httpkit.UserMeta(request, ""),
			AgencyId: agency.GetAgencyId(),
			Status:   membershipStatusActive,
		})
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return
		}
		items = append(items, map[string]any{
			"agency":        hostAgencyFromProto(agency),
			"owner":         users[agency.GetOwnerUserId()],
			"parent_bd":     users[userID],
			"host_count":    len(membersResp.GetMemberships()),
			"created_at_ms": agency.GetCreatedAtMs(),
		})
	}
	httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": len(items)})
}

func (h *Handler) inviteBDLeaderBD(writer http.ResponseWriter, request *http.Request) {
	h.inviteBDLeaderRole(writer, request, "bd")
}

func (h *Handler) inviteBDLeaderAgency(writer http.ResponseWriter, request *http.Request) {
	h.inviteBDLeaderRole(writer, request, "agency")
}

func (h *Handler) inviteBDLeaderRole(writer http.ResponseWriter, request *http.Request, role string) {
	userID := auth.UserIDFromContext(request.Context())
	if _, ok := h.resolveActiveBDLeader(writer, request, userID); !ok {
		return
	}
	var body map[string]any
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := stringJSONValue(body["command_id"])
	targetUserID := int64JSONValue(body["target_user_id"])
	if commandID == "" || targetUserID <= 0 {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}

	if role == "bd" {
		resp, err := h.userHostClient.InviteBD(request.Context(), &userv1.InviteBDRequest{
			Meta:          httpkit.UserMeta(request, ""),
			CommandId:     commandID,
			InviterUserId: userID,
			TargetUserId:  targetUserID,
		})
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return
		}
		httpkit.WriteOK(writer, request, map[string]any{"invitation": roleInvitationData(resp.GetInvitation())})
		return
	}

	agencyName := stringJSONValue(body["agency_name"])
	if agencyName == "" {
		users, err := h.userProfiles(request, []int64{targetUserID})
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return
		}
		target := users[targetUserID]
		agencyName = firstBDLeaderNonBlank(target.Username, target.DisplayUserID, int64String(targetUserID))
	}
	resp, err := h.userHostClient.InviteAgency(request.Context(), &userv1.InviteAgencyRequest{
		Meta:          httpkit.UserMeta(request, ""),
		CommandId:     commandID,
		InviterUserId: userID,
		TargetUserId:  targetUserID,
		AgencyName:    agencyName,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{"invitation": roleInvitationData(resp.GetInvitation())})
}

func (h *Handler) inviteBDAgency(writer http.ResponseWriter, request *http.Request) {
	userID := auth.UserIDFromContext(request.Context())
	if _, ok := h.resolveActiveBD(writer, request, userID); !ok {
		return
	}
	var body map[string]any
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := stringJSONValue(body["command_id"])
	targetUserID := int64JSONValue(body["target_user_id"])
	if commandID == "" || targetUserID <= 0 {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}

	agencyName := stringJSONValue(body["agency_name"])
	if agencyName == "" {
		users, err := h.userProfiles(request, []int64{targetUserID})
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return
		}
		target := users[targetUserID]
		agencyName = firstBDLeaderNonBlank(target.Username, target.DisplayUserID, int64String(targetUserID))
	}
	// InviteAgency 只发送 pending 邀请；Agency 身份必须由目标用户在消息中心接受后才创建。
	resp, err := h.userHostClient.InviteAgency(request.Context(), &userv1.InviteAgencyRequest{
		Meta:          httpkit.UserMeta(request, ""),
		CommandId:     commandID,
		InviterUserId: userID,
		TargetUserId:  targetUserID,
		AgencyName:    agencyName,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{"invitation": roleInvitationData(resp.GetInvitation())})
}

func (h *Handler) resolveActiveBDLeader(writer http.ResponseWriter, request *http.Request, userID int64) (*userv1.BDProfile, bool) {
	if h.userHostClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return nil, false
	}
	resp, err := h.userHostClient.GetBDProfile(request.Context(), &userv1.GetBDProfileRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
		Role:   "bd_leader",
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return nil, false
	}
	profile := resp.GetBdProfile()
	if profile == nil || profile.GetRole() != "bd_leader" || profile.GetStatus() != bdStatusActive {
		httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
		return nil, false
	}
	return profile, true
}

func (h *Handler) resolveActiveBD(writer http.ResponseWriter, request *http.Request, userID int64) (*userv1.BDProfile, bool) {
	if h.userHostClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return nil, false
	}
	resp, err := h.userHostClient.GetBDProfile(request.Context(), &userv1.GetBDProfileRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
		Role:   "bd",
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return nil, false
	}
	profile := resp.GetBdProfile()
	if profile == nil || profile.GetStatus() != bdStatusActive || profile.GetRole() != "bd" {
		httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
		return nil, false
	}
	return profile, true
}

func bdLeaderProfileFromProto(profile *userv1.BDProfile) bdLeaderProfileData {
	if profile == nil {
		return bdLeaderProfileData{}
	}
	return bdLeaderProfileData{
		UserID:             int64String(profile.GetUserId()),
		Role:               profile.GetRole(),
		RegionID:           profile.GetRegionId(),
		ParentLeaderUserID: int64String(profile.GetParentLeaderUserId()),
		Status:             profile.GetStatus(),
		CreatedByUserID:    int64String(profile.GetCreatedByUserId()),
		CreatedAtMS:        profile.GetCreatedAtMs(),
		UpdatedAtMS:        profile.GetUpdatedAtMs(),
	}
}

func roleInvitationData(invitation *userv1.RoleInvitation) map[string]any {
	if invitation == nil {
		return map[string]any{}
	}
	return map[string]any{
		"invitation_id":         int64String(invitation.GetInvitationId()),
		"command_id":            invitation.GetCommandId(),
		"invitation_type":       invitation.GetInvitationType(),
		"status":                invitation.GetStatus(),
		"inviter_user_id":       int64String(invitation.GetInviterUserId()),
		"inviter_bd_user_id":    int64String(invitation.GetInviterBdUserId()),
		"target_user_id":        int64String(invitation.GetTargetUserId()),
		"region_id":             invitation.GetRegionId(),
		"agency_name":           invitation.GetAgencyName(),
		"parent_bd_user_id":     int64String(invitation.GetParentBdUserId()),
		"parent_leader_user_id": int64String(invitation.GetParentLeaderUserId()),
		"processed_by_user_id":  int64String(invitation.GetProcessedByUserId()),
		"process_reason":        invitation.GetProcessReason(),
		"processed_at_ms":       invitation.GetProcessedAtMs(),
		"created_at_ms":         invitation.GetCreatedAtMs(),
		"updated_at_ms":         invitation.GetUpdatedAtMs(),
	}
}

func queryPageSize(request *http.Request, fallback int) int {
	value, _ := strconv.Atoi(strings.TrimSpace(request.URL.Query().Get("page_size")))
	if value <= 0 {
		value = fallback
	}
	if value > 100 {
		return 100
	}
	return value
}

func stringJSONValue(value any) string {
	switch v := value.(type) {
	case string:
		return strings.TrimSpace(v)
	case float64:
		return strconv.FormatInt(int64(v), 10)
	case int64:
		return strconv.FormatInt(v, 10)
	case int:
		return strconv.Itoa(v)
	default:
		return ""
	}
}

func int64JSONValue(value any) int64 {
	switch v := value.(type) {
	case string:
		parsed, _ := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
		return parsed
	case float64:
		return int64(v)
	case int64:
		return v
	case int:
		return int64(v)
	default:
		return 0
	}
}

func firstBDLeaderNonBlank(values ...string) string {
	for _, value := range values {
		if trimmed := strings.TrimSpace(value); trimmed != "" {
			return trimmed
		}
	}
	return "-"
}
