package grpc

import (
	"context"

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

// RegistrationRewardServer 暴露 App 查询和 user-service 注册后发放接口。
type RegistrationRewardServer struct {
	activityv1.UnimplementedRegistrationRewardServiceServer

	svc *service.Service
}

func NewRegistrationRewardServer(svc *service.Service) *RegistrationRewardServer {
	return &RegistrationRewardServer{svc: svc}
}

func (s *RegistrationRewardServer) GetRegistrationRewardEligibility(ctx context.Context, req *activityv1.GetRegistrationRewardEligibilityRequest) (*activityv1.GetRegistrationRewardEligibilityResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	eligibility, err := s.svc.GetEligibility(ctx, req.GetUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetRegistrationRewardEligibilityResponse{Eligibility: eligibilityToProto(eligibility)}, nil
}

func (s *RegistrationRewardServer) IssueRegistrationReward(ctx context.Context, req *activityv1.IssueRegistrationRewardRequest) (*activityv1.IssueRegistrationRewardResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	claim, eligibility, issued, err := s.svc.IssueRegistrationReward(ctx, domain.IssueCommand{
		UserID:    req.GetUserId(),
		CommandID: req.GetCommandId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.IssueRegistrationRewardResponse{
		Claim:       registrationRewardClaimToProto(claim),
		Eligibility: eligibilityToProto(eligibility),
		Issued:      issued,
	}, nil
}

// AdminRegistrationRewardServer 暴露后台配置和领取记录查询入口。
type AdminRegistrationRewardServer struct {
	activityv1.UnimplementedAdminRegistrationRewardServiceServer

	svc *service.Service
}

func NewAdminRegistrationRewardServer(svc *service.Service) *AdminRegistrationRewardServer {
	return &AdminRegistrationRewardServer{svc: svc}
}

func (s *AdminRegistrationRewardServer) GetRegistrationRewardConfig(ctx context.Context, req *activityv1.GetRegistrationRewardConfigRequest) (*activityv1.GetRegistrationRewardConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	config, err := s.svc.GetConfig(ctx)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetRegistrationRewardConfigResponse{Config: registrationRewardConfigToProto(config)}, nil
}

func (s *AdminRegistrationRewardServer) UpdateRegistrationRewardConfig(ctx context.Context, req *activityv1.UpdateRegistrationRewardConfigRequest) (*activityv1.UpdateRegistrationRewardConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	config, err := s.svc.UpdateConfig(ctx, domain.Config{
		Enabled:          req.GetEnabled(),
		RewardType:       req.GetRewardType(),
		CoinAmount:       req.GetCoinAmount(),
		ResourceGroupID:  req.GetResourceGroupId(),
		DailyLimit:       req.GetDailyLimit(),
		UpdatedByAdminID: req.GetOperatorAdminId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpdateRegistrationRewardConfigResponse{Config: registrationRewardConfigToProto(config)}, nil
}

func (s *AdminRegistrationRewardServer) ListRegistrationRewardClaims(ctx context.Context, req *activityv1.ListRegistrationRewardClaimsRequest) (*activityv1.ListRegistrationRewardClaimsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	claims, total, todayClaimedCount, err := s.svc.ListClaims(ctx, domain.ClaimQuery{
		Status:         req.GetStatus(),
		UserID:         req.GetUserId(),
		ClaimedStartMS: req.GetClaimedStartMs(),
		ClaimedEndMS:   req.GetClaimedEndMs(),
		Page:           req.GetPage(),
		PageSize:       req.GetPageSize(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListRegistrationRewardClaimsResponse{
		Claims:            make([]*activityv1.RegistrationRewardClaim, 0, len(claims)),
		Total:             total,
		TodayClaimedCount: todayClaimedCount,
	}
	for _, claim := range claims {
		resp.Claims = append(resp.Claims, registrationRewardClaimToProto(claim))
	}
	return resp, nil
}

func registrationRewardConfigToProto(config domain.Config) *activityv1.RegistrationRewardConfig {
	return &activityv1.RegistrationRewardConfig{
		AppCode:          config.AppCode,
		Enabled:          config.Enabled,
		RewardType:       config.RewardType,
		CoinAmount:       config.CoinAmount,
		ResourceGroupId:  config.ResourceGroupID,
		DailyLimit:       config.DailyLimit,
		UpdatedByAdminId: config.UpdatedByAdminID,
		CreatedAtMs:      config.CreatedAtMS,
		UpdatedAtMs:      config.UpdatedAtMS,
	}
}

func registrationRewardClaimToProto(claim domain.Claim) *activityv1.RegistrationRewardClaim {
	if claim.ClaimID == "" {
		return nil
	}
	return &activityv1.RegistrationRewardClaim{
		ClaimId:             claim.ClaimID,
		CommandId:           claim.CommandID,
		UserId:              claim.UserID,
		RewardDay:           claim.RewardDay,
		RewardType:          claim.RewardType,
		CoinAmount:          claim.CoinAmount,
		ResourceGroupId:     claim.ResourceGroupID,
		Status:              claim.Status,
		WalletCommandId:     claim.WalletCommandID,
		WalletTransactionId: claim.WalletTransactionID,
		ResourceGrantId:     claim.ResourceGrantID,
		FailureReason:       claim.FailureReason,
		ClaimedAtMs:         claim.ClaimedAtMS,
		CreatedAtMs:         claim.CreatedAtMS,
		UpdatedAtMs:         claim.UpdatedAtMS,
	}
}

func eligibilityToProto(item domain.Eligibility) *activityv1.RegistrationRewardEligibility {
	return &activityv1.RegistrationRewardEligibility{
		CanReceive:      item.CanReceive,
		Reason:          item.Reason,
		Enabled:         item.Enabled,
		RewardType:      item.RewardType,
		CoinAmount:      item.CoinAmount,
		ResourceGroupId: item.ResourceGroupID,
		DailyLimit:      item.DailyLimit,
		DailyUsed:       item.DailyUsed,
		DailyRemaining:  item.DailyRemaining,
		RewardDay:       item.RewardDay,
		ServerTimeMs:    item.ServerTimeMS,
	}
}
