package grpc

import (
	"context"
	"time"

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

type ActivityTemplateRuntimeServer struct {
	activityv1.UnimplementedActivityTemplateRuntimeServiceServer
	svc *service.Service
}

func NewActivityTemplateRuntimeServer(svc *service.Service) *ActivityTemplateRuntimeServer {
	return &ActivityTemplateRuntimeServer{svc: svc}
}

func (s *ActivityTemplateRuntimeServer) GetCurrentActivityTemplate(ctx context.Context, req *activityv1.GetCurrentActivityTemplateRequest) (*activityv1.GetCurrentActivityTemplateResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	runtime, found, err := s.svc.GetCurrentRuntime(ctx, req.GetRegionId(), req.GetTemplateCode())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	if !found {
		return &activityv1.GetCurrentActivityTemplateResponse{Found: false}, nil
	}
	return &activityv1.GetCurrentActivityTemplateResponse{Runtime: activityTemplateRuntimeToProto(runtime), Found: true}, nil
}

func (s *ActivityTemplateRuntimeServer) VisitActivityTemplate(ctx context.Context, req *activityv1.VisitActivityTemplateRequest) (*activityv1.VisitActivityTemplateResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	result, err := s.svc.Visit(ctx, domain.VisitCommand{
		UserID: req.GetUserId(), RegionID: req.GetRegionId(), TemplateCode: req.GetTemplateCode(), CommandID: req.GetCommandId(), VersionNo: req.GetVersionNo(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.VisitActivityTemplateResponse{
		Runtime: activityTemplateRuntimeToProto(result.Runtime), Tasks: activityTemplateTasksToProto(result.Tasks), FirstVisit: result.FirstVisit,
	}, nil
}

func (s *ActivityTemplateRuntimeServer) ListActivityTemplateTasks(ctx context.Context, req *activityv1.ListActivityTemplateTasksRequest) (*activityv1.ListActivityTemplateTasksResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	runtime, items, nextRefreshMS, err := s.svc.ListRuntimeTasks(ctx, req.GetUserId(), req.GetRegionId(), req.GetVersionNo(), req.GetTemplateCode(), req.GetTaskDay())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.ListActivityTemplateTasksResponse{
		Runtime: activityTemplateRuntimeToProto(runtime), Tasks: activityTemplateTasksToProto(items), NextRefreshAtMs: nextRefreshMS,
	}, nil
}

func (s *ActivityTemplateRuntimeServer) ClaimActivityTemplateTaskReward(ctx context.Context, req *activityv1.ClaimActivityTemplateTaskRewardRequest) (*activityv1.ClaimActivityTemplateTaskRewardResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	claim, err := s.svc.ClaimRuntimeTaskReward(ctx, domain.ClaimCommand{
		UserID: req.GetUserId(), RegionID: req.GetRegionId(), TemplateCode: req.GetTemplateCode(), VersionNo: req.GetVersionNo(), TaskDay: req.GetTaskDay(),
		TaskKey: req.GetTaskKey(), CommandID: req.GetCommandId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.ClaimActivityTemplateTaskRewardResponse{Claim: activityTemplateTaskClaimToProto(claim), Claimed: claim.Status == domain.ClaimStatusGranted}, nil
}

func (s *ActivityTemplateRuntimeServer) ListActivityTemplateLeaderboard(ctx context.Context, req *activityv1.ListActivityTemplateLeaderboardRequest) (*activityv1.ListActivityTemplateLeaderboardResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	result, err := s.svc.ListRuntimeLeaderboard(ctx, domain.LeaderboardQuery{
		UserID: req.GetUserId(), RegionID: req.GetRegionId(), TemplateCode: req.GetTemplateCode(), VersionNo: req.GetVersionNo(), BoardType: req.GetBoardType(),
		PeriodKey: req.GetPeriodKey(), Page: req.GetPage(), PageSize: req.GetPageSize(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return activityTemplateLeaderboardResultToProto(result), nil
}

func activityTemplateRuntimeToProto(item domain.Runtime) *activityv1.ActivityTemplateRuntime {
	if item.Template.TemplateID == "" {
		return nil
	}
	nowMS := time.Now().UTC().UnixMilli()
	item.Template.LifecycleStatus = service.RuntimeLifecycleStatus(item.Template, item.RuntimeFromMS, item.RuntimeToMS, nowMS)
	return &activityv1.ActivityTemplateRuntime{
		Template: activityTemplateToProto(item.Template), VersionNo: item.VersionNo,
		RuntimeFromMs: item.RuntimeFromMS, RuntimeToMs: item.RuntimeToMS, ServerTimeMs: nowMS,
	}
}

func activityTemplateTasksToProto(items []domain.TaskProgress) []*activityv1.ActivityTemplateRuntimeTask {
	out := make([]*activityv1.ActivityTemplateRuntimeTask, 0, len(items))
	for _, item := range items {
		out = append(out, &activityv1.ActivityTemplateRuntimeTask{
			TemplateId: item.TemplateID, TemplateCode: item.TemplateCode, VersionNo: item.VersionNo,
			TaskDay: item.TaskDay, TaskKey: item.TaskKey, TaskType: item.TaskType, TargetValue: item.TargetValue,
			ProgressValue: item.ProgressValue, RewardResourceGroupId: item.RewardResourceGroupID,
			Status: item.Status, Claimable: item.Status == domain.TaskStatusCompleted,
			CompletedAtMs: item.CompletedAtMS, ClaimedAtMs: item.ClaimedAtMS, SortOrder: item.SortOrder,
			RewardSnapshotId: item.RewardSnapshot.SnapshotID, RewardSnapshotHash: item.RewardSnapshot.SnapshotHash,
			RewardName: item.RewardSnapshot.Name, RewardItems: activityTemplateRewardItemsToProto(item.RewardSnapshot.Items),
		})
	}
	return out
}

func activityTemplateTaskClaimToProto(item domain.TaskClaim) *activityv1.ActivityTemplateTaskClaim {
	if item.ClaimID == "" {
		return nil
	}
	return &activityv1.ActivityTemplateTaskClaim{
		ClaimId: item.ClaimID, CommandId: item.CommandID, TemplateId: item.TemplateID, TemplateCode: item.TemplateCode,
		VersionNo: item.VersionNo, TaskDay: item.TaskDay, TaskKey: item.TaskKey, UserId: item.UserID,
		RewardResourceGroupId: item.RewardResourceGroupID, WalletCommandId: item.WalletCommandID,
		WalletGrantId: item.WalletGrantID, Status: item.Status, FailureReason: item.FailureReason,
		AttemptCount: item.AttemptCount, CreatedAtMs: item.CreatedAtMS, UpdatedAtMs: item.UpdatedAtMS, GrantedAtMs: item.GrantedAtMS,
		RewardSnapshotId: item.RewardSnapshot.SnapshotID, RewardName: item.RewardSnapshot.Name,
		RewardSnapshotHash: item.RewardSnapshot.SnapshotHash, EarningRegionId: item.EarningRegionID,
		AttributionAtMs: item.AttributionAtMS,
		RewardItems:     activityTemplateRewardItemsToProto(item.RewardSnapshot.Items),
	}
}

func activityTemplateLeaderboardResultToProto(result domain.LeaderboardResult) *activityv1.ListActivityTemplateLeaderboardResponse {
	resp := &activityv1.ListActivityTemplateLeaderboardResponse{
		Runtime: activityTemplateRuntimeToProto(result.Runtime), BoardType: result.Period.BoardType, PeriodKey: result.Period.PeriodKey,
		PeriodStartMs: result.Period.StartMS, PeriodEndMs: result.Period.EndMS, PeriodStatus: result.Period.Status,
		Entries: make([]*activityv1.ActivityTemplateLeaderboardEntry, 0, len(result.Entries)), Total: result.Total,
		SettleAfterMs: result.Period.SettleAfterMS,
	}
	for _, item := range result.Entries {
		resp.Entries = append(resp.Entries, activityTemplateLeaderboardEntryToProto(item))
	}
	if result.MyFound {
		resp.MyEntry = activityTemplateLeaderboardEntryToProto(result.MyEntry)
	}
	return resp
}

func activityTemplateLeaderboardEntryToProto(item domain.LeaderboardEntry) *activityv1.ActivityTemplateLeaderboardEntry {
	return &activityv1.ActivityTemplateLeaderboardEntry{
		RankNo: item.RankNo, UserId: item.UserID, Score: item.Score, GiftCount: item.GiftCount,
		CoinAmount: item.CoinAmount, FirstScoredAtMs: item.FirstScoredAtMS, Snapshot: item.Snapshot,
	}
}

func activityTemplateRewardJobToProto(item domain.RewardJob) *activityv1.ActivityTemplateRewardJob {
	if item.RewardJobID == "" {
		return nil
	}
	return &activityv1.ActivityTemplateRewardJob{
		RewardJobId: item.RewardJobID, TemplateId: item.TemplateID, TemplateCode: item.TemplateCode,
		VersionNo: item.VersionNo, BoardType: item.BoardType, PeriodKey: item.PeriodKey,
		RankNo: item.RankNo, 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, GrantedAtMs: item.GrantedAtMS,
		RewardSnapshotId: item.RewardSnapshot.SnapshotID, RewardSnapshotHash: item.RewardSnapshot.SnapshotHash,
		RewardName: item.RewardSnapshot.Name, RewardItems: activityTemplateRewardItemsToProto(item.RewardSnapshot.Items),
		MaxAttempts: item.MaxAttempts, NextRetryAtMs: item.NextRetryAtMS,
	}
}
