package wallet

import (
	"context"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
	resourcedomain "hyapp/services/wallet-service/internal/domain/resource"
	"strings"
)

// ListVipPackages 返回同一配置快照下的最终 VIP 状态和可购买套餐。
func (s *Service) ListVipPackages(ctx context.Context, userID int64) (ledger.VipState, []ledger.VipLevel, error) {
	if userID <= 0 {
		return ledger.VipState{}, nil, xerr.New(xerr.InvalidArgument, "user_id is required")
	}
	if s.repository == nil {
		return ledger.VipState{}, nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
	return s.repository.ListVipPackages(ctx, userID)
}

// GetMyVip 返回当前用户会员状态；过期会员会按 active=false 投影。
func (s *Service) GetMyVip(ctx context.Context, userID int64) (ledger.UserVip, error) {
	if userID <= 0 {
		return ledger.UserVip{}, xerr.New(xerr.InvalidArgument, "user_id is required")
	}
	if s.repository == nil {
		return ledger.UserVip{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
	return s.repository.GetMyVip(ctx, userID)
}

// GetVipProgramConfig 返回当前 App 的完整 VIP 规则和权益目录；不存在配置时直接失败，
// 避免业务在缺少迁移或错误 app_code 时悄悄回退到另一套体系。
func (s *Service) GetVipProgramConfig(ctx context.Context) (ledger.VipProgramConfig, []ledger.VipBenefit, error) {
	if s.repository == nil {
		return ledger.VipProgramConfig{}, nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
	return s.repository.GetVipProgramConfig(ctx)
}

// GetVipState 统一返回付费、体验卡和最终生效状态，客户端与 owner service 都不能自行按等级合并。
func (s *Service) GetVipState(ctx context.Context, userID int64) (ledger.VipState, error) {
	if userID <= 0 {
		return ledger.VipState{}, xerr.New(xerr.InvalidArgument, "user_id is required")
	}
	if s.repository == nil {
		return ledger.VipState{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
	return s.repository.GetVipState(ctx, userID)
}

// BatchGetUserEffectiveVipLevels 为展示聚合提供 wallet effective VIP 的批量 owner 读。
// nowMS 只由 wallet-service 注入，调用方不能通过自定义时间读取未来或历史权益。
func (s *Service) BatchGetUserEffectiveVipLevels(ctx context.Context, userIDs []int64) ([]ledger.UserEffectiveVipLevel, int64, error) {
	if len(userIDs) == 0 {
		return []ledger.UserEffectiveVipLevel{}, s.now().UTC().UnixMilli(), nil
	}
	if len(userIDs) > 100 {
		return nil, 0, xerr.New(xerr.InvalidArgument, "effective vip batch is too large")
	}
	for _, userID := range userIDs {
		if userID <= 0 {
			return nil, 0, xerr.New(xerr.InvalidArgument, "user_ids contains invalid value")
		}
	}
	if s.repository == nil {
		return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
	evaluatedAtMS := s.now().UTC().UnixMilli()
	items, err := s.repository.BatchGetUserEffectiveVipLevels(ctx, userIDs, evaluatedAtMS)
	return items, evaluatedAtMS, err
}

// CheckVipBenefit 是房间、用户资料和通知 owner 执行单项权益时的服务端判定入口。
func (s *Service) CheckVipBenefit(ctx context.Context, userID int64, benefitCode string) (ledger.CheckVipBenefitResult, error) {
	benefitCode = strings.ToLower(strings.TrimSpace(benefitCode))
	if userID <= 0 || benefitCode == "" {
		return ledger.CheckVipBenefitResult{}, xerr.New(xerr.InvalidArgument, "user_id and benefit_code are required")
	}
	if s.repository == nil {
		return ledger.CheckVipBenefitResult{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
	return s.repository.CheckVipBenefit(ctx, userID, benefitCode)
}

// CheckVipResourceBenefit 是装扮资源的 owner 判定入口。它把具体 resource_id 纳入校验，
// 因此 room-service 和客户端都无需复制 VIP 等级继承、体验卡排除或资源目录有效性规则。
func (s *Service) CheckVipResourceBenefit(ctx context.Context, userID int64, benefitCode string, resourceID int64, resourceType string) (ledger.CheckVipBenefitResult, error) {
	benefitCode = strings.ToLower(strings.TrimSpace(benefitCode))
	resourceType = resourcedomain.NormalizeResourceType(resourceType)
	if userID <= 0 || benefitCode == "" || resourceID <= 0 || resourceType == "" {
		return ledger.CheckVipBenefitResult{}, xerr.New(xerr.InvalidArgument, "vip resource benefit request is incomplete")
	}
	if s.repository == nil {
		return ledger.CheckVipBenefitResult{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
	return s.repository.CheckVipResourceBenefit(ctx, userID, benefitCode, resourceID, resourceType)
}

// PurchaseVip 执行 VIP 购买、升级或续期；降级由 repository 在锁内按当前会员状态拒绝。
func (s *Service) PurchaseVip(ctx context.Context, command ledger.PurchaseVipCommand) (ledger.PurchaseVipReceipt, error) {
	command.CommandID = strings.TrimSpace(command.CommandID)
	if command.CommandID == "" || command.UserID <= 0 || command.Level <= 0 {
		return ledger.PurchaseVipReceipt{}, xerr.New(xerr.InvalidArgument, "vip purchase command is incomplete")
	}
	if len(command.CommandID) > 128 {
		return ledger.PurchaseVipReceipt{}, xerr.New(xerr.InvalidArgument, "command_id is too long")
	}
	if s.repository == nil {
		return ledger.PurchaseVipReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	ctx = appcode.WithContext(ctx, command.AppCode)
	return s.repository.PurchaseVip(ctx, command)
}

// GrantVip 是活动和后台赠送 VIP 的统一入口；购买仍走 PurchaseVip 完成扣费。
func (s *Service) GrantVip(ctx context.Context, command ledger.GrantVipCommand) (ledger.GrantVipReceipt, error) {
	command.CommandID = strings.TrimSpace(command.CommandID)
	if command.CommandID == "" || command.TargetUserID <= 0 || command.Level <= 0 {
		return ledger.GrantVipReceipt{}, xerr.New(xerr.InvalidArgument, "vip grant command is incomplete")
	}
	if len(command.CommandID) > 128 {
		return ledger.GrantVipReceipt{}, xerr.New(xerr.InvalidArgument, "command_id is too long")
	}
	command.GrantSource = ledger.NormalizeVipGrantSource(command.GrantSource)
	if command.GrantSource == ledger.VipGrantSourceManagerCenter {
		// 外管赠送只能生成可自主佩戴的体验卡，禁止内部调用方绕过 Portal
		// 直接改变用户当前 VIP 会员状态。
		return ledger.GrantVipReceipt{}, xerr.New(xerr.PermissionDenied, "manager center must grant a vip trial card")
	}
	switch command.GrantSource {
	case ledger.VipGrantSourceAdmin:
		if command.OperatorUserID <= 0 {
			return ledger.GrantVipReceipt{}, xerr.New(xerr.InvalidArgument, "operator_user_id is required")
		}
	case ledger.VipGrantSourceActivity:
		if command.OperatorUserID < 0 {
			return ledger.GrantVipReceipt{}, xerr.New(xerr.InvalidArgument, "operator_user_id is invalid")
		}
	default:
		return ledger.GrantVipReceipt{}, xerr.New(xerr.InvalidArgument, "vip grant_source is invalid")
	}
	command.Reason = strings.TrimSpace(command.Reason)
	if command.Reason == "" {
		command.Reason = command.GrantSource
	}
	if len(command.Reason) > 256 {
		return ledger.GrantVipReceipt{}, xerr.New(xerr.InvalidArgument, "reason is too long")
	}
	if s.repository == nil {
		return ledger.GrantVipReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	ctx = appcode.WithContext(ctx, command.AppCode)
	return s.repository.GrantVip(ctx, command)
}

// GrantVipTrialCard 发放独立绝对计时的背包卡片；发卡不自动佩戴，避免后台操作改变用户当前展示。
func (s *Service) GrantVipTrialCard(ctx context.Context, command ledger.GrantVipTrialCardCommand) (ledger.GrantVipTrialCardReceipt, error) {
	command.CommandID = strings.TrimSpace(command.CommandID)
	command.Reason = strings.TrimSpace(command.Reason)
	command.GrantSource = ledger.NormalizeVipGrantSource(command.GrantSource)
	if command.CommandID == "" || command.TargetUserID <= 0 || command.Level <= 0 || command.DurationMS <= 0 {
		return ledger.GrantVipTrialCardReceipt{}, xerr.New(xerr.InvalidArgument, "vip trial card command is incomplete")
	}
	if len(command.CommandID) > 128 || len(command.Reason) > 256 || command.ResourceID < 0 {
		return ledger.GrantVipTrialCardReceipt{}, xerr.New(xerr.InvalidArgument, "vip trial card command is invalid")
	}
	switch command.GrantSource {
	case ledger.VipGrantSourceAdmin, ledger.VipGrantSourceManagerCenter:
		if command.OperatorUserID <= 0 {
			return ledger.GrantVipTrialCardReceipt{}, xerr.New(xerr.InvalidArgument, "operator_user_id is required")
		}
	case ledger.VipGrantSourceActivity:
		if command.OperatorUserID < 0 {
			return ledger.GrantVipTrialCardReceipt{}, xerr.New(xerr.InvalidArgument, "operator_user_id is invalid")
		}
	default:
		return ledger.GrantVipTrialCardReceipt{}, xerr.New(xerr.InvalidArgument, "vip grant_source is invalid")
	}
	if command.GrantSource == ledger.VipGrantSourceManagerCenter && command.Level > 6 {
		// 外管只能发 VIP1..VIP6 体验卡。限制放在账务 owner 内再次执行，
		// 防止未来新增入口绕过 admin-server 的页面和 handler 校验。
		return ledger.GrantVipTrialCardReceipt{}, xerr.New(xerr.PermissionDenied, "manager center can grant at most VIP6 trial card")
	}
	if command.Reason == "" {
		command.Reason = command.GrantSource
	}
	if s.repository == nil {
		return ledger.GrantVipTrialCardReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	ctx = appcode.WithContext(ctx, command.AppCode)
	return s.repository.GrantVipTrialCard(ctx, command)
}

// EquipVipTrialCard 只切换通用装备指针，任一卡片的 effective/expires 时间都不会变化；
// repository 会在同一事务内拒绝低于当前 paid/trial 最终等级的体验卡。
func (s *Service) EquipVipTrialCard(ctx context.Context, command ledger.EquipVipTrialCardCommand) (ledger.VipTrialCard, ledger.VipState, error) {
	command.RequestID = strings.TrimSpace(command.RequestID)
	command.EquipmentCommandID = strings.TrimSpace(command.EquipmentCommandID)
	command.EntitlementID = strings.TrimSpace(command.EntitlementID)
	if command.RequestID == "" || command.UserID <= 0 || command.EntitlementID == "" {
		return ledger.VipTrialCard{}, ledger.VipState{}, xerr.New(xerr.InvalidArgument, "vip trial equip command is incomplete")
	}
	if len(command.RequestID) > 128 || len(command.EntitlementID) > 96 {
		return ledger.VipTrialCard{}, ledger.VipState{}, xerr.New(xerr.InvalidArgument, "vip trial equip command is invalid")
	}
	if command.EquipmentCommandID != "" && (len(command.EquipmentCommandID) > 128 || command.ResourceID <= 0) {
		return ledger.VipTrialCard{}, ledger.VipState{}, xerr.New(xerr.InvalidArgument, "resource equip command is invalid")
	}
	if s.repository == nil {
		return ledger.VipTrialCard{}, ledger.VipState{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	ctx = appcode.WithContext(ctx, command.AppCode)
	return s.repository.EquipVipTrialCard(ctx, command)
}

// UnequipVipTrialCard 清理当前体验卡装备记录，最终状态立即回落到仍有效的付费会员。
func (s *Service) UnequipVipTrialCard(ctx context.Context, command ledger.UnequipVipTrialCardCommand) (bool, ledger.VipState, error) {
	command.RequestID = strings.TrimSpace(command.RequestID)
	if command.RequestID == "" || command.UserID <= 0 {
		return false, ledger.VipState{}, xerr.New(xerr.InvalidArgument, "vip trial unequip command is incomplete")
	}
	if len(command.RequestID) > 128 {
		return false, ledger.VipState{}, xerr.New(xerr.InvalidArgument, "request_id is too long")
	}
	if s.repository == nil {
		return false, ledger.VipState{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	ctx = appcode.WithContext(ctx, command.AppCode)
	return s.repository.UnequipVipTrialCard(ctx, command)
}

// ListAdminVipLevels 返回后台 VIP program 与动态等级的同一数据库快照。
func (s *Service) ListAdminVipLevels(ctx context.Context) (ledger.VipProgramConfig, []ledger.VipLevel, error) {
	if s.repository == nil {
		return ledger.VipProgramConfig{}, nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
	return s.repository.ListAdminVipLevels(ctx)
}

// UpdateAdminVipLevels 保存后台 VIP 等级配置；完整校验下沉到 repository，保证和购买事务一致。
func (s *Service) UpdateAdminVipLevels(ctx context.Context, levels []ledger.AdminVipLevelCommand, operatorUserID int64) (ledger.VipProgramConfig, []ledger.VipLevel, error) {
	if operatorUserID <= 0 {
		return ledger.VipProgramConfig{}, nil, xerr.New(xerr.InvalidArgument, "operator_user_id is required")
	}
	if s.repository == nil {
		return ledger.VipProgramConfig{}, nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	normalized := make([]ledger.AdminVipLevelCommand, 0, len(levels))
	for _, level := range levels {
		level.Name = strings.TrimSpace(level.Name)
		level.Status = strings.ToLower(strings.TrimSpace(level.Status))
		normalized = append(normalized, level)
	}
	ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
	return s.repository.UpdateAdminVipLevels(ctx, normalized, operatorUserID)
}

// UpdateAdminVipProgramConfig 保存 App 级状态机配置；版本递增与字段白名单由 repository 在锁内处理。
func (s *Service) UpdateAdminVipProgramConfig(ctx context.Context, command ledger.AdminVipProgramConfigCommand) (ledger.VipProgramConfig, error) {
	if command.OperatorUserID <= 0 {
		return ledger.VipProgramConfig{}, xerr.New(xerr.InvalidArgument, "operator_user_id is required")
	}
	if s.repository == nil {
		return ledger.VipProgramConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.Config.AppCode = appcode.Normalize(command.Config.AppCode)
	ctx = appcode.WithContext(ctx, command.Config.AppCode)
	return s.repository.UpdateAdminVipProgramConfig(ctx, command)
}

// UpdateVipUserSettings 保存用户可关闭的 VIP 展示与隐私偏好。用户当前没有 VIP 也允许保存，
// 但偏好不会授予权益；实际执行仍由 CheckVipBenefit 同时校验 effective benefit。
func (s *Service) UpdateVipUserSettings(ctx context.Context, command ledger.UpdateVipUserSettingsCommand) (ledger.VipUserSettings, error) {
	if command.UserID <= 0 || (command.RoomEntryNoticeEnabled == nil && command.OnlineGlobalNoticeEnabled == nil &&
		command.HideProfileDataEnabled == nil && command.AnonymousProfileVisitEnabled == nil &&
		command.LeaderboardInvisibleEnabled == nil) {
		return ledger.VipUserSettings{}, xerr.New(xerr.InvalidArgument, "vip user settings update is incomplete")
	}
	if s.repository == nil {
		return ledger.VipUserSettings{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	ctx = appcode.WithContext(ctx, command.AppCode)
	return s.repository.UpdateVipUserSettings(ctx, command)
}
