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/weeklystar"
	service "hyapp/services/activity-service/internal/service/weeklystar"
)

// WeeklyStarServer exposes App/H5 weekly-star reads.
type WeeklyStarServer struct {
	activityv1.UnimplementedWeeklyStarServiceServer
	svc *service.Service
}

func NewWeeklyStarServer(svc *service.Service) *WeeklyStarServer {
	return &WeeklyStarServer{svc: svc}
}

func (s *WeeklyStarServer) GetWeeklyStarCurrent(ctx context.Context, req *activityv1.GetWeeklyStarCurrentRequest) (*activityv1.GetWeeklyStarCurrentResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	cycle, topEntries, myEntry, found, err := s.svc.GetCurrent(ctx, req.GetUserId(), req.GetRegionId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.GetWeeklyStarCurrentResponse{
		Cycle:        weeklyStarCycleToProto(cycle),
		TopEntries:   weeklyStarEntriesToProto(topEntries),
		ServerTimeMs: time.Now().UnixMilli(),
	}
	if found {
		resp.MyEntry = weeklyStarEntryToProto(myEntry)
	}
	return resp, nil
}

func (s *WeeklyStarServer) ListWeeklyStarLeaderboard(ctx context.Context, req *activityv1.ListWeeklyStarLeaderboardRequest) (*activityv1.ListWeeklyStarLeaderboardResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	cycle, entries, nextToken, total, err := s.svc.ListLeaderboard(ctx, req.GetCycleId(), req.GetRegionId(), req.GetPageSize(), req.GetPageToken())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.ListWeeklyStarLeaderboardResponse{
		Cycle:         weeklyStarCycleToProto(cycle),
		Entries:       weeklyStarEntriesToProto(entries),
		NextPageToken: nextToken,
		Total:         total,
	}, nil
}

func (s *WeeklyStarServer) ListWeeklyStarHistory(ctx context.Context, req *activityv1.ListWeeklyStarHistoryRequest) (*activityv1.ListWeeklyStarHistoryResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	cycles, err := s.svc.ListHistory(ctx, req.GetRegionId(), req.GetLimit())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListWeeklyStarHistoryResponse{Cycles: make([]*activityv1.WeeklyStarHistoryCycle, 0, len(cycles)), ServerTimeMs: time.Now().UnixMilli()}
	for _, cycle := range cycles {
		resp.Cycles = append(resp.Cycles, &activityv1.WeeklyStarHistoryCycle{
			Cycle:      weeklyStarCycleToProto(cycle.Cycle),
			TopEntries: weeklyStarEntriesToProto(cycle.TopEntries),
		})
	}
	return resp, nil
}

// AdminWeeklyStarServer exposes admin weekly-star configuration and audit reads.
type AdminWeeklyStarServer struct {
	activityv1.UnimplementedAdminWeeklyStarServiceServer
	svc *service.Service
}

func NewAdminWeeklyStarServer(svc *service.Service) *AdminWeeklyStarServer {
	return &AdminWeeklyStarServer{svc: svc}
}

func (s *AdminWeeklyStarServer) ListWeeklyStarCycles(ctx context.Context, req *activityv1.ListWeeklyStarCyclesRequest) (*activityv1.ListWeeklyStarCyclesResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	var regionID *int64
	if req.GetRegionId() >= 0 && req.RegionId != 0 {
		value := req.GetRegionId()
		regionID = &value
	}
	cycles, total, err := s.svc.ListCycles(ctx, domain.ListQuery{
		RegionID: regionID,
		Status:   req.GetStatus(),
		StartMS:  req.GetStartMs(),
		EndMS:    req.GetEndMs(),
		Page:     req.GetPage(),
		PageSize: req.GetPageSize(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListWeeklyStarCyclesResponse{Cycles: make([]*activityv1.WeeklyStarCycle, 0, len(cycles)), Total: total}
	for _, cycle := range cycles {
		resp.Cycles = append(resp.Cycles, weeklyStarCycleToProto(cycle))
	}
	return resp, nil
}

func (s *AdminWeeklyStarServer) CreateWeeklyStarCycle(ctx context.Context, req *activityv1.UpsertWeeklyStarCycleRequest) (*activityv1.UpsertWeeklyStarCycleResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	cycle, created, err := s.svc.CreateCycle(ctx, domain.CycleCommand{Cycle: weeklyStarCycleFromProto(req.GetCycle()), OperatorAdminID: req.GetOperatorAdminId()})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpsertWeeklyStarCycleResponse{Cycle: weeklyStarCycleToProto(cycle), Created: created}, nil
}

func (s *AdminWeeklyStarServer) GetWeeklyStarCycle(ctx context.Context, req *activityv1.GetWeeklyStarCycleRequest) (*activityv1.GetWeeklyStarCycleResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	cycle, err := s.svc.GetCycle(ctx, req.GetCycleId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetWeeklyStarCycleResponse{Cycle: weeklyStarCycleToProto(cycle)}, nil
}

func (s *AdminWeeklyStarServer) UpdateWeeklyStarCycle(ctx context.Context, req *activityv1.UpsertWeeklyStarCycleRequest) (*activityv1.UpsertWeeklyStarCycleResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	cycle, created, err := s.svc.UpdateCycle(ctx, domain.CycleCommand{Cycle: weeklyStarCycleFromProto(req.GetCycle()), OperatorAdminID: req.GetOperatorAdminId()})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpsertWeeklyStarCycleResponse{Cycle: weeklyStarCycleToProto(cycle), Created: created}, nil
}

func (s *AdminWeeklyStarServer) SetWeeklyStarCycleStatus(ctx context.Context, req *activityv1.SetWeeklyStarCycleStatusRequest) (*activityv1.SetWeeklyStarCycleStatusResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	cycle, err := s.svc.SetCycleStatus(ctx, req.GetCycleId(), req.GetStatus(), req.GetOperatorAdminId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.SetWeeklyStarCycleStatusResponse{Cycle: weeklyStarCycleToProto(cycle)}, nil
}

func (s *AdminWeeklyStarServer) ListWeeklyStarLeaderboard(ctx context.Context, req *activityv1.ListWeeklyStarLeaderboardRequest) (*activityv1.ListWeeklyStarLeaderboardResponse, error) {
	return NewWeeklyStarServer(s.svc).ListWeeklyStarLeaderboard(ctx, req)
}

func (s *AdminWeeklyStarServer) ListWeeklyStarSettlements(ctx context.Context, req *activityv1.ListWeeklyStarSettlementsRequest) (*activityv1.ListWeeklyStarSettlementsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	settlements, err := s.svc.ListSettlements(ctx, req.GetCycleId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListWeeklyStarSettlementsResponse{Settlements: make([]*activityv1.WeeklyStarSettlement, 0, len(settlements))}
	for _, settlement := range settlements {
		resp.Settlements = append(resp.Settlements, weeklyStarSettlementToProto(settlement))
	}
	return resp, nil
}

func weeklyStarCycleFromProto(item *activityv1.WeeklyStarCycle) domain.Cycle {
	if item == nil {
		return domain.Cycle{}
	}
	cycle := domain.Cycle{
		CycleID:          item.GetCycleId(),
		ActivityCode:     item.GetActivityCode(),
		RegionID:         item.GetRegionId(),
		Title:            item.GetTitle(),
		Status:           item.GetStatus(),
		StartMS:          item.GetStartMs(),
		EndMS:            item.GetEndMs(),
		CreatedByAdminID: item.GetCreatedByAdminId(),
		UpdatedByAdminID: item.GetUpdatedByAdminId(),
	}
	for _, gift := range item.GetGifts() {
		cycle.Gifts = append(cycle.Gifts, domain.Gift{GiftID: gift.GetGiftId(), SortOrder: gift.GetSortOrder()})
	}
	for _, reward := range item.GetRewards() {
		cycle.Rewards = append(cycle.Rewards, domain.Reward{RankNo: reward.GetRankNo(), ResourceGroupID: reward.GetResourceGroupId()})
	}
	return cycle
}

func weeklyStarCycleToProto(item domain.Cycle) *activityv1.WeeklyStarCycle {
	if item.CycleID == "" {
		return nil
	}
	resp := &activityv1.WeeklyStarCycle{
		AppCode:          item.AppCode,
		CycleId:          item.CycleID,
		ActivityCode:     item.ActivityCode,
		RegionId:         item.RegionID,
		Title:            item.Title,
		Status:           item.Status,
		StartMs:          item.StartMS,
		EndMs:            item.EndMS,
		Gifts:            make([]*activityv1.WeeklyStarGift, 0, len(item.Gifts)),
		Rewards:          make([]*activityv1.WeeklyStarReward, 0, len(item.Rewards)),
		CreatedByAdminId: item.CreatedByAdminID,
		UpdatedByAdminId: item.UpdatedByAdminID,
		SettledAtMs:      item.SettledAtMS,
		CreatedAtMs:      item.CreatedAtMS,
		UpdatedAtMs:      item.UpdatedAtMS,
	}
	for _, gift := range item.Gifts {
		resp.Gifts = append(resp.Gifts, &activityv1.WeeklyStarGift{GiftId: gift.GiftID, SortOrder: gift.SortOrder})
	}
	for _, reward := range item.Rewards {
		resp.Rewards = append(resp.Rewards, &activityv1.WeeklyStarReward{RankNo: reward.RankNo, ResourceGroupId: reward.ResourceGroupID})
	}
	return resp
}

func weeklyStarEntryToProto(item domain.LeaderboardEntry) *activityv1.WeeklyStarLeaderboardEntry {
	if item.UserID <= 0 {
		return nil
	}
	return &activityv1.WeeklyStarLeaderboardEntry{
		RankNo:          item.RankNo,
		UserId:          item.UserID,
		Score:           item.Score,
		FirstScoredAtMs: item.FirstScoredAtMS,
		LastScoredAtMs:  item.LastScoredAtMS,
	}
}

func weeklyStarEntriesToProto(items []domain.LeaderboardEntry) []*activityv1.WeeklyStarLeaderboardEntry {
	resp := make([]*activityv1.WeeklyStarLeaderboardEntry, 0, len(items))
	for _, item := range items {
		resp = append(resp, weeklyStarEntryToProto(item))
	}
	return resp
}

func weeklyStarSettlementToProto(item domain.Settlement) *activityv1.WeeklyStarSettlement {
	return &activityv1.WeeklyStarSettlement{
		SettlementId:    item.SettlementID,
		CycleId:         item.CycleID,
		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,
	}
}
