package grpc

import (
	"context"

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

// GrowthLevelServer 把 App 等级查询和内部等级事件消费适配为 gRPC。
type GrowthLevelServer struct {
	activityv1.UnimplementedGrowthLevelServiceServer

	svc *service.Service
}

func NewGrowthLevelServer(svc *service.Service) *GrowthLevelServer {
	return &GrowthLevelServer{svc: svc}
}

func (s *GrowthLevelServer) GetMyLevelOverview(ctx context.Context, req *activityv1.GetMyLevelOverviewRequest) (*activityv1.GetMyLevelOverviewResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	overview, err := s.svc.GetMyLevelOverview(ctx, req.GetUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.GetMyLevelOverviewResponse{
		Tracks:       make([]*activityv1.LevelTrackOverview, 0, len(overview.Tracks)),
		ServerTimeMs: overview.ServerTimeMS,
	}
	for _, item := range overview.Tracks {
		resp.Tracks = append(resp.Tracks, levelTrackOverviewToProto(item))
	}
	return resp, nil
}

func (s *GrowthLevelServer) GetLevelTrack(ctx context.Context, req *activityv1.GetLevelTrackRequest) (*activityv1.GetLevelTrackResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	detail, err := s.svc.GetLevelTrack(ctx, req.GetUserId(), req.GetTrack())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.GetLevelTrackResponse{
		Overview:     levelTrackOverviewToProto(detail.Overview),
		Rules:        make([]*activityv1.LevelRule, 0, len(detail.Rules)),
		Tiers:        make([]*activityv1.LevelTier, 0, len(detail.Tiers)),
		ServerTimeMs: detail.ServerTimeMS,
	}
	for _, rule := range detail.Rules {
		resp.Rules = append(resp.Rules, levelRuleToProto(rule))
	}
	for _, tier := range detail.Tiers {
		resp.Tiers = append(resp.Tiers, levelTierToProto(tier))
	}
	return resp, nil
}

func (s *GrowthLevelServer) BatchGetUserLevelDisplayProfiles(ctx context.Context, req *activityv1.BatchGetUserLevelDisplayProfilesRequest) (*activityv1.BatchGetUserLevelDisplayProfilesResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	profiles, err := s.svc.BatchGetUserLevelDisplayProfiles(ctx, req.GetUserIds())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.BatchGetUserLevelDisplayProfilesResponse{
		Profiles:     make([]*activityv1.UserLevelDisplayProfile, 0, len(profiles.Profiles)),
		ServerTimeMs: profiles.ServerTimeMS,
	}
	for _, item := range profiles.Profiles {
		resp.Profiles = append(resp.Profiles, userLevelDisplayProfileToProto(item))
	}
	return resp, nil
}

func (s *GrowthLevelServer) ListLevelRewards(ctx context.Context, req *activityv1.ListLevelRewardsRequest) (*activityv1.ListLevelRewardsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	rewards, total, err := s.svc.ListLevelRewards(ctx, domain.RewardQuery{
		UserID:   req.GetUserId(),
		Track:    req.GetTrack(),
		Status:   req.GetStatus(),
		Page:     req.GetPage(),
		PageSize: req.GetPageSize(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListLevelRewardsResponse{Rewards: make([]*activityv1.LevelRewardJob, 0, len(rewards)), Total: total}
	for _, reward := range rewards {
		resp.Rewards = append(resp.Rewards, levelRewardToProto(reward))
	}
	return resp, nil
}

func (s *GrowthLevelServer) ConsumeLevelEvent(ctx context.Context, req *activityv1.ConsumeLevelEventRequest) (*activityv1.ConsumeLevelEventResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	result, err := s.svc.ConsumeLevelEvent(ctx, domain.ValueEvent{
		EventID:         req.GetEventId(),
		SourceEventID:   req.GetSourceEventId(),
		SourceService:   req.GetSourceService(),
		SourceEventType: req.GetSourceEventType(),
		UserID:          req.GetUserId(),
		Track:           req.GetTrack(),
		MetricType:      req.GetMetricType(),
		ValueDelta:      req.GetValueDelta(),
		OccurredAtMS:    req.GetOccurredAtMs(),
		DimensionsJSON:  req.GetDimensionsJson(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.ConsumeLevelEventResponse{
		EventId:        result.EventID,
		Status:         result.Status,
		LevelChanged:   result.LevelChanged,
		PreviousLevel:  result.PreviousLevel,
		NewLevel:       result.NewLevel,
		RewardJobCount: result.RewardJobCount,
	}, nil
}

func (s *GrowthLevelServer) SetUserLevel(ctx context.Context, req *activityv1.SetUserLevelRequest) (*activityv1.SetUserLevelResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	result, err := s.svc.SetUserLevel(ctx, domain.SetUserLevelCommand{
		CommandID:      req.GetCommandId(),
		UserID:         req.GetUserId(),
		Track:          req.GetTrack(),
		Level:          req.GetLevel(),
		OperatorUserID: req.GetOperatorUserId(),
		Reason:         req.GetReason(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.SetUserLevelResponse{
		EventId:        result.EventID,
		Status:         result.Status,
		Track:          result.Track,
		PreviousLevel:  result.PreviousLevel,
		NewLevel:       result.NewLevel,
		TotalValue:     result.TotalValue,
		RewardJobCount: result.RewardJobCount,
		ServerTimeMs:   result.ServerTimeMS,
	}, nil
}

func (s *GrowthLevelServer) IssueRegistrationLevelBadges(ctx context.Context, req *activityv1.IssueRegistrationLevelBadgesRequest) (*activityv1.IssueRegistrationLevelBadgesResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	grants, err := s.svc.IssueRegistrationLevelBadges(ctx, req.GetUserId(), req.GetCommandId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.IssueRegistrationLevelBadgesResponse{
		Grants:       make([]*activityv1.RegistrationLevelBadgeGrant, 0, len(grants.Grants)),
		ServerTimeMs: grants.ServerTimeMS,
	}
	for _, item := range grants.Grants {
		resp.Grants = append(resp.Grants, registrationLevelBadgeGrantToProto(item))
	}
	return resp, nil
}

// AdminGrowthLevelServer 暴露后台等级配置入口，业务约束仍由 service/repository 校验。
type AdminGrowthLevelServer struct {
	activityv1.UnimplementedAdminGrowthLevelServiceServer

	svc *service.Service
}

func NewAdminGrowthLevelServer(svc *service.Service) *AdminGrowthLevelServer {
	return &AdminGrowthLevelServer{svc: svc}
}

func (s *AdminGrowthLevelServer) ListLevelConfig(ctx context.Context, req *activityv1.ListLevelConfigRequest) (*activityv1.ListLevelConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	config, err := s.svc.ListLevelConfig(ctx, domain.ConfigQuery{
		Track:  req.GetTrack(),
		Status: req.GetStatus(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListLevelConfigResponse{
		Tracks:       make([]*activityv1.LevelTrack, 0, len(config.Tracks)),
		Rules:        make([]*activityv1.LevelRule, 0, len(config.Rules)),
		Tiers:        make([]*activityv1.LevelTier, 0, len(config.Tiers)),
		ServerTimeMs: config.ServerTimeMS,
	}
	for _, track := range config.Tracks {
		resp.Tracks = append(resp.Tracks, levelTrackToProto(track))
	}
	for _, rule := range config.Rules {
		resp.Rules = append(resp.Rules, levelRuleToProto(rule))
	}
	for _, tier := range config.Tiers {
		resp.Tiers = append(resp.Tiers, levelTierToProto(tier))
	}
	return resp, nil
}

func (s *AdminGrowthLevelServer) BatchGetUserLevelAdminProfiles(ctx context.Context, req *activityv1.BatchGetUserLevelAdminProfilesRequest) (*activityv1.BatchGetUserLevelAdminProfilesResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	profiles, err := s.svc.BatchGetUserLevelAdminProfiles(ctx, req.GetUserIds())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.BatchGetUserLevelAdminProfilesResponse{Profiles: make([]*activityv1.AdminUserLevelProfile, 0, len(profiles.Profiles)), ServerTimeMs: profiles.ServerTimeMS}
	for _, profile := range profiles.Profiles {
		resp.Profiles = append(resp.Profiles, adminUserLevelProfileToProto(profile))
	}
	return resp, nil
}

func (s *AdminGrowthLevelServer) AdjustTemporaryUserLevels(ctx context.Context, req *activityv1.AdjustTemporaryUserLevelsRequest) (*activityv1.AdjustTemporaryUserLevelsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	adjustments := make([]domain.TemporaryLevelAdjustment, 0, len(req.GetAdjustments()))
	for _, item := range req.GetAdjustments() {
		adjustments = append(adjustments, domain.TemporaryLevelAdjustment{Track: item.GetTrack(), Level: item.GetLevel(), DurationDays: item.GetDurationDays()})
	}
	profile, err := s.svc.AdjustTemporaryUserLevels(ctx, domain.AdjustTemporaryLevelsCommand{
		CommandID: req.GetCommandId(), UserID: req.GetUserId(), Adjustments: adjustments,
		OperatorAdminID: req.GetOperatorAdminId(), Reason: req.GetReason(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.AdjustTemporaryUserLevelsResponse{Profile: adminUserLevelProfileToProto(profile), ServerTimeMs: profile.ServerTimeMS}, nil
}

func (s *AdminGrowthLevelServer) UpsertLevelTrack(ctx context.Context, req *activityv1.UpsertLevelTrackRequest) (*activityv1.UpsertLevelTrackResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	item, created, err := s.svc.UpsertLevelTrack(ctx, domain.TrackCommand{
		Track:             req.GetTrack(),
		Name:              req.GetName(),
		Status:            req.GetStatus(),
		SortOrder:         req.GetSortOrder(),
		DisplayConfigJSON: req.GetDisplayConfigJson(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpsertLevelTrackResponse{Track: levelTrackToProto(item), Created: created}, nil
}

func (s *AdminGrowthLevelServer) UpsertLevelRule(ctx context.Context, req *activityv1.UpsertLevelRuleRequest) (*activityv1.UpsertLevelRuleResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	item, created, err := s.svc.UpsertLevelRule(ctx, domain.RuleCommand{
		Track:                 req.GetTrack(),
		Level:                 req.GetLevel(),
		RequiredValue:         req.GetRequiredValue(),
		Name:                  req.GetName(),
		Status:                req.GetStatus(),
		RewardResourceGroupID: req.GetRewardResourceGroupId(),
		SortOrder:             req.GetSortOrder(),
		DisplayConfigJSON:     req.GetDisplayConfigJson(),
		OperatorAdminID:       req.GetOperatorAdminId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpsertLevelRuleResponse{Rule: levelRuleToProto(item), Created: created}, nil
}

func (s *AdminGrowthLevelServer) UpsertLevelTier(ctx context.Context, req *activityv1.UpsertLevelTierRequest) (*activityv1.UpsertLevelTierResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	item, created, err := s.svc.UpsertLevelTier(ctx, domain.TierCommand{
		TierID:                       req.GetTierId(),
		Track:                        req.GetTrack(),
		MinLevel:                     req.GetMinLevel(),
		MaxLevel:                     req.GetMaxLevel(),
		Name:                         req.GetName(),
		DisplayAvatarFrameResourceID: req.GetDisplayAvatarFrameResourceId(),
		DisplayBadgeResourceID:       req.GetDisplayBadgeResourceId(),
		RewardResourceGroupID:        req.GetRewardResourceGroupId(),
		Status:                       req.GetStatus(),
		DisplayConfigJSON:            req.GetDisplayConfigJson(),
		OperatorAdminID:              req.GetOperatorAdminId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpsertLevelTierResponse{Tier: levelTierToProto(item), Created: created}, nil
}

// BatchUpsertLevelConfig 将单次 HTTP 批量保存保持为一次 gRPC 和一次数据库事务。
func (s *AdminGrowthLevelServer) BatchUpsertLevelConfig(ctx context.Context, req *activityv1.BatchUpsertLevelConfigRequest) (*activityv1.BatchUpsertLevelConfigResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	command := domain.BatchConfigCommand{
		Rules:           make([]domain.RuleCommand, 0, len(req.GetRules())),
		Tiers:           make([]domain.TierCommand, 0, len(req.GetTiers())),
		OperatorAdminID: req.GetOperatorAdminId(),
	}
	for _, item := range req.GetRules() {
		command.Rules = append(command.Rules, domain.RuleCommand{Track: item.GetTrack(), Level: item.GetLevel(), RequiredValue: item.GetRequiredValue(), Name: item.GetName(), Status: item.GetStatus(), RewardResourceGroupID: item.GetRewardResourceGroupId(), SortOrder: item.GetSortOrder(), DisplayConfigJSON: item.GetDisplayConfigJson()})
	}
	for _, item := range req.GetTiers() {
		command.Tiers = append(command.Tiers, domain.TierCommand{TierID: item.GetTierId(), Track: item.GetTrack(), MinLevel: item.GetMinLevel(), MaxLevel: item.GetMaxLevel(), Name: item.GetName(), DisplayAvatarFrameResourceID: item.GetDisplayAvatarFrameResourceId(), DisplayBadgeResourceID: item.GetDisplayBadgeResourceId(), RewardResourceGroupID: item.GetRewardResourceGroupId(), Status: item.GetStatus(), DisplayConfigJSON: item.GetDisplayConfigJson()})
	}
	config, err := s.svc.BatchUpsertLevelConfig(ctx, command)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	response := &activityv1.BatchUpsertLevelConfigResponse{Rules: make([]*activityv1.LevelRule, 0, len(config.Rules)), Tiers: make([]*activityv1.LevelTier, 0, len(config.Tiers)), ServerTimeMs: config.ServerTimeMS}
	for _, item := range config.Rules {
		response.Rules = append(response.Rules, levelRuleToProto(item))
	}
	for _, item := range config.Tiers {
		response.Tiers = append(response.Tiers, levelTierToProto(item))
	}
	return response, nil
}

func levelTrackToProto(item domain.Track) *activityv1.LevelTrack {
	return &activityv1.LevelTrack{
		Track:             item.Track,
		Name:              item.Name,
		Status:            item.Status,
		SortOrder:         item.SortOrder,
		DisplayConfigJson: item.DisplayConfigJSON,
		CreatedAtMs:       item.CreatedAtMS,
		UpdatedAtMs:       item.UpdatedAtMS,
	}
}

func levelRuleToProto(item domain.Rule) *activityv1.LevelRule {
	return &activityv1.LevelRule{
		Track:                 item.Track,
		Level:                 item.Level,
		RequiredValue:         item.RequiredValue,
		Name:                  item.Name,
		Status:                item.Status,
		RewardResourceGroupId: item.RewardResourceGroupID,
		SortOrder:             item.SortOrder,
		DisplayConfigJson:     item.DisplayConfigJSON,
		CreatedByAdminId:      item.CreatedByAdminID,
		UpdatedByAdminId:      item.UpdatedByAdminID,
		CreatedAtMs:           item.CreatedAtMS,
		UpdatedAtMs:           item.UpdatedAtMS,
	}
}

func levelTierToProto(item domain.Tier) *activityv1.LevelTier {
	return &activityv1.LevelTier{
		TierId:                       item.TierID,
		Track:                        item.Track,
		MinLevel:                     item.MinLevel,
		MaxLevel:                     item.MaxLevel,
		Name:                         item.Name,
		DisplayAvatarFrameResourceId: item.DisplayAvatarFrameResourceID,
		DisplayBadgeResourceId:       item.DisplayBadgeResourceID,
		RewardResourceGroupId:        item.RewardResourceGroupID,
		Status:                       item.Status,
		DisplayConfigJson:            item.DisplayConfigJSON,
		CreatedByAdminId:             item.CreatedByAdminID,
		UpdatedByAdminId:             item.UpdatedByAdminID,
		CreatedAtMs:                  item.CreatedAtMS,
		UpdatedAtMs:                  item.UpdatedAtMS,
	}
}

func levelTrackOverviewToProto(item domain.TrackOverview) *activityv1.LevelTrackOverview {
	return &activityv1.LevelTrackOverview{
		Track:                        item.Track,
		Name:                         item.Name,
		Level:                        item.Level,
		TierId:                       item.TierID,
		TotalValue:                   item.TotalValue,
		CurrentLevelRequiredValue:    item.CurrentLevelRequiredValue,
		NextLevel:                    item.NextLevel,
		NextLevelRequiredValue:       item.NextLevelRequiredValue,
		DisplayAvatarFrameResourceId: item.DisplayAvatarFrameResourceID,
		DisplayBadgeResourceId:       item.DisplayBadgeResourceID,
		DisplayBadgeSourceLevel:      item.DisplayBadgeSourceLevel,
		RewardPendingCount:           item.RewardPendingCount,
		SortOrder:                    item.SortOrder,
	}
}

func userLevelDisplayProfileToProto(item domain.UserLevelDisplayProfile) *activityv1.UserLevelDisplayProfile {
	return &activityv1.UserLevelDisplayProfile{
		UserId: item.UserID,
		Wealth: levelDisplayTrackProfileToProto(item.Wealth),
		Game:   levelDisplayTrackProfileToProto(item.Game),
		Charm:  levelDisplayTrackProfileToProto(item.Charm),
	}
}

func levelDisplayTrackProfileToProto(item domain.LevelDisplayTrackProfile) *activityv1.LevelDisplayTrackProfile {
	return &activityv1.LevelDisplayTrackProfile{
		Track:                 item.Track,
		Level:                 item.Level,
		TierId:                item.TierID,
		AvatarFrameResourceId: item.AvatarFrameResourceID,
		BadgeResourceId:       item.BadgeResourceID,
		BadgeSourceLevel:      item.BadgeSourceLevel,
		UpdatedAtMs:           item.UpdatedAtMS,
	}
}

func levelRewardToProto(item domain.RewardJob) *activityv1.LevelRewardJob {
	return &activityv1.LevelRewardJob{
		RewardJobId:          item.RewardJobID,
		UserId:               item.UserID,
		Track:                item.Track,
		RewardSourceType:     item.RewardSourceType,
		RewardSourceId:       item.RewardSourceID,
		ResourceGroupId:      item.ResourceGroupID,
		WalletCommandId:      item.WalletCommandID,
		WalletGrantId:        item.WalletGrantID,
		Status:               item.Status,
		AttemptCount:         item.AttemptCount,
		FailureReason:        item.FailureReason,
		CreatedAtMs:          item.CreatedAtMS,
		UpdatedAtMs:          item.UpdatedAtMS,
		RewardOrigin:         item.RewardOrigin,
		TemporaryLevelId:     item.TemporaryLevelID,
		GrantGeneration:      item.GrantGeneration,
		RewardLevel:          item.RewardLevel,
		Permanent:            item.Permanent,
		TemporaryExpiresAtMs: item.TemporaryExpiresAtMS,
		RevokeStatus:         item.RevokeStatus,
	}
}

func registrationLevelBadgeGrantToProto(item domain.RegistrationLevelBadgeGrant) *activityv1.RegistrationLevelBadgeGrant {
	return &activityv1.RegistrationLevelBadgeGrant{
		Track:      item.Track,
		Level:      item.Level,
		ResourceId: item.ResourceID,
		GrantId:    item.GrantID,
	}
}

func adminUserLevelProfileToProto(item domain.AdminUserLevelProfile) *activityv1.AdminUserLevelProfile {
	result := &activityv1.AdminUserLevelProfile{UserId: item.UserID, Tracks: make([]*activityv1.AdminLevelTrackProfile, 0, len(item.Tracks))}
	for _, track := range item.Tracks {
		result.Tracks = append(result.Tracks, &activityv1.AdminLevelTrackProfile{
			Track: track.Track, RealLevel: track.RealLevel, RealTotalValue: track.RealTotalValue,
			DisplayLevel: track.DisplayLevel, DisplayValue: track.DisplayValue,
			TemporaryLevelId: track.TemporaryLevelID, TemporaryTargetLevel: track.TemporaryTargetLevel,
			StartedAtMs: track.StartedAtMS, ExpiresAtMs: track.ExpiresAtMS,
		})
	}
	return result
}
