package grpc

import (
	"context"

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

type WheelServer struct {
	activityv1.UnimplementedWheelServiceServer

	svc *service.Service
}

func NewWheelServer(svc *service.Service) *WheelServer {
	return &WheelServer{svc: svc}
}

func (s *WheelServer) ExecuteWheelDraw(ctx context.Context, req *activityv1.ExecuteWheelDrawRequest) (*activityv1.ExecuteWheelDrawResponse, error) {
	meta := req.GetWheel()
	ctx = appcode.WithContext(ctx, meta.GetMeta().GetAppCode())
	result, err := s.svc.Draw(ctx, domain.DrawCommand{
		CommandID:       meta.GetCommandId(),
		WheelID:         meta.GetWheelId(),
		UserID:          meta.GetUserId(),
		DeviceID:        meta.GetDeviceId(),
		DrawCount:       meta.GetDrawCount(),
		CoinSpent:       meta.GetCoinSpent(),
		PaidAtMS:        meta.GetPaidAtMs(),
		VisibleRegionID: meta.GetVisibleRegionId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.ExecuteWheelDrawResponse{Result: wheelDrawResultToProto(result)}, nil
}

type AdminWheelServer struct {
	activityv1.UnimplementedAdminWheelServiceServer

	svc *service.Service
}

func NewAdminWheelServer(svc *service.Service) *AdminWheelServer {
	return &AdminWheelServer{svc: svc}
}

func (s *AdminWheelServer) GetWheelConfig(ctx context.Context, req *activityv1.GetWheelConfigRequest) (*activityv1.GetWheelConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	config, err := s.svc.GetConfig(ctx, req.GetWheelId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetWheelConfigResponse{Config: wheelRuleConfigToProto(config)}, nil
}

func (s *AdminWheelServer) UpsertWheelConfig(ctx context.Context, req *activityv1.UpsertWheelConfigRequest) (*activityv1.UpsertWheelConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	config := wheelRuleConfigFromProto(req.GetConfig())
	config.CreatedByAdminID = req.GetOperatorAdminId()
	updated, err := s.svc.UpsertConfig(ctx, config)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpsertWheelConfigResponse{Config: wheelRuleConfigToProto(updated)}, nil
}

func (s *AdminWheelServer) ListWheelDraws(ctx context.Context, req *activityv1.ListWheelDrawsRequest) (*activityv1.ListWheelDrawsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	draws, total, err := s.svc.ListDraws(ctx, domain.DrawQuery{
		WheelID:  req.GetWheelId(),
		UserID:   req.GetUserId(),
		Status:   req.GetStatus(),
		Page:     req.GetPage(),
		PageSize: req.GetPageSize(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListWheelDrawsResponse{Draws: make([]*activityv1.WheelDrawResult, 0, len(draws)), Total: total}
	for _, draw := range draws {
		resp.Draws = append(resp.Draws, wheelDrawResultToProto(draw))
	}
	return resp, nil
}

func (s *AdminWheelServer) GetWheelDrawSummary(ctx context.Context, req *activityv1.GetWheelDrawSummaryRequest) (*activityv1.GetWheelDrawSummaryResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	summary, err := s.svc.GetDrawSummary(ctx, domain.DrawQuery{
		WheelID: req.GetWheelId(),
		UserID:  req.GetUserId(),
		Status:  req.GetStatus(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetWheelDrawSummaryResponse{Summary: wheelDrawSummaryToProto(summary)}, nil
}

func wheelRuleConfigFromProto(config *activityv1.WheelRuleConfig) domain.RuleConfig {
	if config == nil {
		return domain.RuleConfig{}
	}
	tiers := make([]domain.Tier, 0, len(config.GetTiers()))
	for _, tier := range config.GetTiers() {
		tiers = append(tiers, domain.Tier{
			TierID:        tier.GetTierId(),
			DisplayName:   tier.GetDisplayName(),
			RewardType:    tier.GetRewardType(),
			RewardID:      tier.GetRewardId(),
			RewardCount:   tier.GetRewardCount(),
			RewardCoins:   tier.GetRewardCoins(),
			RTPValueCoins: tier.GetRtpValueCoins(),
			WeightPPM:     tier.GetWeightPpm(),
			Enabled:       tier.GetEnabled(),
			MetadataJSON:  tier.GetMetadataJson(),
		})
	}
	return domain.RuleConfig{
		AppCode:               config.GetAppCode(),
		WheelID:               config.GetWheelId(),
		RuleVersion:           config.GetRuleVersion(),
		Enabled:               config.GetEnabled(),
		DrawPriceCoins:        config.GetDrawPriceCoins(),
		TargetRTPPPM:          config.GetTargetRtpPpm(),
		PoolRatePPM:           config.GetPoolRatePpm(),
		SettlementWindowDraws: config.GetSettlementWindowDraws(),
		InitialPoolCoins:      config.GetInitialPoolCoins(),
		PoolReserveCoins:      config.GetPoolReserveCoins(),
		MaxSingleRTPPayout:    config.GetMaxSingleRtpPayout(),
		EffectiveFromMS:       config.GetEffectiveFromMs(),
		CreatedByAdminID:      config.GetCreatedByAdminId(),
		CreatedAtMS:           config.GetCreatedAtMs(),
		Tiers:                 tiers,
	}
}

func wheelRuleConfigToProto(config domain.RuleConfig) *activityv1.WheelRuleConfig {
	tiers := make([]*activityv1.WheelPrizeTier, 0, len(config.Tiers))
	for _, tier := range config.Tiers {
		tiers = append(tiers, &activityv1.WheelPrizeTier{
			TierId:        tier.TierID,
			DisplayName:   tier.DisplayName,
			RewardType:    tier.RewardType,
			RewardId:      tier.RewardID,
			RewardCount:   tier.RewardCount,
			RewardCoins:   tier.RewardCoins,
			RtpValueCoins: tier.RTPValueCoins,
			WeightPpm:     tier.WeightPPM,
			Enabled:       tier.Enabled,
			MetadataJson:  tier.MetadataJSON,
		})
	}
	return &activityv1.WheelRuleConfig{
		AppCode:               config.AppCode,
		WheelId:               config.WheelID,
		RuleVersion:           config.RuleVersion,
		Enabled:               config.Enabled,
		DrawPriceCoins:        config.DrawPriceCoins,
		TargetRtpPpm:          config.TargetRTPPPM,
		PoolRatePpm:           config.PoolRatePPM,
		SettlementWindowDraws: config.SettlementWindowDraws,
		InitialPoolCoins:      config.InitialPoolCoins,
		PoolReserveCoins:      config.PoolReserveCoins,
		MaxSingleRtpPayout:    config.MaxSingleRTPPayout,
		EffectiveFromMs:       config.EffectiveFromMS,
		CreatedByAdminId:      config.CreatedByAdminID,
		CreatedAtMs:           config.CreatedAtMS,
		Tiers:                 tiers,
	}
}

func wheelDrawResultToProto(result domain.DrawResult) *activityv1.WheelDrawResult {
	if len(result.Rewards) == 0 {
		result.Rewards = []domain.DrawReward{{
			SelectedTierID: result.SelectedTierID,
			RewardType:     result.RewardType,
			RewardID:       result.RewardID,
			RewardCount:    result.RewardCount,
			RewardCoins:    result.RewardCoins,
			RTPValueCoins:  result.RTPValueCoins,
			RewardStatus:   result.RewardStatus,
			MetadataJSON:   result.MetadataJSON,
		}}
	}
	rewards := make([]*activityv1.WheelDrawReward, 0, len(result.Rewards))
	for _, reward := range result.Rewards {
		rewards = append(rewards, wheelDrawRewardToProto(reward))
	}
	return &activityv1.WheelDrawResult{
		DrawId:              result.DrawID,
		DrawIds:             result.DrawIDs,
		CommandId:           result.CommandID,
		WheelId:             result.WheelID,
		RuleVersion:         result.RuleVersion,
		SelectedTierId:      result.SelectedTierID,
		RewardType:          result.RewardType,
		RewardId:            result.RewardID,
		RewardCount:         result.RewardCount,
		RewardCoins:         result.RewardCoins,
		RtpValueCoins:       result.RTPValueCoins,
		RewardStatus:        result.RewardStatus,
		RtpWindowIndex:      result.RTPWindowIndex,
		ActualRtpPpm:        result.ActualRTPPPM,
		CreatedAtMs:         result.CreatedAtMS,
		WalletTransactionId: result.WalletTransactionID,
		CoinBalanceAfter:    result.CoinBalanceAfter,
		MetadataJson:        result.MetadataJSON,
		Rewards:             rewards,
	}
}

func wheelDrawRewardToProto(reward domain.DrawReward) *activityv1.WheelDrawReward {
	return &activityv1.WheelDrawReward{
		SelectedTierId: reward.SelectedTierID,
		RewardType:     reward.RewardType,
		RewardId:       reward.RewardID,
		RewardCount:    reward.RewardCount,
		RewardCoins:    reward.RewardCoins,
		RtpValueCoins:  reward.RTPValueCoins,
		RewardStatus:   reward.RewardStatus,
		MetadataJson:   reward.MetadataJSON,
	}
}

func wheelDrawSummaryToProto(summary domain.DrawSummary) *activityv1.WheelDrawSummary {
	return &activityv1.WheelDrawSummary{
		WheelId:            summary.WheelID,
		TotalDraws:         summary.TotalDraws,
		UniqueUsers:        summary.UniqueUsers,
		TotalSpentCoins:    summary.TotalSpentCoins,
		TotalRtpValueCoins: summary.TotalRTPValueCoins,
		ActualRtpPpm:       summary.ActualRTPPPM,
		PendingDraws:       summary.PendingDraws,
		GrantedDraws:       summary.GrantedDraws,
		FailedDraws:        summary.FailedDraws,
	}
}
