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"
)

// AdminActivityTemplateServer 只把 protobuf 转为领域命令；发布规则、版本和事务都由 service/repository 拥有。
type AdminActivityTemplateServer struct {
	activityv1.UnimplementedAdminActivityTemplateServiceServer
	svc *service.Service
}

func NewAdminActivityTemplateServer(svc *service.Service) *AdminActivityTemplateServer {
	return &AdminActivityTemplateServer{svc: svc}
}

func (s *AdminActivityTemplateServer) ListActivityTemplates(ctx context.Context, req *activityv1.ListActivityTemplatesRequest) (*activityv1.ListActivityTemplatesResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	query := domain.ListQuery{
		Keyword:  req.GetKeyword(),
		Status:   req.GetStatus(),
		StartMS:  req.GetStartMs(),
		EndMS:    req.GetEndMs(),
		Page:     req.GetPage(),
		PageSize: req.GetPageSize(),
	}
	if req.RegionId != nil {
		value := req.GetRegionId()
		query.RegionID = &value
	}
	if req.AllRegions != nil {
		value := req.GetAllRegions()
		query.AllRegions = &value
	}
	items, total, err := s.svc.List(ctx, query)
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListActivityTemplatesResponse{
		Templates:    make([]*activityv1.ActivityTemplateSummary, 0, len(items)),
		Total:        total,
		ServerTimeMs: time.Now().UTC().UnixMilli(),
	}
	for _, item := range items {
		resp.Templates = append(resp.Templates, activityTemplateSummaryToProto(item))
	}
	return resp, nil
}

func (s *AdminActivityTemplateServer) CreateActivityTemplate(ctx context.Context, req *activityv1.CreateActivityTemplateRequest) (*activityv1.CreateActivityTemplateResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	item, err := s.svc.Create(ctx, activityTemplateFromProto(req.GetTemplate()), req.GetOperatorAdminId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.CreateActivityTemplateResponse{Template: activityTemplateToProto(item)}, nil
}

func (s *AdminActivityTemplateServer) GetActivityTemplate(ctx context.Context, req *activityv1.GetActivityTemplateRequest) (*activityv1.GetActivityTemplateResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	item, err := s.svc.Get(ctx, req.GetTemplateId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.GetActivityTemplateResponse{Template: activityTemplateToProto(item)}, nil
}

func (s *AdminActivityTemplateServer) UpdateActivityTemplate(ctx context.Context, req *activityv1.UpdateActivityTemplateRequest) (*activityv1.UpdateActivityTemplateResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	item, err := s.svc.Update(ctx, domain.UpdateCommand{
		Template:         activityTemplateFromProto(req.GetTemplate()),
		ExpectedRevision: req.GetExpectedRevision(),
		OperatorAdminID:  req.GetOperatorAdminId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.UpdateActivityTemplateResponse{Template: activityTemplateToProto(item)}, nil
}

func (s *AdminActivityTemplateServer) SetActivityTemplateStatus(ctx context.Context, req *activityv1.SetActivityTemplateStatusRequest) (*activityv1.SetActivityTemplateStatusResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	item, err := s.svc.SetStatus(ctx, domain.StatusCommand{
		TemplateID:       req.GetTemplateId(),
		Status:           req.GetStatus(),
		ExpectedRevision: req.GetExpectedRevision(),
		OperatorAdminID:  req.GetOperatorAdminId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.SetActivityTemplateStatusResponse{Template: activityTemplateToProto(item)}, nil
}

func (s *AdminActivityTemplateServer) DeleteActivityTemplate(ctx context.Context, req *activityv1.DeleteActivityTemplateRequest) (*activityv1.DeleteActivityTemplateResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	item, archived, err := s.svc.Delete(ctx, domain.StatusCommand{
		TemplateID:       req.GetTemplateId(),
		ExpectedRevision: req.GetExpectedRevision(),
		OperatorAdminID:  req.GetOperatorAdminId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.DeleteActivityTemplateResponse{Template: activityTemplateToProto(item), Archived: archived}, nil
}

func (s *AdminActivityTemplateServer) ListActivityTemplateVersions(ctx context.Context, req *activityv1.ListActivityTemplateVersionsRequest) (*activityv1.ListActivityTemplateVersionsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	items, total, err := s.svc.ListVersions(ctx, req.GetTemplateId(), req.GetPage(), req.GetPageSize())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ListActivityTemplateVersionsResponse{Versions: make([]*activityv1.ActivityTemplateVersion, 0, len(items)), Total: total}
	for _, item := range items {
		resp.Versions = append(resp.Versions, &activityv1.ActivityTemplateVersion{
			TemplateId:         item.TemplateID,
			VersionNo:          item.VersionNo,
			Snapshot:           activityTemplateToProto(item.Snapshot),
			PublishedByAdminId: item.PublishedByAdminID,
			PublishedAtMs:      item.PublishedAtMS,
			RuntimeFromMs:      item.RuntimeFromMS,
			RuntimeToMs:        item.RuntimeToMS,
		})
	}
	return resp, nil
}

func (s *AdminActivityTemplateServer) CloneActivityTemplate(ctx context.Context, req *activityv1.CloneActivityTemplateRequest) (*activityv1.CloneActivityTemplateResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	item, err := s.svc.Clone(ctx, domain.CloneCommand{
		SourceTemplateID: req.GetSourceTemplateId(),
		SourceVersion:    req.GetSourceVersion(),
		TemplateCode:     req.GetTemplateCode(),
		Name:             req.GetName(),
		OperatorAdminID:  req.GetOperatorAdminId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &activityv1.CloneActivityTemplateResponse{Template: activityTemplateToProto(item)}, nil
}

func (s *AdminActivityTemplateServer) ValidateActivityTemplate(ctx context.Context, req *activityv1.ValidateActivityTemplateRequest) (*activityv1.ValidateActivityTemplateResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
	issues, err := s.svc.Validate(ctx, activityTemplateFromProto(req.GetTemplate()), req.GetForPublish())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &activityv1.ValidateActivityTemplateResponse{Valid: len(issues) == 0, Issues: make([]*activityv1.ActivityTemplateValidationIssue, 0, len(issues))}
	for _, issue := range issues {
		resp.Issues = append(resp.Issues, &activityv1.ActivityTemplateValidationIssue{Field: issue.Field, Code: issue.Code, Message: issue.Message})
	}
	return resp, nil
}

func activityTemplateFromProto(item *activityv1.ActivityTemplate) domain.Template {
	if item == nil {
		return domain.Template{}
	}
	out := domain.Template{
		AppCode:              item.GetAppCode(),
		TemplateID:           item.GetTemplateId(),
		TemplateCode:         item.GetTemplateCode(),
		Name:                 item.GetName(),
		ActivityType:         item.GetActivityType(),
		Status:               item.GetStatus(),
		LifecycleStatus:      item.GetLifecycleStatus(),
		StartMS:              item.GetStartMs(),
		EndMS:                item.GetEndMs(),
		AllRegions:           item.GetAllRegions(),
		RegionIDs:            append([]int64(nil), item.GetRegionIds()...),
		DailyLeaderboardSize: item.GetDailyLeaderboardSize(),
		TotalLeaderboardSize: item.GetTotalLeaderboardSize(),
		DisplayConfigJSON:    item.GetDisplayConfigJson(),
		Revision:             item.GetRevision(),
		PublishedVersion:     item.GetPublishedVersion(),
		CreatedByAdminID:     item.GetCreatedByAdminId(),
		UpdatedByAdminID:     item.GetUpdatedByAdminId(),
		PublishedByAdminID:   item.GetPublishedByAdminId(),
		CreatedAtMS:          item.GetCreatedAtMs(),
		UpdatedAtMS:          item.GetUpdatedAtMs(),
		PublishedAtMS:        item.GetPublishedAtMs(),
		ArchivedAtMS:         item.GetArchivedAtMs(),
	}
	for _, locale := range item.GetLocales() {
		out.Locales = append(out.Locales, domain.Locale{Locale: locale.GetLocale(), Title: locale.GetTitle(), Rules: locale.GetRules()})
	}
	for _, gift := range item.GetGifts() {
		out.Gifts = append(out.Gifts, domain.Gift{
			GiftID: gift.GetGiftId(), SortOrder: gift.GetSortOrder(), Name: gift.GetName(), IconURL: gift.GetIconUrl(),
			CoinPrice: gift.GetCoinPrice(), PriceVersion: gift.GetPriceVersion(),
		})
	}
	for _, task := range item.GetTasks() {
		out.Tasks = append(out.Tasks, domain.Task{
			TaskKey: task.GetTaskKey(), TaskType: task.GetTaskType(), TargetValue: task.GetTargetValue(),
			RewardResourceGroupID: task.GetRewardResourceGroupId(), SortOrder: task.GetSortOrder(),
			RewardSnapshot: activityTemplateRewardSnapshotFromProto(task.GetRewardSnapshotId(), task.GetRewardSnapshotHash(), task.GetRewardName(), task.GetRewardItems()),
		})
	}
	for _, reward := range item.GetRankRewards() {
		out.RankRewards = append(out.RankRewards, domain.RankReward{
			BoardType: reward.GetBoardType(), RankFrom: reward.GetRankFrom(), RankTo: reward.GetRankTo(),
			ResourceGroupID: reward.GetResourceGroupId(), SortOrder: reward.GetSortOrder(),
			RewardSnapshot: activityTemplateRewardSnapshotFromProto(reward.GetRewardSnapshotId(), reward.GetRewardSnapshotHash(), reward.GetRewardName(), reward.GetRewardItems()),
		})
	}
	for _, asset := range item.GetAssets() {
		out.Assets = append(out.Assets, domain.Asset{
			AssetKey: asset.GetAssetKey(), Locale: asset.GetLocale(), URL: asset.GetUrl(),
			MediaType: asset.GetMediaType(), SortOrder: asset.GetSortOrder(),
		})
	}
	return out
}

func activityTemplateToProto(item domain.Template) *activityv1.ActivityTemplate {
	if item.TemplateID == "" && item.TemplateCode == "" && item.Name == "" {
		return nil
	}
	out := &activityv1.ActivityTemplate{
		AppCode:              item.AppCode,
		TemplateId:           item.TemplateID,
		TemplateCode:         item.TemplateCode,
		Name:                 item.Name,
		ActivityType:         item.ActivityType,
		Status:               item.Status,
		LifecycleStatus:      item.LifecycleStatus,
		StartMs:              item.StartMS,
		EndMs:                item.EndMS,
		AllRegions:           item.AllRegions,
		RegionIds:            append([]int64(nil), item.RegionIDs...),
		Locales:              make([]*activityv1.ActivityTemplateLocale, 0, len(item.Locales)),
		Gifts:                make([]*activityv1.ActivityTemplateGift, 0, len(item.Gifts)),
		Tasks:                make([]*activityv1.ActivityTemplateTask, 0, len(item.Tasks)),
		DailyLeaderboardSize: item.DailyLeaderboardSize,
		TotalLeaderboardSize: item.TotalLeaderboardSize,
		RankRewards:          make([]*activityv1.ActivityTemplateRankReward, 0, len(item.RankRewards)),
		Assets:               make([]*activityv1.ActivityTemplateAsset, 0, len(item.Assets)),
		DisplayConfigJson:    item.DisplayConfigJSON,
		Revision:             item.Revision,
		PublishedVersion:     item.PublishedVersion,
		CreatedByAdminId:     item.CreatedByAdminID,
		UpdatedByAdminId:     item.UpdatedByAdminID,
		PublishedByAdminId:   item.PublishedByAdminID,
		CreatedAtMs:          item.CreatedAtMS,
		UpdatedAtMs:          item.UpdatedAtMS,
		PublishedAtMs:        item.PublishedAtMS,
		ArchivedAtMs:         item.ArchivedAtMS,
	}
	for _, locale := range item.Locales {
		out.Locales = append(out.Locales, &activityv1.ActivityTemplateLocale{Locale: locale.Locale, Title: locale.Title, Rules: locale.Rules})
	}
	for _, gift := range item.Gifts {
		out.Gifts = append(out.Gifts, &activityv1.ActivityTemplateGift{
			GiftId: gift.GiftID, SortOrder: gift.SortOrder, Name: gift.Name, IconUrl: gift.IconURL,
			CoinPrice: gift.CoinPrice, PriceVersion: gift.PriceVersion,
		})
	}
	for _, task := range item.Tasks {
		out.Tasks = append(out.Tasks, &activityv1.ActivityTemplateTask{
			TaskKey: task.TaskKey, TaskType: task.TaskType, TargetValue: task.TargetValue,
			RewardResourceGroupId: task.RewardResourceGroupID, SortOrder: task.SortOrder,
			RewardSnapshotId: task.RewardSnapshot.SnapshotID, RewardSnapshotHash: task.RewardSnapshot.SnapshotHash,
			RewardName: task.RewardSnapshot.Name, RewardItems: activityTemplateRewardItemsToProto(task.RewardSnapshot.Items),
		})
	}
	for _, reward := range item.RankRewards {
		out.RankRewards = append(out.RankRewards, &activityv1.ActivityTemplateRankReward{
			BoardType: reward.BoardType, RankFrom: reward.RankFrom, RankTo: reward.RankTo,
			ResourceGroupId: reward.ResourceGroupID, SortOrder: reward.SortOrder,
			RewardSnapshotId: reward.RewardSnapshot.SnapshotID, RewardSnapshotHash: reward.RewardSnapshot.SnapshotHash,
			RewardName: reward.RewardSnapshot.Name, RewardItems: activityTemplateRewardItemsToProto(reward.RewardSnapshot.Items),
		})
	}
	for _, asset := range item.Assets {
		out.Assets = append(out.Assets, &activityv1.ActivityTemplateAsset{
			AssetKey: asset.AssetKey, Locale: asset.Locale, Url: asset.URL, MediaType: asset.MediaType, SortOrder: asset.SortOrder,
		})
	}
	return out
}

func activityTemplateRewardItemsToProto(items []domain.RewardItem) []*activityv1.ActivityTemplateRewardItem {
	out := make([]*activityv1.ActivityTemplateRewardItem, 0, len(items))
	for _, item := range items {
		out = append(out, &activityv1.ActivityTemplateRewardItem{
			ItemType: item.ItemType, ResourceId: item.ResourceID, ResourceType: item.ResourceType,
			Name: item.Name, IconUrl: item.IconURL, Quantity: item.Quantity, DurationMs: item.DurationMS,
			WalletAssetType: item.WalletAssetType, WalletAssetAmount: item.WalletAssetAmount, SortOrder: item.SortOrder,
		})
	}
	return out
}

func activityTemplateRewardSnapshotFromProto(snapshotID, snapshotHash, name string, items []*activityv1.ActivityTemplateRewardItem) domain.RewardSnapshot {
	out := domain.RewardSnapshot{SnapshotID: snapshotID, SnapshotHash: snapshotHash, Name: name, Items: make([]domain.RewardItem, 0, len(items))}
	for _, item := range items {
		out.Items = append(out.Items, domain.RewardItem{
			ItemType: item.GetItemType(), ResourceID: item.GetResourceId(), ResourceType: item.GetResourceType(),
			Name: item.GetName(), IconURL: item.GetIconUrl(), Quantity: item.GetQuantity(), DurationMS: item.GetDurationMs(),
			WalletAssetType: item.GetWalletAssetType(), WalletAssetAmount: item.GetWalletAssetAmount(), SortOrder: item.GetSortOrder(),
		})
	}
	return out
}

func activityTemplateSummaryToProto(item domain.Summary) *activityv1.ActivityTemplateSummary {
	return &activityv1.ActivityTemplateSummary{
		AppCode: item.AppCode, TemplateId: item.TemplateID, TemplateCode: item.TemplateCode, Name: item.Name,
		ActivityType: item.ActivityType, Status: item.Status, LifecycleStatus: item.LifecycleStatus,
		StartMs: item.StartMS, EndMs: item.EndMS, AllRegions: item.AllRegions, RegionIds: append([]int64(nil), item.RegionIDs...),
		Revision: item.Revision, PublishedVersion: item.PublishedVersion,
		CreatedByAdminId: item.CreatedByAdminID, UpdatedByAdminId: item.UpdatedByAdminID,
		CreatedAtMs: item.CreatedAtMS, UpdatedAtMs: item.UpdatedAtMS,
	}
}
