package externaladmin

import (
	"errors"
	"fmt"
	"strconv"
	"strings"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/userclient"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/response"

	"github.com/gin-gonic/gin"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type externalInviteRequest struct {
	TargetUserID   FlexibleString `json:"targetUserId" binding:"required"`
	ParentBDUserID FlexibleString `json:"parentBdUserId"`
	AgencyID       FlexibleString `json:"agencyId"`
	AgencyName     string         `json:"agencyName"`
}

func (h *Handler) ListExternalTeamUsers(client userclient.ExternalTeamClient, fixedRole string) gin.HandlerFunc {
	return func(c *gin.Context) {
		scope, ok := CurrentScope(c)
		if !ok || client == nil {
			response.ServerError(c, "团队服务暂不可用")
			return
		}
		role := strings.ToLower(strings.TrimSpace(fixedRole))
		if role == "" {
			role = strings.ToLower(strings.TrimSpace(c.Query("role")))
		}
		if !externalTeamRoleAllowed(c, role) {
			response.Forbidden(c, "没有该团队列表权限")
			return
		}
		page := positiveQueryInt(c.Query("page"), 1)
		pageSize := positiveQueryInt(c.Query("pageSize"), 20)
		if pageSize > 100 {
			pageSize = 100
		}
		result, err := client.ListExternalTeamUsers(c.Request.Context(), userclient.ListExternalTeamUsersRequest{
			RequestID: middleware.CurrentRequestID(c), Caller: "hyapp-admin-external",
			ScopeOwnerUserIDs: scope.OwnerUserIDs, RegionID: scope.RegionID, Role: role,
			Status:  strings.ToLower(strings.TrimSpace(c.DefaultQuery("status", "active"))),
			Keyword: strings.TrimSpace(c.Query("keyword")), Page: page, PageSize: pageSize,
		})
		if err != nil {
			writeExternalOwnerError(c, err, "获取团队用户失败")
			return
		}
		if result == nil {
			response.ServerError(c, "团队服务返回空结果")
			return
		}
		response.OK(c, response.Page{Items: result.Users, Page: result.Page, PageSize: result.PageSize, Total: result.Total})
	}
}

func (h *Handler) SearchExternalInvitationCandidate(client userclient.ExternalTeamClient) gin.HandlerFunc {
	return func(c *gin.Context) {
		scope, ok := CurrentScope(c)
		if !ok || client == nil {
			response.ServerError(c, "邀请服务暂不可用")
			return
		}
		invitationType := strings.ToLower(strings.TrimSpace(c.Query("invitationType")))
		if !externalInvitePermissionAllowed(c, invitationType) {
			response.Forbidden(c, "没有该邀请权限")
			return
		}
		item, err := client.SearchExternalInvitationCandidate(c.Request.Context(), userclient.SearchExternalInvitationCandidateRequest{
			RequestID: middleware.CurrentRequestID(c), Caller: "hyapp-admin-external",
			OperatorUserID: scope.LinkedAppUserID, ExpectedRegionID: scope.RegionID,
			InvitationType: invitationType, Keyword: strings.TrimSpace(c.Query("keyword")),
		})
		if err != nil {
			writeExternalOwnerError(c, err, "未找到符合条件的同区域用户")
			return
		}
		response.OK(c, item)
	}
}

func (h *Handler) CreateExternalBDInvitation(client userclient.ExternalTeamClient) gin.HandlerFunc {
	return h.createExternalInvitation(client, "bd")
}

func (h *Handler) CreateExternalAgencyInvitation(client userclient.ExternalTeamClient) gin.HandlerFunc {
	return h.createExternalInvitation(client, "agency")
}

func (h *Handler) CreateExternalHostInvitation(client userclient.ExternalTeamClient) gin.HandlerFunc {
	return h.createExternalInvitation(client, "host")
}

func (h *Handler) createExternalInvitation(client userclient.ExternalTeamClient, invitationType string) gin.HandlerFunc {
	return func(c *gin.Context) {
		scope, ok := CurrentScope(c)
		if !ok || client == nil {
			response.ServerError(c, "邀请服务暂不可用")
			return
		}
		var request externalInviteRequest
		if err := c.ShouldBindJSON(&request); err != nil {
			response.BadRequest(c, "邀请参数不正确")
			return
		}
		targetUserID, err := positiveFlexibleID(request.TargetUserID)
		if err != nil {
			response.BadRequest(c, "目标用户 ID 不正确")
			return
		}
		requestID := middleware.CurrentRequestID(c)
		commandID := fmt.Sprintf("external-%s:%s:%d", invitationType, requestID, targetUserID)
		var invitation *userclient.ExternalRoleInvitation
		switch invitationType {
		case "bd":
			invitation, err = client.ExternalInviteBD(c.Request.Context(), userclient.ExternalInviteBDRequest{
				RequestID: requestID, Caller: "hyapp-admin-external", CommandID: commandID,
				OperatorUserID: scope.LinkedAppUserID, TargetUserID: targetUserID, ExpectedRegionID: scope.RegionID,
			})
		case "agency":
			parentBDUserID, parseErr := positiveFlexibleID(request.ParentBDUserID)
			if parseErr != nil || strings.TrimSpace(request.AgencyName) == "" {
				response.BadRequest(c, "请选择团队 BD 并填写 Agency 名称")
				return
			}
			invitation, err = client.ExternalInviteAgency(c.Request.Context(), userclient.ExternalInviteAgencyRequest{
				RequestID: requestID, Caller: "hyapp-admin-external", CommandID: commandID,
				OperatorUserID: scope.LinkedAppUserID, ParentBDUserID: parentBDUserID, TargetUserID: targetUserID,
				AgencyName: strings.TrimSpace(request.AgencyName), ScopeOwnerUserIDs: scope.OwnerUserIDs, ExpectedRegionID: scope.RegionID,
			})
		case "host":
			agencyID, parseErr := positiveFlexibleID(request.AgencyID)
			if parseErr != nil {
				response.BadRequest(c, "请选择团队 Agency")
				return
			}
			invitation, err = client.ExternalInviteHost(c.Request.Context(), userclient.ExternalInviteHostRequest{
				RequestID: requestID, Caller: "hyapp-admin-external", CommandID: commandID,
				OperatorUserID: scope.LinkedAppUserID, AgencyID: agencyID, TargetUserID: targetUserID,
				ScopeOwnerUserIDs: scope.OwnerUserIDs, ExpectedRegionID: scope.RegionID,
			})
		}
		if err != nil {
			writeExternalOwnerError(c, err, "创建邀请失败")
			return
		}
		response.Created(c, invitation)
	}
}

func (h *Handler) RequireExternalTeamTarget(client userclient.ExternalTeamClient, param string) gin.HandlerFunc {
	return func(c *gin.Context) {
		scope, ok := CurrentScope(c)
		targetUserID, err := strconv.ParseInt(strings.TrimSpace(c.Param(param)), 10, 64)
		if !ok || client == nil || err != nil || targetUserID <= 0 {
			response.Forbidden(c, "目标用户不在当前团队")
			c.Abort()
			return
		}
		allowed := false
		for _, userStatus := range []string{"active", "banned", "disabled"} {
			result, lookupErr := client.ListExternalTeamUsers(c.Request.Context(), userclient.ListExternalTeamUsersRequest{
				RequestID: middleware.CurrentRequestID(c), Caller: "hyapp-admin-external-target-check",
				ScopeOwnerUserIDs: scope.OwnerUserIDs, RegionID: scope.RegionID, Status: userStatus,
				Keyword: strconv.FormatInt(targetUserID, 10), Page: 1, PageSize: 10,
			})
			if lookupErr != nil {
				writeExternalOwnerError(c, lookupErr, "团队范围校验失败")
				c.Abort()
				return
			}
			for _, item := range result.Users {
				if item.UserID == targetUserID && item.RegionID == scope.RegionID {
					allowed = true
					break
				}
			}
			if allowed {
				break
			}
		}
		if !allowed {
			response.Forbidden(c, "目标用户不在当前团队或区域")
			c.Abort()
			return
		}
		c.Next()
	}
}

// HydrateExternalRoomScope resolves business team members before entering roomadmin.
// Room owners are BD/Agency/Host users, not portal Local/经理/SuperAdmin account roots.
func (h *Handler) HydrateExternalRoomScope(client userclient.ExternalTeamClient) gin.HandlerFunc {
	return func(c *gin.Context) {
		scope, ok := CurrentScope(c)
		if !ok || client == nil {
			response.ServerError(c, "团队服务暂不可用")
			c.Abort()
			return
		}
		const maxRoomScopeUsers = 5000
		seen := make(map[int64]struct{})
		teamUserIDs := make([]int64, 0)
		for _, userStatus := range []string{"active", "banned", "disabled"} {
			for page := 1; ; page++ {
				result, err := client.ListExternalTeamUsers(c.Request.Context(), userclient.ListExternalTeamUsersRequest{
					RequestID: middleware.CurrentRequestID(c), Caller: "hyapp-admin-external-room-scope",
					ScopeOwnerUserIDs: scope.OwnerUserIDs, RegionID: scope.RegionID, Status: userStatus,
					Page: page, PageSize: 200,
				})
				if err != nil {
					writeExternalOwnerError(c, err, "房间团队范围校验失败")
					c.Abort()
					return
				}
				if result == nil {
					response.ServerError(c, "团队服务返回空结果")
					c.Abort()
					return
				}
				for _, item := range result.Users {
					if item.UserID <= 0 || item.RegionID != scope.RegionID {
						continue
					}
					if _, exists := seen[item.UserID]; exists {
						continue
					}
					seen[item.UserID] = struct{}{}
					teamUserIDs = append(teamUserIDs, item.UserID)
					if len(teamUserIDs) > maxRoomScopeUsers {
						response.Forbidden(c, "当前团队规模超过房间管理上限")
						c.Abort()
						return
					}
				}
				if len(result.Users) == 0 || result.PageSize <= 0 || int64(page*result.PageSize) >= result.Total {
					break
				}
			}
		}
		requestScope := appctx.ExternalScope{
			AccountID: scope.AccountID, LinkedAppUserID: scope.LinkedAppUserID,
			IdentityType: scope.IdentityType, RegionID: scope.RegionID, OwnerUserIDs: teamUserIDs,
		}
		c.Request = c.Request.WithContext(appctx.WithExternalScope(c.Request.Context(), requestScope))
		c.Next()
	}
}

func positiveFlexibleID(value FlexibleString) (int64, error) {
	parsed, err := strconv.ParseInt(strings.TrimSpace(string(value)), 10, 64)
	if err != nil || parsed <= 0 {
		return 0, ErrInvalidInput
	}
	return parsed, nil
}

func positiveQueryInt(raw string, fallback int) int {
	value, err := strconv.Atoi(strings.TrimSpace(raw))
	if err != nil || value <= 0 {
		return fallback
	}
	return value
}

func externalTeamRoleAllowed(c *gin.Context, role string) bool {
	switch role {
	case "":
		return principalHasAnyPermission(c, "user:list", "user:update", "user:ban", "user-ban:list", "user:unban", "user-level:grant")
	case "bd":
		// Agency invitations must select an existing BD even when the account was
		// intentionally granted agency:invite without the standalone BD list menu.
		return principalHasAnyPermission(c, "bd:list", "bd:invite", "agency:invite")
	case "agency":
		// Host invitations have the same bounded parent-selector dependency on Agency.
		return principalHasAnyPermission(c, "agency:list", "agency:invite", "host:invite")
	case "host":
		return principalHasAnyPermission(c, "host:list", "host:invite")
	default:
		return false
	}
}

func externalInvitePermissionAllowed(c *gin.Context, invitationType string) bool {
	return principalHasAnyPermission(c, invitationType+":invite")
}

func principalHasAnyPermission(c *gin.Context, permissions ...string) bool {
	principal, ok := CurrentPrincipal(c)
	if !ok {
		return false
	}
	assigned := make(map[string]struct{}, len(principal.Permissions))
	for _, permission := range principal.Permissions {
		assigned[permission] = struct{}{}
	}
	for _, permission := range permissions {
		if _, exists := assigned[permission]; exists {
			return true
		}
	}
	return false
}

func writeExternalOwnerError(c *gin.Context, err error, fallback string) {
	if err == nil {
		return
	}
	code := status.Code(err)
	switch {
	case code == codes.InvalidArgument:
		response.BadRequest(c, status.Convert(err).Message())
	case code == codes.NotFound:
		response.NotFound(c, status.Convert(err).Message())
	case code == codes.PermissionDenied || code == codes.FailedPrecondition:
		response.Forbidden(c, status.Convert(err).Message())
	case errors.Is(err, ErrInvalidInput):
		response.BadRequest(c, fallback)
	default:
		response.ServerError(c, fallback)
	}
}
