package userapi

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

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

const (
	agencySalaryAssetType    = "AGENCY_SALARY_USD"
	hostSalaryAssetType      = "HOST_SALARY_USD"
	agencyStatusActive       = "active"
	membershipStatusActive   = "active"
	membershipTypeOwner      = "owner"
	applicationStatusPending = "pending"
)

type agencyCenterSalaryData struct {
	AssetType       string  `json:"asset_type"`
	AvailableAmount int64   `json:"available_amount"`
	FrozenAmount    int64   `json:"frozen_amount"`
	DisplayAmount   float64 `json:"display_amount"`
}

type agencyCenterUserData struct {
	UserID        string `json:"user_id"`
	DisplayUserID string `json:"display_user_id"`
	Username      string `json:"username"`
	Avatar        string `json:"avatar,omitempty"`
	Country       string `json:"country,omitempty"`
}

type agencyCenterHostData struct {
	MembershipID   string                 `json:"membership_id"`
	AgencyID       string                 `json:"agency_id"`
	HostUserID     string                 `json:"host_user_id"`
	DisplayUserID  string                 `json:"display_user_id"`
	Username       string                 `json:"username"`
	Avatar         string                 `json:"avatar,omitempty"`
	RegionID       int64                  `json:"region_id"`
	MembershipType string                 `json:"membership_type"`
	Status         string                 `json:"status"`
	JoinedAtMS     int64                  `json:"joined_at_ms"`
	Salary         agencyCenterSalaryData `json:"salary"`
}

type agencyCenterApplicationData struct {
	ApplicationID   string               `json:"application_id"`
	CommandID       string               `json:"command_id"`
	ApplicantUserID string               `json:"applicant_user_id"`
	AgencyID        string               `json:"agency_id"`
	RegionID        int64                `json:"region_id"`
	Status          string               `json:"status"`
	ReviewReason    string               `json:"review_reason,omitempty"`
	ReviewedAtMS    int64                `json:"reviewed_at_ms,omitempty"`
	CreatedAtMS     int64                `json:"created_at_ms"`
	UpdatedAtMS     int64                `json:"updated_at_ms"`
	Applicant       agencyCenterUserData `json:"applicant"`
}

func (h *Handler) getAgencyCenterOverview(writer http.ResponseWriter, request *http.Request) {
	agency, ok := h.resolveCurrentOwnerAgency(writer, request)
	if !ok {
		return
	}
	if h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}

	salary, err := h.salaryBalance(request, agency.GetOwnerUserId(), agencySalaryAssetType)
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	applications, err := h.userHostClient.GetAgencyApplications(request.Context(), &userv1.GetAgencyApplicationsRequest{
		Meta:     httpkit.UserMeta(request, ""),
		AgencyId: agency.GetAgencyId(),
		Status:   applicationStatusPending,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	var bd *agencyCenterUserData
	if agency.GetParentBdUserId() > 0 {
		profiles, err := h.userProfiles(request, []int64{agency.GetParentBdUserId()})
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return
		}
		if profile, exists := profiles[agency.GetParentBdUserId()]; exists {
			bd = &profile
		}
	}

	httpkit.WriteOK(writer, request, map[string]any{
		"agency":                    hostAgencyFromProto(agency),
		"bd":                        bd,
		"salary":                    salary,
		"pending_application_count": len(applications.GetApplications()),
		"server_time_ms":            time.Now().UnixMilli(),
	})
}

func (h *Handler) getAgencyCenterPlatformPolicy(writer http.ResponseWriter, request *http.Request) {
	agency, ok := h.resolveCurrentOwnerAgency(writer, request)
	if !ok {
		return
	}
	if h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}

	cycleKey := time.Now().UTC().Format("2006-01")
	// Agency Center 不是主播身份，不能复用 host-center 的 active host 校验；这里固定用当前 agency 的区域读取完整工资政策。
	resp, err := h.walletClient.GetActiveHostSalaryPolicy(request.Context(), &walletv1.GetActiveHostSalaryPolicyRequest{
		RequestId:             httpkit.RequestIDFromContext(request.Context()),
		AppCode:               appcode.FromContext(request.Context()),
		RegionId:              agency.GetRegionId(),
		SettlementTriggerMode: "",
		CycleKey:              cycleKey,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	var progressData any
	policy := resp.GetPolicy()
	if resp.GetFound() && strings.EqualFold(strings.TrimSpace(policy.GetPolicyType()), hostPolicyTypeSalaryDiamond) {
		// 只有工资型政策才存在 owner 的周期主播进度；永久 POINT_DIAMOND 钱包不读取、更不暴露旧工资周期数据。
		progressResp, err := h.walletClient.GetHostSalaryProgress(request.Context(), &walletv1.GetHostSalaryProgressRequest{
			RequestId:  httpkit.RequestIDFromContext(request.Context()),
			AppCode:    appcode.FromContext(request.Context()),
			HostUserId: agency.GetOwnerUserId(),
			CycleKey:   cycleKey,
		})
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return
		}
		progressData = hostCenterSalaryProgressFromProto(progressResp.GetProgress())
	}
	// H5 需要完整政策明细和 owner 本人的周期进度；Agency 没有单主播等级卡，因此不返回 level_progress。
	httpkit.WriteOK(writer, request, map[string]any{
		"found":            resp.GetFound(),
		"agency_region_id": agency.GetRegionId(),
		"policy":           hostCenterPolicyFromProto(policy),
		"progress":         progressData,
	})
}

func (h *Handler) listAgencyCenterHosts(writer http.ResponseWriter, request *http.Request) {
	agency, ok := h.resolveCurrentOwnerAgency(writer, request)
	if !ok {
		return
	}
	if h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	resp, 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
	}

	memberships := make([]*userv1.AgencyMembership, 0, len(resp.GetMemberships()))
	userIDs := make([]int64, 0, len(resp.GetMemberships()))
	for _, membership := range resp.GetMemberships() {
		if membership.GetHostUserId() == agency.GetOwnerUserId() || membership.GetMembershipType() == membershipTypeOwner {
			continue
		}
		memberships = append(memberships, membership)
		userIDs = append(userIDs, membership.GetHostUserId())
	}
	profiles, err := h.userProfiles(request, userIDs)
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	items := make([]agencyCenterHostData, 0, len(memberships))
	for _, membership := range memberships {
		salary, err := h.salaryBalance(request, membership.GetHostUserId(), hostSalaryAssetType)
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return
		}
		profile := profiles[membership.GetHostUserId()]
		items = append(items, agencyCenterHostData{
			MembershipID:   int64String(membership.GetMembershipId()),
			AgencyID:       int64String(membership.GetAgencyId()),
			HostUserID:     int64String(membership.GetHostUserId()),
			DisplayUserID:  profile.DisplayUserID,
			Username:       profile.Username,
			Avatar:         profile.Avatar,
			RegionID:       membership.GetRegionId(),
			MembershipType: membership.GetMembershipType(),
			Status:         membership.GetStatus(),
			JoinedAtMS:     membership.GetJoinedAtMs(),
			Salary:         salary,
		})
	}

	httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": len(items)})
}

func (h *Handler) listAgencyCenterApplications(writer http.ResponseWriter, request *http.Request) {
	agency, ok := h.resolveCurrentOwnerAgency(writer, request)
	if !ok {
		return
	}
	status := strings.TrimSpace(request.URL.Query().Get("status"))
	if status == "" {
		status = applicationStatusPending
	}
	if status != applicationStatusPending && status != "approved" && status != "rejected" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}

	resp, err := h.userHostClient.GetAgencyApplications(request.Context(), &userv1.GetAgencyApplicationsRequest{
		Meta:     httpkit.UserMeta(request, ""),
		AgencyId: agency.GetAgencyId(),
		Status:   status,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	userIDs := make([]int64, 0, len(resp.GetApplications()))
	for _, application := range resp.GetApplications() {
		userIDs = append(userIDs, application.GetApplicantUserId())
	}
	profiles, err := h.userProfiles(request, userIDs)
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	items := make([]agencyCenterApplicationData, 0, len(resp.GetApplications()))
	for _, application := range resp.GetApplications() {
		items = append(items, agencyCenterApplicationData{
			ApplicationID:   int64String(application.GetApplicationId()),
			CommandID:       application.GetCommandId(),
			ApplicantUserID: int64String(application.GetApplicantUserId()),
			AgencyID:        int64String(application.GetAgencyId()),
			RegionID:        application.GetRegionId(),
			Status:          application.GetStatus(),
			ReviewReason:    application.GetReviewReason(),
			ReviewedAtMS:    application.GetReviewedAtMs(),
			CreatedAtMS:     application.GetCreatedAtMs(),
			UpdatedAtMS:     application.GetUpdatedAtMs(),
			Applicant:       profiles[application.GetApplicantUserId()],
		})
	}

	httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": len(items)})
}

func (h *Handler) reviewAgencyCenterApplication(writer http.ResponseWriter, request *http.Request) {
	applicationID, ok := httpkit.PositiveInt64PathValue(request, "application_id")
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	if _, ok := h.resolveCurrentOwnerAgency(writer, request); !ok {
		return
	}
	var body struct {
		CommandID string `json:"command_id"`
		Decision  string `json:"decision"`
		Reason    string `json:"reason"`
	}
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := strings.TrimSpace(body.CommandID)
	decision := strings.ToLower(strings.TrimSpace(body.Decision))
	if commandID == "" || (decision != "approve" && decision != "reject") {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}

	resp, err := h.userHostClient.ReviewAgencyApplication(request.Context(), &userv1.ReviewAgencyApplicationRequest{
		Meta:           httpkit.UserMeta(request, ""),
		CommandId:      commandID,
		ReviewerUserId: auth.UserIDFromContext(request.Context()),
		ApplicationId:  applicationID,
		Decision:       decision,
		Reason:         strings.TrimSpace(body.Reason),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{
		"application": hostAgencyApplicationFromProto(resp.GetApplication()),
		"membership":  agencyMembershipData(resp.GetMembership()),
	})
}

func (h *Handler) removeAgencyCenterHost(writer http.ResponseWriter, request *http.Request) {
	hostUserID, ok := httpkit.PositiveInt64PathValue(request, "host_user_id")
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	agency, ok := h.resolveCurrentOwnerAgency(writer, request)
	if !ok {
		return
	}
	var body struct {
		CommandID string `json:"command_id"`
		Reason    string `json:"reason"`
	}
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := strings.TrimSpace(body.CommandID)
	if commandID == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}

	resp, err := h.userHostClient.KickAgencyHost(request.Context(), &userv1.KickAgencyHostRequest{
		Meta:           httpkit.UserMeta(request, ""),
		CommandId:      commandID,
		OperatorUserId: auth.UserIDFromContext(request.Context()),
		AgencyId:       agency.GetAgencyId(),
		HostUserId:     hostUserID,
		Reason:         strings.TrimSpace(body.Reason),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{"membership": agencyMembershipData(resp.GetMembership())})
}

func (h *Handler) inviteAgencyCenterHost(writer http.ResponseWriter, request *http.Request) {
	if _, ok := h.resolveCurrentOwnerAgency(writer, request); !ok {
		return
	}
	var body struct {
		CommandID    string `json:"command_id"`
		TargetUserID string `json:"target_user_id"`
	}
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	targetUserID := int64JSONValue(body.TargetUserID)
	if strings.TrimSpace(body.CommandID) == "" || targetUserID <= 0 {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}

	// Agency owner 只负责发送邀请；目标用户接受后 user-service 再原子创建 Host 身份和成员关系。
	resp, err := h.userHostClient.InviteHost(request.Context(), &userv1.InviteHostRequest{
		Meta:          httpkit.UserMeta(request, ""),
		CommandId:     strings.TrimSpace(body.CommandID),
		InviterUserId: auth.UserIDFromContext(request.Context()),
		TargetUserId:  targetUserID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{"invitation": roleInvitationData(resp.GetInvitation())})
}

func (h *Handler) resolveCurrentOwnerAgency(writer http.ResponseWriter, request *http.Request) (*userv1.Agency, bool) {
	if h.userHostClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return nil, false
	}
	userID := auth.UserIDFromContext(request.Context())
	roleResp, err := h.userHostClient.GetUserRoleSummary(request.Context(), &userv1.GetUserRoleSummaryRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return nil, false
	}
	summary := roleResp.GetSummary()
	if summary == nil || summary.GetAgencyId() <= 0 {
		httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
		return nil, false
	}
	agencyResp, err := h.userHostClient.GetAgency(request.Context(), &userv1.GetAgencyRequest{
		Meta:     httpkit.UserMeta(request, ""),
		AgencyId: summary.GetAgencyId(),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return nil, false
	}
	agency := agencyResp.GetAgency()
	// Agency Center 只属于当前 active Agency owner。manager_profiles 是 Manager Center 权限事实，
	// 不能作为 Agency owner 的前置条件；普通成员即使 role summary 带 current agency，也会被 owner 校验挡住。
	if agency == nil || agency.GetOwnerUserId() != userID || agency.GetStatus() != agencyStatusActive {
		httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
		return nil, false
	}
	return agency, true
}

func (h *Handler) userProfiles(request *http.Request, userIDs []int64) (map[int64]agencyCenterUserData, error) {
	profiles := make(map[int64]agencyCenterUserData, len(userIDs))
	if len(userIDs) == 0 {
		return profiles, nil
	}
	if h.userProfileClient == nil {
		return profiles, nil
	}
	resp, err := h.userProfileClient.BatchGetUsers(request.Context(), &userv1.BatchGetUsersRequest{
		Meta:    httpkit.UserMeta(request, ""),
		UserIds: userIDs,
	})
	if err != nil {
		return nil, err
	}
	for userID, user := range resp.GetUsers() {
		profiles[userID] = agencyCenterUserFromProto(user)
	}
	for _, userID := range userIDs {
		if _, exists := profiles[userID]; !exists {
			profiles[userID] = agencyCenterUserData{UserID: int64String(userID)}
		}
	}
	return profiles, nil
}

func (h *Handler) salaryBalance(request *http.Request, userID int64, assetType string) (agencyCenterSalaryData, error) {
	resp, err := h.walletClient.GetBalances(request.Context(), &walletv1.GetBalancesRequest{
		RequestId:  httpkit.RequestIDFromContext(request.Context()),
		UserId:     userID,
		AssetTypes: []string{assetType},
		AppCode:    appcode.FromContext(request.Context()),
	})
	if err != nil {
		return agencyCenterSalaryData{}, err
	}
	for _, balance := range resp.GetBalances() {
		if balance.GetAssetType() == assetType {
			return salaryDataFromProto(balance), nil
		}
	}
	return agencyCenterSalaryData{AssetType: assetType}, nil
}

func salaryDataFromProto(balance *walletv1.AssetBalance) agencyCenterSalaryData {
	if balance == nil {
		return agencyCenterSalaryData{}
	}
	return agencyCenterSalaryData{
		AssetType:       balance.GetAssetType(),
		AvailableAmount: balance.GetAvailableAmount(),
		FrozenAmount:    balance.GetFrozenAmount(),
		DisplayAmount:   float64(balance.GetAvailableAmount()) / 100,
	}
}

func agencyCenterUserFromProto(user *userv1.User) agencyCenterUserData {
	if user == nil {
		return agencyCenterUserData{}
	}
	return agencyCenterUserData{
		UserID:        int64String(user.GetUserId()),
		DisplayUserID: user.GetDisplayUserId(),
		Username:      user.GetUsername(),
		Avatar:        user.GetAvatar(),
		Country:       firstBDLeaderNonBlank(user.GetCountryDisplayName(), user.GetCountryName(), user.GetCountry()),
	}
}

func agencyMembershipData(membership *userv1.AgencyMembership) map[string]any {
	if membership == nil {
		return map[string]any{}
	}
	return map[string]any{
		"membership_id":   int64String(membership.GetMembershipId()),
		"agency_id":       int64String(membership.GetAgencyId()),
		"host_user_id":    int64String(membership.GetHostUserId()),
		"region_id":       membership.GetRegionId(),
		"membership_type": membership.GetMembershipType(),
		"status":          membership.GetStatus(),
		"joined_at_ms":    membership.GetJoinedAtMs(),
		"ended_at_ms":     membership.GetEndedAtMs(),
	}
}
