package resource

import (
	"context"
	"strings"
	"time"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/model"
	"hyapp-admin-server/internal/modules/shared"
)

const (
	grantSourceAdmin         = "admin"
	grantSourceManagerCenter = "manager_center"
)

type adminOperator struct {
	UserID   uint
	Username string
	Name     string
}

type managerOperator struct {
	UserID        int64
	DisplayUserID string
	Username      string
	Avatar        string
}

func (h *Handler) enrichGrantTargets(ctx context.Context, grants []grantDTO) error {
	ids := collectGrantTargetIDs(grants)
	if len(ids) == 0 {
		return nil
	}
	users := make(map[int64]grantUserDTO, len(ids))
	if h.userDB != nil {
		var err error
		users, err = h.queryGrantTargetUsers(ctx, ids)
		if err != nil {
			return err
		}
	}
	applyGrantTargetUsers(grants, users)
	return nil
}

func (h *Handler) queryGrantTargetUsers(ctx context.Context, ids []int64) (map[int64]grantUserDTO, error) {
	out := make(map[int64]grantUserDTO, len(ids))
	if len(ids) == 0 || h.userDB == nil {
		return out, nil
	}
	nowMs := time.Now().UnixMilli()
	args := append([]any{nowMs, nowMs, appctx.FromContext(ctx)}, int64Args(ids)...)
	rows, err := h.userDB.QueryContext(ctx, `
		SELECT user_id,
		       current_display_user_id,
		       COALESCE(default_display_user_id, ''),
		       COALESCE((
		         SELECT lease.display_user_id
		         FROM pretty_display_user_id_leases lease
		         WHERE lease.app_code = users.app_code
		           AND lease.user_id = users.user_id
		           AND lease.status = 'active'
		           AND (COALESCE(lease.expires_at_ms, 0) = 0 OR lease.expires_at_ms > ?)
		         ORDER BY lease.created_at_ms DESC, lease.lease_id DESC
		         LIMIT 1
		       ), ''),
		       COALESCE((
		         SELECT pdi.pretty_id
		         FROM pretty_display_user_id_leases lease
		         JOIN pretty_display_ids pdi
		           ON pdi.app_code = lease.app_code
		          AND pdi.assigned_lease_id = lease.lease_id
		          AND pdi.status = 'assigned'
		         WHERE lease.app_code = users.app_code
		           AND lease.user_id = users.user_id
		           AND lease.status = 'active'
		           AND (COALESCE(lease.expires_at_ms, 0) = 0 OR lease.expires_at_ms > ?)
		         ORDER BY lease.created_at_ms DESC, lease.lease_id DESC
		         LIMIT 1
		       ), ''),
		       COALESCE(username, ''),
		       COALESCE(avatar, '')
		FROM users
		WHERE app_code = ? AND user_id IN (`+placeholders(len(ids))+`)
	`, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var user grantUserDTO
		if err := rows.Scan(&user.UserID, &user.DisplayUserID, &user.DefaultDisplayUserID, &user.PrettyDisplayUserID, &user.PrettyID, &user.Username, &user.Avatar); err != nil {
			return nil, err
		}
		out[user.UserID] = user
	}
	return out, rows.Err()
}

func applyGrantTargetUsers(grants []grantDTO, users map[int64]grantUserDTO) {
	for index := range grants {
		targetUserID := grants[index].TargetUserID
		if targetUserID <= 0 {
			continue
		}
		if user, ok := users[targetUserID]; ok {
			grants[index].TargetUser = &user
			continue
		}
		grants[index].TargetUser = &grantUserDTO{UserID: targetUserID}
	}
}

func (h *Handler) enrichResourceShopPurchaseUsers(ctx context.Context, orders []resourceShopPurchaseOrderDTO) error {
	ids := collectResourceShopPurchaseUserIDs(orders)
	if len(ids) == 0 {
		return nil
	}
	users := map[int64]grantUserDTO{}
	if h.userDB != nil {
		var err error
		users, err = h.queryGrantTargetUsers(ctx, ids)
		if err != nil {
			return err
		}
	}
	for index := range orders {
		userID := orders[index].UserID
		if userID <= 0 {
			continue
		}
		if user, ok := users[userID]; ok {
			orders[index].User = &user
			continue
		}
		orders[index].User = &grantUserDTO{UserID: userID}
	}
	return nil
}

func collectResourceShopPurchaseUserIDs(orders []resourceShopPurchaseOrderDTO) []int64 {
	seen := make(map[int64]struct{}, len(orders))
	ids := make([]int64, 0, len(orders))
	for _, order := range orders {
		if order.UserID <= 0 {
			continue
		}
		if _, exists := seen[order.UserID]; exists {
			continue
		}
		seen[order.UserID] = struct{}{}
		ids = append(ids, order.UserID)
	}
	return ids
}

func (h *Handler) resolveResourceShopPurchaseUserID(ctx context.Context, appCode string, keyword string) (int64, bool, error) {
	keyword = strings.TrimSpace(keyword)
	if keyword == "" {
		return 0, false, nil
	}
	if h == nil {
		return 0, false, nil
	}
	// 资源商店购买记录同样由 wallet-service 按内部 user_id 过滤；后台输入允许短号、靓号和昵称，先解析再下传。
	return shared.ResolveExactUserID(ctx, h.userDB, appCode, keyword, time.Now().UnixMilli())
}

func (h *Handler) enrichGrantOperators(ctx context.Context, grants []grantDTO) error {
	// operator_user_id 在 wallet 发放事实里只保存稳定 ID；后台列表按来源补展示资料，避免把 admin 用户和 app 用户混成同一张表。
	adminIDs, managerIDs := collectGrantOperatorIDs(grants)
	admins, err := h.queryAdminOperators(adminIDs)
	if err != nil {
		return err
	}
	managers, err := h.queryManagerOperators(ctx, managerIDs)
	if err != nil {
		return err
	}
	applyGrantOperators(grants, admins, managers)
	return nil
}

func (h *Handler) queryAdminOperators(ids []uint) (map[uint]adminOperator, error) {
	out := make(map[uint]adminOperator, len(ids))
	if len(ids) == 0 || h.store == nil {
		return out, nil
	}
	users, err := h.store.UsersByIDs(ids)
	if err != nil {
		return nil, err
	}
	for id, user := range users {
		out[id] = adminOperatorFromModel(user)
	}
	return out, nil
}

func (h *Handler) queryManagerOperators(ctx context.Context, ids []int64) (map[int64]managerOperator, error) {
	out := make(map[int64]managerOperator, len(ids))
	if len(ids) == 0 || h.userDB == nil {
		return out, nil
	}
	// 经理中心发放人是 app 用户，只读 user-service 的用户库投影；资源发放事实仍然以 wallet-service 返回为准。
	args := append([]any{appctx.FromContext(ctx)}, int64Args(ids)...)
	rows, err := h.userDB.QueryContext(ctx, `
		SELECT user_id,
		       current_display_user_id,
		       COALESCE(username, ''),
		       COALESCE(avatar, '')
		FROM users
		WHERE app_code = ? AND user_id IN (`+placeholders(len(ids))+`)
	`, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var operator managerOperator
		if err := rows.Scan(&operator.UserID, &operator.DisplayUserID, &operator.Username, &operator.Avatar); err != nil {
			return nil, err
		}
		out[operator.UserID] = operator
	}
	return out, rows.Err()
}

func applyGrantOperators(grants []grantDTO, admins map[uint]adminOperator, managers map[int64]managerOperator) {
	for index := range grants {
		source := normalizeGrantSource(grants[index].GrantSource)
		operatorID := grants[index].OperatorUserID
		if operatorID <= 0 {
			continue
		}
		// 即使资料缺失也返回 source + user_id，前端仍能展示可追溯的操作人线索。
		operator := &operatorDTO{
			Source: source,
			UserID: operatorID,
		}
		switch source {
		case grantSourceAdmin:
			if admin, ok := admins[uint(operatorID)]; ok {
				operator.Username = admin.Username
				operator.Name = admin.Name
			}
		case grantSourceManagerCenter:
			if manager, ok := managers[operatorID]; ok {
				operator.DisplayUserID = manager.DisplayUserID
				operator.Username = manager.Username
				operator.Avatar = manager.Avatar
			}
		}
		grants[index].Operator = operator
	}
}

func collectGrantOperatorIDs(grants []grantDTO) ([]uint, []int64) {
	adminSet := make(map[uint]struct{})
	managerSet := make(map[int64]struct{})
	for _, grant := range grants {
		if grant.OperatorUserID <= 0 {
			continue
		}
		switch normalizeGrantSource(grant.GrantSource) {
		case grantSourceAdmin:
			adminSet[uint(grant.OperatorUserID)] = struct{}{}
		case grantSourceManagerCenter:
			managerSet[grant.OperatorUserID] = struct{}{}
		}
	}
	adminIDs := make([]uint, 0, len(adminSet))
	for id := range adminSet {
		adminIDs = append(adminIDs, id)
	}
	managerIDs := make([]int64, 0, len(managerSet))
	for id := range managerSet {
		managerIDs = append(managerIDs, id)
	}
	return adminIDs, managerIDs
}

func collectGrantTargetIDs(grants []grantDTO) []int64 {
	targetSet := make(map[int64]struct{})
	for _, grant := range grants {
		if grant.TargetUserID <= 0 {
			continue
		}
		targetSet[grant.TargetUserID] = struct{}{}
	}
	targetIDs := make([]int64, 0, len(targetSet))
	for id := range targetSet {
		targetIDs = append(targetIDs, id)
	}
	return targetIDs
}

func adminOperatorFromModel(user model.User) adminOperator {
	return adminOperator{
		UserID:   user.ID,
		Username: user.Username,
		Name:     user.Name,
	}
}

func normalizeGrantSource(source string) string {
	return strings.ToLower(strings.TrimSpace(source))
}

func placeholders(count int) string {
	if count <= 0 {
		return ""
	}
	items := make([]string, count)
	for index := range items {
		items[index] = "?"
	}
	return strings.Join(items, ",")
}

func int64Args(values []int64) []any {
	args := make([]any, 0, len(values))
	for _, value := range values {
		args = append(args, value)
	}
	return args
}
