package grpc

import (
	"context"

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

// SevenDayCheckInServer 暴露 App 七日签到面板查询和签到命令入口。
type SevenDayCheckInServer struct {
	activityv1.UnimplementedSevenDayCheckInServiceServer

	svc *service.Service
}

func NewSevenDayCheckInServer(svc *service.Service) *SevenDayCheckInServer {
	return &SevenDayCheckInServer{svc: svc}
}

func (s *SevenDayCheckInServer) GetSevenDayCheckInStatus(ctx context.Context, req *activityv1.GetSevenDayCheckInStatusRequest) (*activityv1.GetSevenDayCheckInStatusResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	status, err := s.svc.GetStatus(ctx, req.GetUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return checkinStatusToProto(status), nil
}

func (s *SevenDayCheckInServer) SignSevenDayCheckIn(ctx context.Context, req *activityv1.SignSevenDayCheckInRequest) (*activityv1.SignSevenDayCheckInResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	claim, err := s.svc.Sign(ctx, req.GetUserId(), req.GetCommandId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return checkinSignToProto(claim), nil
}

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

	svc *service.Service
}

func NewAdminSevenDayCheckInServer(svc *service.Service) *AdminSevenDayCheckInServer {
	return &AdminSevenDayCheckInServer{svc: svc}
}

func (s *AdminSevenDayCheckInServer) GetSevenDayCheckInConfig(ctx context.Context, req *activityv1.GetSevenDayCheckInConfigRequest) (*activityv1.GetSevenDayCheckInConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	config, err := s.svc.GetConfig(ctx)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetSevenDayCheckInConfigResponse{Config: checkinConfigToProto(config)}, nil
}

func (s *AdminSevenDayCheckInServer) UpdateSevenDayCheckInConfig(ctx context.Context, req *activityv1.UpdateSevenDayCheckInConfigRequest) (*activityv1.UpdateSevenDayCheckInConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	rewards := make([]domain.Reward, 0, len(req.GetRewards()))
	for _, reward := range req.GetRewards() {
		rewards = append(rewards, domain.Reward{DayIndex: reward.GetDayIndex(), ResourceGroupID: reward.GetResourceGroupId()})
	}
	config, err := s.svc.UpdateConfig(ctx, domain.Config{
		Enabled:          req.GetEnabled(),
		Rewards:          rewards,
		UpdatedByAdminID: req.GetOperatorAdminId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpdateSevenDayCheckInConfigResponse{Config: checkinConfigToProto(config)}, nil
}

func (s *AdminSevenDayCheckInServer) ListSevenDayCheckInClaims(ctx context.Context, req *activityv1.ListSevenDayCheckInClaimsRequest) (*activityv1.ListSevenDayCheckInClaimsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	claims, total, err := s.svc.ListClaims(ctx, domain.ClaimQuery{
		Status:     req.GetStatus(),
		UserID:     req.GetUserId(),
		CheckinDay: req.GetCheckinDay(),
		DayIndex:   req.GetDayIndex(),
		Page:       req.GetPage(),
		PageSize:   req.GetPageSize(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListSevenDayCheckInClaimsResponse{Claims: make([]*activityv1.SevenDayCheckInClaim, 0, len(claims)), Total: total}
	for _, claim := range claims {
		resp.Claims = append(resp.Claims, checkinClaimToProto(claim))
	}
	return resp, nil
}

func checkinStatusToProto(status domain.StatusResult) *activityv1.GetSevenDayCheckInStatusResponse {
	resp := &activityv1.GetSevenDayCheckInStatusResponse{
		Enabled:         status.Enabled,
		CanSign:         status.CanSign,
		Reason:          status.Reason,
		CycleNo:         status.CycleNo,
		CurrentDayIndex: status.CurrentDayIndex,
		NextDayIndex:    status.NextDayIndex,
		CheckinDay:      status.CheckinDay,
		LastCheckinDay:  status.LastCheckinDay,
		ConfigVersion:   status.ConfigVersion,
		Rewards:         make([]*activityv1.SevenDayCheckInRewardStatus, 0, len(status.Rewards)),
		ServerTimeMs:    status.ServerTimeMS,
		NextRefreshAtMs: status.NextRefreshMS,
	}
	for _, reward := range status.Rewards {
		resp.Rewards = append(resp.Rewards, &activityv1.SevenDayCheckInRewardStatus{
			DayIndex:        reward.DayIndex,
			ResourceGroupId: reward.ResourceGroupID,
			Status:          reward.Status,
			ClaimId:         reward.ClaimID,
			WalletGrantId:   reward.WalletGrantID,
			GrantedAtMs:     reward.GrantedAtMS,
		})
	}
	return resp
}

func checkinSignToProto(claim domain.Claim) *activityv1.SignSevenDayCheckInResponse {
	return &activityv1.SignSevenDayCheckInResponse{
		ClaimId:         claim.ClaimID,
		CycleNo:         claim.CycleNo,
		CheckinDay:      claim.CheckinDay,
		DayIndex:        claim.DayIndex,
		ResourceGroupId: claim.ResourceGroupID,
		Status:          claim.Status,
		WalletGrantId:   claim.WalletGrantID,
		GrantedAtMs:     claim.GrantedAtMS,
		ServerTimeMs:    claim.UpdatedAtMS,
	}
}

func checkinConfigToProto(config domain.Config) *activityv1.SevenDayCheckInConfig {
	resp := &activityv1.SevenDayCheckInConfig{
		AppCode:          config.AppCode,
		Enabled:          config.Enabled,
		ConfigVersion:    config.ConfigVersion,
		Rewards:          make([]*activityv1.SevenDayCheckInReward, 0, len(config.Rewards)),
		UpdatedByAdminId: config.UpdatedByAdminID,
		CreatedAtMs:      config.CreatedAtMS,
		UpdatedAtMs:      config.UpdatedAtMS,
	}
	for _, reward := range config.Rewards {
		resp.Rewards = append(resp.Rewards, &activityv1.SevenDayCheckInReward{
			DayIndex:        reward.DayIndex,
			ResourceGroupId: reward.ResourceGroupID,
		})
	}
	return resp
}

func checkinClaimToProto(claim domain.Claim) *activityv1.SevenDayCheckInClaim {
	return &activityv1.SevenDayCheckInClaim{
		ClaimId:         claim.ClaimID,
		CommandId:       claim.CommandID,
		UserId:          claim.UserID,
		CycleNo:         claim.CycleNo,
		CheckinDay:      claim.CheckinDay,
		DayIndex:        claim.DayIndex,
		ConfigVersion:   claim.ConfigVersion,
		ResourceGroupId: claim.ResourceGroupID,
		WalletCommandId: claim.WalletCommandID,
		WalletGrantId:   claim.WalletGrantID,
		Status:          claim.Status,
		FailureReason:   claim.FailureReason,
		SignedAtMs:      claim.SignedAtMS,
		GrantedAtMs:     claim.GrantedAtMS,
		CreatedAtMs:     claim.CreatedAtMS,
		UpdatedAtMs:     claim.UpdatedAtMS,
	}
}
