package grpc

import (
	"context"
	"time"

	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
)

// ListVipPackages 返回当前 VIP 和可购买包列表。
func (s *Server) ListVipPackages(ctx context.Context, req *walletv1.ListVipPackagesRequest) (*walletv1.ListVipPackagesResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	current, levels, err := s.svc.ListVipPackages(ctx, req.GetUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &walletv1.ListVipPackagesResponse{CurrentVip: vipToProto(current), Packages: make([]*walletv1.VipLevel, 0, len(levels))}
	for _, level := range levels {
		resp.Packages = append(resp.Packages, vipLevelToProto(level))
	}
	return resp, nil
}

// GetMyVip 返回当前登录用户 VIP 状态。
func (s *Server) GetMyVip(ctx context.Context, req *walletv1.GetMyVipRequest) (*walletv1.GetMyVipResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	vip, err := s.svc.GetMyVip(ctx, req.GetUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.GetMyVipResponse{Vip: vipToProto(vip)}, nil
}

// PurchaseVip 购买、续期或升级 VIP。
func (s *Server) PurchaseVip(ctx context.Context, req *walletv1.PurchaseVipRequest) (*walletv1.PurchaseVipResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	receipt, err := s.svc.PurchaseVip(ctx, ledger.PurchaseVipCommand{
		AppCode:   req.GetAppCode(),
		CommandID: req.GetCommandId(),
		UserID:    req.GetUserId(),
		Level:     req.GetLevel(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.PurchaseVipResponse{
		OrderId:          receipt.OrderID,
		TransactionId:    receipt.TransactionID,
		Vip:              vipToProto(receipt.Vip),
		CoinSpent:        receipt.CoinSpent,
		CoinBalanceAfter: receipt.CoinBalanceAfter,
		RewardItems:      vipRewardItemsToProto(receipt.RewardItems),
	}, nil
}

// GrantVip 统一处理活动和后台赠送 VIP。
func (s *Server) GrantVip(ctx context.Context, req *walletv1.GrantVipRequest) (*walletv1.GrantVipResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	receipt, err := s.svc.GrantVip(ctx, ledger.GrantVipCommand{
		AppCode:        req.GetAppCode(),
		CommandID:      req.GetCommandId(),
		TargetUserID:   req.GetTargetUserId(),
		Level:          req.GetLevel(),
		GrantSource:    req.GetGrantSource(),
		OperatorUserID: req.GetOperatorUserId(),
		Reason:         req.GetReason(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.GrantVipResponse{
		TransactionId: receipt.TransactionID,
		Vip:           vipToProto(receipt.Vip),
		RewardItems:   vipRewardItemsToProto(receipt.RewardItems),
		ServerTimeMs:  time.Now().UnixMilli(),
	}, nil
}

// ListAdminVipLevels 返回后台 VIP 配置。
func (s *Server) ListAdminVipLevels(ctx context.Context, req *walletv1.ListAdminVipLevelsRequest) (*walletv1.ListAdminVipLevelsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	levels, err := s.svc.ListAdminVipLevels(ctx)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &walletv1.ListAdminVipLevelsResponse{Levels: make([]*walletv1.VipLevel, 0, len(levels)), ServerTimeMs: time.Now().UnixMilli()}
	for _, level := range levels {
		resp.Levels = append(resp.Levels, vipLevelToProto(level))
	}
	return resp, nil
}

// UpdateAdminVipLevels 保存后台 VIP 配置。
func (s *Server) UpdateAdminVipLevels(ctx context.Context, req *walletv1.UpdateAdminVipLevelsRequest) (*walletv1.UpdateAdminVipLevelsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	commands := make([]ledger.AdminVipLevelCommand, 0, len(req.GetLevels()))
	for _, item := range req.GetLevels() {
		if item == nil {
			continue
		}
		commands = append(commands, ledger.AdminVipLevelCommand{
			Level:                      item.GetLevel(),
			Name:                       item.GetName(),
			Status:                     item.GetStatus(),
			PriceCoin:                  item.GetPriceCoin(),
			DurationMS:                 item.GetDurationMs(),
			RewardResourceGroupID:      item.GetRewardResourceGroupId(),
			RequiredRechargeCoinAmount: item.GetRequiredRechargeCoinAmount(),
			SortOrder:                  item.GetSortOrder(),
		})
	}
	levels, err := s.svc.UpdateAdminVipLevels(ctx, commands, req.GetOperatorUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &walletv1.UpdateAdminVipLevelsResponse{Levels: make([]*walletv1.VipLevel, 0, len(levels)), ServerTimeMs: time.Now().UnixMilli()}
	for _, level := range levels {
		resp.Levels = append(resp.Levels, vipLevelToProto(level))
	}
	return resp, nil
}

func vipToProto(vip ledger.UserVip) *walletv1.UserVip {
	return &walletv1.UserVip{
		UserId:      vip.UserID,
		Level:       vip.Level,
		Name:        vip.Name,
		Active:      vip.Active,
		StartedAtMs: vip.StartedAtMS,
		ExpiresAtMs: vip.ExpiresAtMS,
		UpdatedAtMs: vip.UpdatedAtMS,
	}
}

func vipLevelToProto(level ledger.VipLevel) *walletv1.VipLevel {
	return &walletv1.VipLevel{
		Level:                      level.Level,
		Name:                       level.Name,
		Status:                     level.Status,
		PriceCoin:                  level.PriceCoin,
		DurationMs:                 level.DurationMS,
		RewardResourceGroupId:      level.RewardResourceGroupID,
		RewardItems:                vipRewardItemsToProto(level.RewardItems),
		CanPurchase:                level.CanPurchase,
		SortOrder:                  level.SortOrder,
		RechargeGateRequired:       level.RechargeGateRequired,
		RequiredRechargeCoinAmount: level.RequiredRechargeCoinAmount,
		UserRechargeCoinAmount:     level.UserRechargeCoinAmount,
		PurchaseLockedReason:       level.PurchaseLockedReason,
		CreatedAtMs:                level.CreatedAtMS,
		UpdatedAtMs:                level.UpdatedAtMS,
	}
}

func vipRewardItemsToProto(items []ledger.VipRewardItem) []*walletv1.VipRewardItem {
	resp := make([]*walletv1.VipRewardItem, 0, len(items))
	for _, item := range items {
		resp = append(resp, &walletv1.VipRewardItem{
			ResourceId:   item.ResourceID,
			ResourceCode: item.ResourceCode,
			ResourceType: item.ResourceType,
			Name:         item.Name,
			Quantity:     item.Quantity,
			ExpiresAtMs:  item.ExpiresAtMS,
			AssetUrl:     item.AssetURL,
			PreviewUrl:   item.PreviewURL,
			AnimationUrl: item.AnimationURL,
		})
	}
	return resp
}
