package appuser

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/userclient"
	userv1 "hyapp.local/api/proto/user/v1"
)

const maxContentBlacklistIdentifierRunes = 64

// AppUserContentBlacklistEntry 是管理端当前态读模型。
// userId 始终按字符串输出，避免 19 位 Snowflake ID 穿过 JavaScript Number 后失真。
type AppUserContentBlacklistEntry struct {
	Target          AppUserBrief `json:"target"`
	UserStatus      string       `json:"userStatus"`
	OperatorAdminID string       `json:"operatorAdminId"`
	Reason          string       `json:"reason"`
	CreatedAtMs     int64        `json:"createdAtMs"`
	UpdatedAtMs     int64        `json:"updatedAtMs"`
}

type AppUserContentBlacklistCandidate struct {
	Target      AppUserBrief `json:"target"`
	UserStatus  string       `json:"userStatus"`
	Blacklisted bool         `json:"blacklisted"`
}

type contentBlacklistMutationRequest struct {
	UserIdentifier string `json:"user_id"`
	Reason         string `json:"reason"`
}

// ListUserContentBlacklist 通过 owner service 分页读取，不允许 admin 用直连 userDB 的方式复制治理事实。
func (s *Service) ListUserContentBlacklist(ctx context.Context, page int, pageSize int, requestID string) ([]AppUserContentBlacklistEntry, int64, error) {
	if s.contentBlacklistClient == nil {
		return nil, 0, fmt.Errorf("user content blacklist client is not configured")
	}
	if page < 1 {
		page = 1
	}
	if pageSize <= 0 {
		pageSize = 50
	}
	if pageSize > 200 {
		pageSize = 200
	}
	response, err := s.contentBlacklistClient.ListUserContentBlacklist(ctx, &userv1.ListUserContentBlacklistRequest{
		Meta:     contentBlacklistRequestMeta(ctx, requestID),
		Page:     int32(page),
		PageSize: int32(pageSize),
	})
	if err != nil {
		return nil, 0, err
	}
	items := make([]AppUserContentBlacklistEntry, 0, len(response.GetItems()))
	for _, item := range response.GetItems() {
		if item == nil {
			continue
		}
		items = append(items, appUserContentBlacklistEntryFromProto(item))
	}
	return items, response.GetTotal(), nil
}

// ResolveUserContentBlacklistCandidate 复用 user-service 的后台通用 ID 解析：
// 永久默认短号、当前有效展示号、靓号和内部长 user_id 都收敛到同一个真实 user_id。
func (s *Service) ResolveUserContentBlacklistCandidate(ctx context.Context, identifier string, requestID string) (AppUserContentBlacklistCandidate, error) {
	identifier = strings.TrimSpace(identifier)
	if identifier == "" || len([]rune(identifier)) > maxContentBlacklistIdentifierRunes {
		return AppUserContentBlacklistCandidate{}, fmt.Errorf("%w: 用户 ID 参数不正确", ErrInvalidArgument)
	}
	if s.contentBlacklistClient == nil {
		return AppUserContentBlacklistCandidate{}, fmt.Errorf("user content blacklist client is not configured")
	}
	identity, err := s.contentBlacklistClient.ResolveAdminUserIdentifier(ctx, userclient.ResolveAdminUserIdentifierRequest{
		RequestID:      requestID,
		Caller:         "hyapp-admin-server",
		UserIdentifier: identifier,
	})
	if err != nil {
		return AppUserContentBlacklistCandidate{}, err
	}
	if identity == nil || identity.UserID <= 0 {
		return AppUserContentBlacklistCandidate{}, ErrNotFound
	}
	user, err := s.userClient.GetUser(ctx, userclient.GetUserRequest{
		RequestID: requestID,
		Caller:    "hyapp-admin-server",
		UserID:    identity.UserID,
	})
	if err != nil {
		return AppUserContentBlacklistCandidate{}, err
	}
	if user == nil {
		return AppUserContentBlacklistCandidate{}, ErrNotFound
	}
	return AppUserContentBlacklistCandidate{
		Target: AppUserBrief{
			UserID:        strconv.FormatInt(user.UserID, 10),
			DisplayUserID: user.DisplayUserID,
			Username:      user.Username,
			Avatar:        user.Avatar,
		},
		UserStatus:  user.Status,
		Blacklisted: user.ContentBlacklisted,
	}, nil
}

// AddUserContentBlacklist 先用通用 ID 契约解析真实用户，再仅把真实 user_id 发送给治理 RPC。
// 这样前端输入的短号即使以后过期，也不会成为黑名单持久键。
func (s *Service) AddUserContentBlacklist(ctx context.Context, identifier string, operatorAdminID int64, reason string, requestID string) (AppUserContentBlacklistEntry, error) {
	if operatorAdminID <= 0 {
		return AppUserContentBlacklistEntry{}, fmt.Errorf("%w: 操作管理员参数不正确", ErrInvalidArgument)
	}
	candidate, err := s.ResolveUserContentBlacklistCandidate(ctx, identifier, requestID)
	if err != nil {
		return AppUserContentBlacklistEntry{}, err
	}
	userID, err := strconv.ParseInt(candidate.Target.UserID, 10, 64)
	if err != nil || userID <= 0 {
		return AppUserContentBlacklistEntry{}, fmt.Errorf("%w: 用户 ID 参数不正确", ErrInvalidArgument)
	}
	reason = strings.TrimSpace(reason)
	if reason == "" {
		reason = "admin_content_blacklist"
	}
	response, err := s.contentBlacklistClient.AdminAddUserContentBlacklist(ctx, &userv1.AdminAddUserContentBlacklistRequest{
		Meta:            contentBlacklistRequestMeta(ctx, requestID),
		UserId:          userID,
		OperatorAdminId: operatorAdminID,
		Reason:          reason,
	})
	if err != nil {
		return AppUserContentBlacklistEntry{}, err
	}
	if response.GetItem() == nil {
		return AppUserContentBlacklistEntry{}, fmt.Errorf("user-service returned empty content blacklist entry")
	}
	return appUserContentBlacklistEntryFromProto(response.GetItem()), nil
}

// RemoveUserContentBlacklist 以内部真实 user_id 移除当前态；重复移除由 owner service 返回 removed=false，
// handler 仍按幂等成功处理，避免管理员因网络重试得到伪失败。
func (s *Service) RemoveUserContentBlacklist(ctx context.Context, userID int64, operatorAdminID int64, reason string, requestID string) (bool, error) {
	if s.contentBlacklistClient == nil {
		return false, fmt.Errorf("user content blacklist client is not configured")
	}
	if userID <= 0 || operatorAdminID <= 0 {
		return false, fmt.Errorf("%w: 黑名单移除参数不正确", ErrInvalidArgument)
	}
	reason = strings.TrimSpace(reason)
	if reason == "" {
		reason = "admin_remove_content_blacklist"
	}
	response, err := s.contentBlacklistClient.AdminRemoveUserContentBlacklist(ctx, &userv1.AdminRemoveUserContentBlacklistRequest{
		Meta:            contentBlacklistRequestMeta(ctx, requestID),
		UserId:          userID,
		OperatorAdminId: operatorAdminID,
		Reason:          reason,
	})
	if err != nil {
		return false, err
	}
	return response.GetRemoved(), nil
}

func contentBlacklistRequestMeta(ctx context.Context, requestID string) *userv1.RequestMeta {
	return &userv1.RequestMeta{
		RequestId: strings.TrimSpace(requestID),
		Caller:    "hyapp-admin-server",
		SentAtMs:  time.Now().UTC().UnixMilli(),
		AppCode:   appctx.FromContext(ctx),
	}
}

func appUserContentBlacklistEntryFromProto(item *userv1.UserContentBlacklistEntry) AppUserContentBlacklistEntry {
	return AppUserContentBlacklistEntry{
		Target: AppUserBrief{
			UserID:        strconv.FormatInt(item.GetUserId(), 10),
			DisplayUserID: item.GetDisplayUserId(),
			Username:      item.GetUsername(),
			Avatar:        item.GetAvatar(),
		},
		UserStatus:      contentBlacklistUserStatus(item.GetUserStatus()),
		OperatorAdminID: strconv.FormatInt(item.GetOperatorAdminId(), 10),
		Reason:          item.GetReason(),
		CreatedAtMs:     item.GetCreatedAtMs(),
		UpdatedAtMs:     item.GetUpdatedAtMs(),
	}
}

func contentBlacklistUserStatus(status userv1.UserStatus) string {
	switch status {
	case userv1.UserStatus_USER_STATUS_ACTIVE:
		return "active"
	case userv1.UserStatus_USER_STATUS_DISABLED:
		return "disabled"
	case userv1.UserStatus_USER_STATUS_BANNED:
		return "banned"
	default:
		return ""
	}
}
