package user

import (
	"context"
	"strings"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	userdomain "hyapp/services/user-service/internal/domain/user"
)

const (
	AdminUserBanStatusActive   = "active"
	AdminUserBanStatusReleased = "released"
	AdminUserBanStatusExpired  = "expired"

	adminUserBanReasonPrefix = "admin_user_ban:"
)

// AdminUserBanCommand 是后台管理员永久或限时封禁命令；ExpiresAtMs=0 明确表示永久。
type AdminUserBanCommand struct {
	CommandID       string
	TargetUserID    int64
	ExpiresAtMs     int64
	OperatorAdminID int64
	Reason          string
	RequestID       string
	NowMs           int64
}

// ReleaseAdminUserBanCommand 只用于单条管理员封禁的自动到期和底层兼容释放。
// 后台人工解封必须走 AdminForceUnbanCommand，保证经理封禁和重叠管理员封禁在同一事务内一起释放。
type ReleaseAdminUserBanCommand struct {
	BanID           string
	TargetUserID    int64
	OperatorAdminID int64
	Status          string
	Reason          string
	RequestID       string
	NowMs           int64
}

// AdminForceUnbanCommand 是后台管理员覆盖全部用户治理封禁来源的命令。
// RequestID 是跨 gRPC 重试的幂等边界，避免一次超时重试误释放原操作完成后新产生的封禁。
type AdminForceUnbanCommand struct {
	TargetUserID    int64
	OperatorAdminID int64
	Reason          string
	RequestID       string
	NowMs           int64
}

// AdminForceUnbanResult 返回本次命令实际释放的事实数量和最终用户主状态。
// StatusRestored 只描述首次执行时是否发生 banned/disabled -> active，不因幂等重试重复写状态日志。
type AdminForceUnbanResult struct {
	User                      userdomain.User
	ReleasedAdminBanCount     int64
	ReleasedManagerBlockCount int64
	StatusRestored            bool
}

// AdminUserBan 是后台管理员封禁事实；TargetOldStatus 用于审计，不作为自动恢复的唯一判断依据。
type AdminUserBan struct {
	BanID             string
	CommandID         string
	TargetUserID      int64
	TargetOldStatus   userdomain.Status
	ExpiresAtMs       int64
	Status            string
	Reason            string
	OperatorAdminID   int64
	CreatedAtMs       int64
	UpdatedAtMs       int64
	ReleasedByAdminID int64
	ReleasedReason    string
	ReleasedAtMs      int64
}

// AdminUserBanPersistenceResult 表达创建封禁和账号准入状态在同一事务提交后的事实。
type AdminUserBanPersistenceResult struct {
	Ban           AdminUserBan
	Status        UserStatusPersistenceResult
	StatusChanged bool
}

// AdminUserBanRelease 表达一条管理员封禁释放后的用户状态；仍有其他来源时 Restored=false。
type AdminUserBanRelease struct {
	Ban      AdminUserBan
	User     userdomain.User
	Restored bool
}

// AdminBanUser 创建永久或限时管理员封禁。
// 已被其他管理员/经理封禁的用户允许叠加新事实，但不会重复吊销 session 或重复驱逐实时连接。
func (s *Service) AdminBanUser(ctx context.Context, command AdminUserBanCommand) (AdminUserBan, UserStatusResult, error) {
	if s.moderationRepository == nil {
		return AdminUserBan{}, UserStatusResult{}, xerr.New(xerr.Unavailable, "moderation repository is not configured")
	}
	command.CommandID = strings.TrimSpace(command.CommandID)
	command.Reason = strings.TrimSpace(command.Reason)
	if command.CommandID == "" || command.TargetUserID <= 0 || command.OperatorAdminID <= 0 || command.ExpiresAtMs < 0 {
		return AdminUserBan{}, UserStatusResult{}, xerr.New(xerr.InvalidArgument, "admin ban command is incomplete")
	}
	// 封禁期限由 user-service owner 时钟判定；调用方 sent_at_ms 仅是链路元数据，延迟或伪造值不能创建已过期事实。
	command.NowMs = s.now().UTC().UnixMilli()
	if command.ExpiresAtMs > 0 && command.ExpiresAtMs <= command.NowMs {
		return AdminUserBan{}, UserStatusResult{}, xerr.New(xerr.InvalidArgument, "expires_at_ms must be in the future or zero")
	}
	if command.Reason == "" {
		command.Reason = "admin_user_ban"
	}
	if command.RequestID == "" {
		command.RequestID = command.CommandID
	}

	persisted, err := s.moderationRepository.CreateAdminUserBan(ctx, command)
	if err != nil {
		return AdminUserBan{}, UserStatusResult{}, err
	}
	statusCommand := UserStatusCommand{
		AppCode:        appcode.FromContext(ctx),
		TargetUserID:   command.TargetUserID,
		Status:         userdomain.StatusBanned,
		OperatorType:   OperatorTypeAdmin,
		OperatorUserID: command.OperatorAdminID,
		Reason:         adminUserBanReasonPrefix + persisted.Ban.BanID,
		RequestID:      command.RequestID,
		NowMs:          command.NowMs,
	}
	return persisted.Ban, s.statusResultAfterPersistence(ctx, statusCommand, persisted.Status, persisted.StatusChanged), nil
}

// AdminUnbanUser 是后台人工强制解封入口。
// repository 原子释放全部管理员/经理封禁，并覆盖没有独立事实表记录的 legacy banned/disabled 状态。
func (s *Service) AdminUnbanUser(ctx context.Context, command AdminForceUnbanCommand) (AdminForceUnbanResult, UserStatusResult, error) {
	if s.moderationRepository == nil {
		return AdminForceUnbanResult{}, UserStatusResult{}, xerr.New(xerr.Unavailable, "moderation repository is not configured")
	}
	command.Reason = strings.TrimSpace(command.Reason)
	if command.TargetUserID <= 0 || command.OperatorAdminID <= 0 {
		return AdminForceUnbanResult{}, UserStatusResult{}, xerr.New(xerr.InvalidArgument, "admin unban command is incomplete")
	}
	// 解封审计和状态恢复统一使用 owner 时钟，避免跨服务时钟偏差污染事实顺序。
	command.NowMs = s.now().UTC().UnixMilli()
	command.RequestID = strings.TrimSpace(command.RequestID)
	if command.RequestID == "" {
		return AdminForceUnbanResult{}, UserStatusResult{}, xerr.New(xerr.InvalidArgument, "admin unban request_id is required")
	}
	if command.Reason == "" {
		command.Reason = "admin_force_unban"
	}
	// release_reason 还会附带操作类型和管理员 ID，给前缀预留空间，避免多字节原因触发 MySQL 截断。
	if len(command.Reason) > 200 {
		return AdminForceUnbanResult{}, UserStatusResult{}, xerr.New(xerr.InvalidArgument, "admin unban reason is too long")
	}

	release, err := s.moderationRepository.ForceReleaseUserBans(ctx, command)
	if err != nil {
		return AdminForceUnbanResult{}, UserStatusResult{}, err
	}
	return release, UserStatusResult{User: release.User, AccessTokenRevoked: true}, nil
}

// ExpireAdminUserBans 处理一批已到期的限时管理员封禁；永久封禁的 expires_at_ms=0 永远不会被 claim。
func (s *Service) ExpireAdminUserBans(ctx context.Context, limit int32) (int, error) {
	if s.moderationRepository == nil {
		return 0, xerr.New(xerr.Unavailable, "moderation repository is not configured")
	}
	if limit <= 0 || limit > 500 {
		limit = 100
	}
	releases, err := s.moderationRepository.ExpireAdminUserBans(ctx, s.now().UTC().UnixMilli(), limit)
	if err != nil {
		return 0, err
	}
	return len(releases), nil
}
