package grpc

import (
	"context"

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

type CPWeeklyRankServer struct {
	activityv1.UnimplementedCPWeeklyRankServiceServer
	activityv1.UnimplementedAdminCPWeeklyRankServiceServer
	service *service.Service
}

func NewCPWeeklyRankServer(service *service.Service) *CPWeeklyRankServer {
	return &CPWeeklyRankServer{service: service}
}

func (s *CPWeeklyRankServer) GetCPWeeklyRankStatus(ctx context.Context, req *activityv1.GetCPWeeklyRankStatusRequest) (*activityv1.GetCPWeeklyRankStatusResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	if s.service == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp weekly rank service is not configured"))
	}
	status, err := s.service.GetStatus(ctx, req.GetLimit(), req.GetNowMs())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetCPWeeklyRankStatusResponse{
		Config:          cpWeeklyRankConfigToProto(status.Config),
		Entries:         cpWeeklyRankEntriesToProto(status.Entries),
		PreviousEntries: cpWeeklyRankEntriesToProto(status.PreviousEntries),
		PeriodStartMs:   status.PeriodStartMS,
		PeriodEndMs:     status.PeriodEndMS,
		ServerTimeMs:    status.ServerTimeMS,
	}, nil
}

func (s *CPWeeklyRankServer) GetCPWeeklyRankConfig(ctx context.Context, req *activityv1.GetCPWeeklyRankConfigRequest) (*activityv1.GetCPWeeklyRankConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	if s.service == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp weekly rank service is not configured"))
	}
	config, err := s.service.GetConfig(ctx)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetCPWeeklyRankConfigResponse{Config: cpWeeklyRankConfigToProto(config)}, nil
}

func (s *CPWeeklyRankServer) UpdateCPWeeklyRankConfig(ctx context.Context, req *activityv1.UpdateCPWeeklyRankConfigRequest) (*activityv1.UpdateCPWeeklyRankConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	if s.service == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp weekly rank service is not configured"))
	}
	config, err := s.service.UpdateConfig(ctx, cpWeeklyRankConfigFromProto(req.GetConfig()), req.GetOperatorAdminId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpdateCPWeeklyRankConfigResponse{Config: cpWeeklyRankConfigToProto(config)}, nil
}

func (s *CPWeeklyRankServer) ListCPWeeklyRankSettlements(ctx context.Context, req *activityv1.ListCPWeeklyRankSettlementsRequest) (*activityv1.ListCPWeeklyRankSettlementsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	if s.service == nil {
		return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp weekly rank service is not configured"))
	}
	items, err := s.service.ListSettlements(ctx, req.GetPeriodStartMs(), req.GetPeriodEndMs())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.ListCPWeeklyRankSettlementsResponse{Settlements: cpWeeklyRankSettlementsToProto(items)}, nil
}

func cpWeeklyRankConfigFromProto(item *activityv1.CPWeeklyRankConfig) domain.Config {
	if item == nil {
		return domain.Config{}
	}
	config := domain.Config{
		AppCode:          item.GetAppCode(),
		ActivityCode:     item.GetActivityCode(),
		Enabled:          item.GetEnabled(),
		RelationType:     item.GetRelationType(),
		TopCount:         item.GetTopCount(),
		UpdatedByAdminID: item.GetUpdatedByAdminId(),
		CreatedAtMS:      item.GetCreatedAtMs(),
		UpdatedAtMS:      item.GetUpdatedAtMs(),
		Rewards:          make([]domain.Reward, 0, len(item.GetRewards())),
	}
	for _, reward := range item.GetRewards() {
		config.Rewards = append(config.Rewards, domain.Reward{
			RankNo:          reward.GetRankNo(),
			ResourceGroupID: reward.GetResourceGroupId(),
		})
	}
	return config
}

func cpWeeklyRankConfigToProto(item domain.Config) *activityv1.CPWeeklyRankConfig {
	return &activityv1.CPWeeklyRankConfig{
		ActivityCode:     item.ActivityCode,
		Enabled:          item.Enabled,
		RelationType:     item.RelationType,
		TopCount:         item.TopCount,
		Rewards:          cpWeeklyRankRewardsToProto(item.Rewards),
		UpdatedByAdminId: item.UpdatedByAdminID,
		CreatedAtMs:      item.CreatedAtMS,
		UpdatedAtMs:      item.UpdatedAtMS,
		AppCode:          item.AppCode,
	}
}

func cpWeeklyRankRewardsToProto(items []domain.Reward) []*activityv1.CPWeeklyRankReward {
	resp := make([]*activityv1.CPWeeklyRankReward, 0, len(items))
	for _, item := range items {
		resp = append(resp, &activityv1.CPWeeklyRankReward{RankNo: item.RankNo, ResourceGroupId: item.ResourceGroupID})
	}
	return resp
}

func cpWeeklyRankEntriesToProto(items []domain.Entry) []*activityv1.CPWeeklyRankEntry {
	resp := make([]*activityv1.CPWeeklyRankEntry, 0, len(items))
	for _, item := range items {
		resp = append(resp, &activityv1.CPWeeklyRankEntry{
			Rank:            item.Rank,
			RelationshipId:  item.RelationshipID,
			RelationType:    item.RelationType,
			Score:           item.Score,
			UserA:           cpWeeklyRankUserToProto(item.UserA),
			UserB:           cpWeeklyRankUserToProto(item.UserB),
			FormedAtMs:      item.FormedAtMS,
			UpdatedAtMs:     item.UpdatedAtMS,
			FirstScoredAtMs: item.FirstScoredAtMS,
			LastScoredAtMs:  item.LastScoredAtMS,
		})
	}
	return resp
}

func cpWeeklyRankUserToProto(item domain.User) *activityv1.CPWeeklyRankUser {
	if item.UserID <= 0 {
		return nil
	}
	return &activityv1.CPWeeklyRankUser{
		UserId:        item.UserID,
		DisplayUserId: item.DisplayUserID,
		Username:      item.Username,
		Avatar:        item.Avatar,
	}
}

func cpWeeklyRankSettlementsToProto(items []domain.Settlement) []*activityv1.CPWeeklyRankSettlement {
	resp := make([]*activityv1.CPWeeklyRankSettlement, 0, len(items))
	for _, item := range items {
		resp = append(resp, &activityv1.CPWeeklyRankSettlement{
			SettlementId:    item.SettlementID,
			PeriodStartMs:   item.PeriodStartMS,
			PeriodEndMs:     item.PeriodEndMS,
			RankNo:          item.RankNo,
			RelationshipId:  item.RelationshipID,
			UserId:          item.UserID,
			Score:           item.Score,
			ResourceGroupId: item.ResourceGroupID,
			WalletCommandId: item.WalletCommandID,
			WalletGrantId:   item.WalletGrantID,
			Status:          item.Status,
			FailureReason:   item.FailureReason,
			AttemptCount:    item.AttemptCount,
			CreatedAtMs:     item.CreatedAtMS,
			UpdatedAtMs:     item.UpdatedAtMS,
		})
	}
	return resp
}
