package grpc

import (
	"context"

	activityv1 "hyapp.local/api/proto/activity/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	domain "hyapp/services/activity-service/internal/domain/luckygift"
	service "hyapp/services/activity-service/internal/service/luckygift"
)

// LuckyGiftServer 暴露房间送礼主链路调用的幸运礼物查询和抽奖入口。
type LuckyGiftServer struct {
	activityv1.UnimplementedLuckyGiftServiceServer

	svc *service.Service
}

func NewLuckyGiftServer(svc *service.Service) *LuckyGiftServer {
	return &LuckyGiftServer{svc: svc}
}

func (s *LuckyGiftServer) CheckLuckyGift(ctx context.Context, req *activityv1.CheckLuckyGiftRequest) (*activityv1.CheckLuckyGiftResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	result, err := s.svc.Check(ctx, domain.CheckCommand{
		PoolID: req.GetPoolId(),
		GiftID: req.GetGiftId(),
		UserID: req.GetUserId(),
		RoomID: req.GetRoomId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.CheckLuckyGiftResponse{
		Enabled:        result.Enabled,
		Reason:         result.Reason,
		PoolId:         result.PoolID,
		GiftId:         result.GiftID,
		GiftPrice:      result.GiftPrice,
		RuleVersion:    result.RuleVersion,
		TargetRtpPpm:   result.TargetRTPPPM,
		ExperiencePool: result.ExperiencePool,
	}, nil
}

func (s *LuckyGiftServer) ExecuteLuckyGiftDraw(ctx context.Context, req *activityv1.ExecuteLuckyGiftDrawRequest) (*activityv1.ExecuteLuckyGiftDrawResponse, error) {
	meta := req.GetLuckyGift()
	ctx = appcode.WithContext(ctx, meta.GetMeta().GetAppCode())
	result, err := s.svc.Draw(ctx, luckyDrawCommandFromProto(meta))
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.ExecuteLuckyGiftDrawResponse{Result: luckyDrawResultToProto(result)}, nil
}

func (s *LuckyGiftServer) BatchExecuteLuckyGiftDraw(ctx context.Context, req *activityv1.BatchExecuteLuckyGiftDrawRequest) (*activityv1.BatchExecuteLuckyGiftDrawResponse, error) {
	if len(req.GetLuckyGifts()) == 0 {
		return nil, xerr.ToGRPCError(xerr.New(xerr.InvalidArgument, "lucky gift batch draw commands are required"))
	}
	ctx = appcode.WithContext(ctx, req.GetLuckyGifts()[0].GetMeta().GetAppCode())
	cmds := make([]domain.DrawCommand, 0, len(req.GetLuckyGifts()))
	for _, meta := range req.GetLuckyGifts() {
		cmds = append(cmds, luckyDrawCommandFromProto(meta))
	}
	results, err := s.svc.DrawBatch(ctx, cmds)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.BatchExecuteLuckyGiftDrawResponse{Results: make([]*activityv1.LuckyGiftDrawResult, 0, len(results))}
	for _, result := range results {
		resp.Results = append(resp.Results, luckyDrawResultToProto(result))
	}
	return resp, nil
}

func luckyDrawCommandFromProto(meta *activityv1.LuckyGiftMeta) domain.DrawCommand {
	return domain.DrawCommand{
		CommandID:       meta.GetCommandId(),
		PoolID:          meta.GetPoolId(),
		UserID:          meta.GetUserId(),
		TargetUserID:    meta.GetTargetUserId(),
		DeviceID:        meta.GetDeviceId(),
		RoomID:          meta.GetRoomId(),
		AnchorID:        meta.GetAnchorId(),
		GiftID:          meta.GetGiftId(),
		GiftCount:       meta.GetGiftCount(),
		CoinSpent:       meta.GetCoinSpent(),
		PaidAtMS:        meta.GetPaidAtMs(),
		VisibleRegionID: meta.GetVisibleRegionId(),
		CountryID:       meta.GetCountryId(),
	}
}

// AdminLuckyGiftServer 暴露后台幸运礼物规则配置和抽奖审计入口。
type AdminLuckyGiftServer struct {
	activityv1.UnimplementedAdminLuckyGiftServiceServer

	svc *service.Service
}

func NewAdminLuckyGiftServer(svc *service.Service) *AdminLuckyGiftServer {
	return &AdminLuckyGiftServer{svc: svc}
}

func (s *AdminLuckyGiftServer) GetLuckyGiftConfig(ctx context.Context, req *activityv1.GetLuckyGiftConfigRequest) (*activityv1.GetLuckyGiftConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	config, err := s.svc.GetConfig(ctx, req.GetPoolId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetLuckyGiftConfigResponse{Config: luckyRuleConfigToProto(config)}, nil
}

func (s *AdminLuckyGiftServer) UpsertLuckyGiftConfig(ctx context.Context, req *activityv1.UpsertLuckyGiftConfigRequest) (*activityv1.UpsertLuckyGiftConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	config := luckyRuleConfigFromProto(req.GetConfig())
	config.CreatedByAdminID = req.GetOperatorAdminId()
	updated, err := s.svc.UpsertConfig(ctx, config)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpsertLuckyGiftConfigResponse{Config: luckyRuleConfigToProto(updated)}, nil
}

func (s *AdminLuckyGiftServer) ListLuckyGiftConfigs(ctx context.Context, req *activityv1.ListLuckyGiftConfigsRequest) (*activityv1.ListLuckyGiftConfigsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	configs, err := s.svc.ListConfigs(ctx)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListLuckyGiftConfigsResponse{Configs: make([]*activityv1.LuckyGiftRuleConfig, 0, len(configs))}
	for _, config := range configs {
		resp.Configs = append(resp.Configs, luckyRuleConfigToProto(config))
	}
	return resp, nil
}

func (s *AdminLuckyGiftServer) ListLuckyGiftDraws(ctx context.Context, req *activityv1.ListLuckyGiftDrawsRequest) (*activityv1.ListLuckyGiftDrawsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	draws, total, err := s.svc.ListDraws(ctx, domain.DrawQuery{
		GiftID:   req.GetGiftId(),
		PoolID:   req.GetPoolId(),
		UserID:   req.GetUserId(),
		RoomID:   req.GetRoomId(),
		Status:   req.GetStatus(),
		Page:     req.GetPage(),
		PageSize: req.GetPageSize(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListLuckyGiftDrawsResponse{Draws: make([]*activityv1.LuckyGiftDrawResult, 0, len(draws)), Total: total}
	for _, draw := range draws {
		resp.Draws = append(resp.Draws, luckyDrawResultToProto(draw))
	}
	return resp, nil
}

func (s *AdminLuckyGiftServer) GetLuckyGiftDrawSummary(ctx context.Context, req *activityv1.GetLuckyGiftDrawSummaryRequest) (*activityv1.GetLuckyGiftDrawSummaryResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	summary, err := s.svc.GetDrawSummary(ctx, domain.DrawQuery{
		GiftID: req.GetGiftId(),
		PoolID: req.GetPoolId(),
		UserID: req.GetUserId(),
		RoomID: req.GetRoomId(),
		Status: req.GetStatus(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetLuckyGiftDrawSummaryResponse{Summary: luckyDrawSummaryToProto(summary)}, nil
}

func luckyRuleConfigFromProto(config *activityv1.LuckyGiftRuleConfig) domain.RuleConfig {
	if config == nil {
		return domain.RuleConfig{}
	}
	stages := make([]domain.RuleStage, 0, len(config.GetStages()))
	for _, stage := range config.GetStages() {
		tiers := make([]domain.RuleTier, 0, len(stage.GetTiers()))
		for _, tier := range stage.GetTiers() {
			tiers = append(tiers, domain.RuleTier{
				Stage:          tier.GetStage(),
				TierID:         tier.GetTierId(),
				MultiplierPPM:  tier.GetMultiplierPpm(),
				BaseWeightPPM:  tier.GetBaseWeightPpm(),
				RewardSource:   tier.GetRewardSource(),
				HighWaterOnly:  tier.GetHighWaterOnly(),
				BroadcastLevel: tier.GetBroadcastLevel(),
				Enabled:        tier.GetEnabled(),
			})
		}
		stages = append(stages, domain.RuleStage{
			Stage: stage.GetStage(),
			Tiers: tiers,
		})
	}
	return domain.RuleConfig{
		AppCode:                  config.GetAppCode(),
		PoolID:                   config.GetPoolId(),
		RuleVersion:              config.GetRuleVersion(),
		Enabled:                  config.GetEnabled(),
		TargetRTPPPM:             config.GetTargetRtpPpm(),
		PoolRatePPM:              config.GetPoolRatePpm(),
		SettlementWindowWager:    config.GetSettlementWindowWager(),
		ControlBandPPM:           config.GetControlBandPpm(),
		GiftPriceReference:       config.GetGiftPriceReference(),
		NoviceMaxEquivalentDraws: config.GetNoviceMaxEquivalentDraws(),
		NormalMaxEquivalentDraws: config.GetNormalMaxEquivalentDraws(),
		EffectiveFromMS:          config.GetEffectiveFromMs(),
		CreatedByAdminID:         config.GetCreatedByAdminId(),
		CreatedAtMS:              config.GetCreatedAtMs(),
		MaxSinglePayout:          config.GetMaxSinglePayout(),
		UserHourlyPayoutCap:      config.GetUserHourlyPayoutCap(),
		UserDailyPayoutCap:       config.GetUserDailyPayoutCap(),
		DeviceDailyPayoutCap:     config.GetDeviceDailyPayoutCap(),
		RoomHourlyPayoutCap:      config.GetRoomHourlyPayoutCap(),
		AnchorDailyPayoutCap:     config.GetAnchorDailyPayoutCap(),
		Stages:                   stages,
	}
}

func luckyRuleConfigToProto(config domain.RuleConfig) *activityv1.LuckyGiftRuleConfig {
	stages := make([]*activityv1.LuckyGiftRuleStage, 0, len(config.Stages))
	for _, stage := range config.Stages {
		tiers := make([]*activityv1.LuckyGiftRuleTier, 0, len(stage.Tiers))
		for _, tier := range stage.Tiers {
			tiers = append(tiers, &activityv1.LuckyGiftRuleTier{
				Stage:          tier.Stage,
				TierId:         tier.TierID,
				MultiplierPpm:  tier.MultiplierPPM,
				BaseWeightPpm:  tier.BaseWeightPPM,
				RewardSource:   tier.RewardSource,
				HighWaterOnly:  tier.HighWaterOnly,
				BroadcastLevel: tier.BroadcastLevel,
				Enabled:        tier.Enabled,
			})
		}
		stages = append(stages, &activityv1.LuckyGiftRuleStage{
			Stage: stage.Stage,
			Tiers: tiers,
		})
	}
	return &activityv1.LuckyGiftRuleConfig{
		AppCode:                  config.AppCode,
		PoolId:                   config.PoolID,
		RuleVersion:              config.RuleVersion,
		Enabled:                  config.Enabled,
		TargetRtpPpm:             config.TargetRTPPPM,
		PoolRatePpm:              config.PoolRatePPM,
		SettlementWindowWager:    config.SettlementWindowWager,
		ControlBandPpm:           config.ControlBandPPM,
		GiftPriceReference:       config.GiftPriceReference,
		NoviceMaxEquivalentDraws: config.NoviceMaxEquivalentDraws,
		NormalMaxEquivalentDraws: config.NormalMaxEquivalentDraws,
		EffectiveFromMs:          config.EffectiveFromMS,
		CreatedByAdminId:         config.CreatedByAdminID,
		CreatedAtMs:              config.CreatedAtMS,
		MaxSinglePayout:          config.MaxSinglePayout,
		UserHourlyPayoutCap:      config.UserHourlyPayoutCap,
		UserDailyPayoutCap:       config.UserDailyPayoutCap,
		DeviceDailyPayoutCap:     config.DeviceDailyPayoutCap,
		RoomHourlyPayoutCap:      config.RoomHourlyPayoutCap,
		AnchorDailyPayoutCap:     config.AnchorDailyPayoutCap,
		Stages:                   stages,
	}
}

func luckyDrawResultToProto(result domain.DrawResult) *activityv1.LuckyGiftDrawResult {
	return &activityv1.LuckyGiftDrawResult{
		DrawId:                    result.DrawID,
		CommandId:                 result.CommandID,
		PoolId:                    result.PoolID,
		GiftId:                    result.GiftID,
		RuleVersion:               result.RuleVersion,
		ExperiencePool:            result.ExperiencePool,
		SelectedTierId:            result.SelectedTierID,
		MultiplierPpm:             result.MultiplierPPM,
		BaseRewardCoins:           result.BaseRewardCoins,
		RoomAtmosphereRewardCoins: result.RoomAtmosphereRewardCoins,
		ActivitySubsidyCoins:      result.ActivitySubsidyCoins,
		EffectiveRewardCoins:      result.EffectiveRewardCoins,
		BudgetSourcesJson:         result.BudgetSourcesJSON,
		RewardStatus:              result.RewardStatus,
		RtpWindowIndex:            result.RTPWindowIndex,
		GiftRtpWindowIndex:        result.GiftRTPWindowIndex,
		GlobalBaseRtpPpm:          result.GlobalBaseRTPPPM,
		GiftBaseRtpPpm:            result.GiftBaseRTPPPM,
		StageFeedback:             result.StageFeedback,
		HighMultiplier:            result.HighMultiplier,
		CreatedAtMs:               result.CreatedAtMS,
		WalletTransactionId:       result.WalletTransactionID,
		CoinBalanceAfter:          result.CoinBalanceAfter,
	}
}

func luckyDrawSummaryToProto(summary domain.DrawSummary) *activityv1.LuckyGiftDrawSummary {
	return &activityv1.LuckyGiftDrawSummary{
		PoolId:                    summary.PoolID,
		TotalDraws:                summary.TotalDraws,
		UniqueUsers:               summary.UniqueUsers,
		UniqueRooms:               summary.UniqueRooms,
		TotalSpentCoins:           summary.TotalSpentCoins,
		TotalRewardCoins:          summary.TotalRewardCoins,
		BaseRewardCoins:           summary.BaseRewardCoins,
		RoomAtmosphereRewardCoins: summary.RoomAtmosphereRewardCoins,
		ActivitySubsidyCoins:      summary.ActivitySubsidyCoins,
		ActualRtpPpm:              summary.ActualRTPPPM,
		PendingDraws:              summary.PendingDraws,
		GrantedDraws:              summary.GrantedDraws,
		FailedDraws:               summary.FailedDraws,
	}
}
