package app

import (
	"errors"

	roomv1 "hyapp.local/api/proto/room/v1"
	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/pkg/tencentim"
	"hyapp/pkg/userleaderboard"
	activityclient "hyapp/services/activity-service/internal/client"
	"hyapp/services/activity-service/internal/config"
	achievementservice "hyapp/services/activity-service/internal/service/achievement"
	activityservice "hyapp/services/activity-service/internal/service/activity"
	activitytemplateservice "hyapp/services/activity-service/internal/service/activitytemplate"
	agencyopeningservice "hyapp/services/activity-service/internal/service/agencyopening"
	broadcastservice "hyapp/services/activity-service/internal/service/broadcast"
	cpweeklyrankservice "hyapp/services/activity-service/internal/service/cpweeklyrank"
	cumulativerechargeservice "hyapp/services/activity-service/internal/service/cumulativerecharge"
	firstrechargeservice "hyapp/services/activity-service/internal/service/firstrecharge"
	growthservice "hyapp/services/activity-service/internal/service/growth"
	inviteactivityservice "hyapp/services/activity-service/internal/service/inviteactivity"
	messageservice "hyapp/services/activity-service/internal/service/message"
	registrationrewardservice "hyapp/services/activity-service/internal/service/registrationreward"
	rewardnoticeservice "hyapp/services/activity-service/internal/service/rewardnotice"
	roomturnoverrewardservice "hyapp/services/activity-service/internal/service/roomturnoverreward"
	sevendaycheckinservice "hyapp/services/activity-service/internal/service/sevendaycheckin"
	taskservice "hyapp/services/activity-service/internal/service/task"
	weeklystarservice "hyapp/services/activity-service/internal/service/weeklystar"
	wheelservice "hyapp/services/activity-service/internal/service/wheel"
	mysqlstorage "hyapp/services/activity-service/internal/storage/mysql"
)

type serviceBundle struct {
	activity                    *activityservice.Service
	activityTemplate            *activitytemplateservice.Service
	message                     *messageservice.Service
	actionConfirm               *messageservice.ActionConfirmService
	task                        *taskservice.Service
	registrationReward          *registrationrewardservice.Service
	sevenDayCheckIn             *sevendaycheckinservice.Service
	growth                      *growthservice.Service
	achievement                 *achievementservice.Service
	agencyOpening               *agencyopeningservice.Service
	weeklyStar                  *weeklystarservice.Service
	cpWeeklyRank                *cpweeklyrankservice.Service
	roomTurnoverReward          *roomturnoverrewardservice.Service
	broadcast                   *broadcastservice.Service
	wheel                       *wheelservice.Service
	firstRechargeReward         *firstrechargeservice.Service
	cumulativeRecharge          *cumulativerechargeservice.Service
	inviteActivityReward        *inviteactivityservice.Service
	userLeaderboard             *userleaderboard.Store
	broadcastPublisherAvailable bool
}

func buildServiceBundle(cfg config.Config, repository *mysqlstorage.Repository, clients externalClients, userLeaderboardStore *userleaderboard.Store) (*serviceBundle, error) {
	walletClient := walletv1.NewWalletServiceClient(clients.walletConn)
	roomClient := roomv1.NewRoomQueryServiceClient(clients.roomConn)
	regionSource := activityclient.NewGRPCRegionSource(clients.userConn)

	activitySvc := activityservice.New(activityservice.Config{NodeID: cfg.NodeID}, repository)
	activityTemplateSvc := activitytemplateservice.New(repository, walletClient, regionSource)
	messageSvc := messageservice.New(messageservice.Config{NodeID: cfg.NodeID}, repository, messageservice.WithTargetSource(activityclient.NewGRPCUserTargetSource(clients.userConn)))
	// 所有活动奖励消息统一从真实 wallet grant/coin 回执生成；message service 仍是唯一 inbox owner。
	rewardNoticeSvc := rewardnoticeservice.New(messageSvc, walletClient)
	activityTemplateSvc.BindNotice(messageSvc)
	actionConfirmSvc := messageservice.NewActionConfirm(messageservice.Config{
		NodeID:   cfg.NodeID,
		AppCodes: cfg.MessageActionConfirmWorker.AppCodes,
	}, repository, activityclient.NewGRPCRoleInvitationClient(clients.userConn))
	taskSvc := taskservice.New(repository, walletClient)
	taskSvc.BindRewardNotice(rewardNoticeSvc)
	registrationRewardSvc := registrationrewardservice.New(repository, walletClient)
	registrationRewardSvc.BindRewardNotice(rewardNoticeSvc)
	sevenDayCheckInSvc := sevendaycheckinservice.New(repository, walletClient)
	sevenDayCheckInSvc.BindRewardNotice(rewardNoticeSvc)
	growthOptions := []growthservice.Option{growthservice.WithNoticeService(messageSvc)}
	if userLeaderboardStore != nil {
		// 复用游戏等级已接收的 game_spend_coin 事实写榜，避免再建一条会漂移口径的游戏消费链路。
		growthOptions = append(growthOptions, growthservice.WithGameLeaderboard(userLeaderboardStore))
	}
	growthSvc := growthservice.New(repository, walletClient, growthOptions...)
	achievementSvc := achievementservice.New(repository, walletClient)
	agencyOpeningSvc := agencyopeningservice.New(repository, walletClient, activityclient.NewGRPCAgencyOpeningSource(clients.userConn), roomClient)
	weeklyStarSvc := weeklystarservice.New(repository, walletClient)
	weeklyStarSvc.BindRewardNotice(rewardNoticeSvc)
	cpWeeklyRankSvc := cpweeklyrankservice.New(repository, activityclient.NewGRPCCPWeeklyRankSource(clients.userConn), walletClient)
	cpWeeklyRankSvc.BindRewardNotice(rewardNoticeSvc)
	roomTurnoverRewardSvc := roomturnoverrewardservice.New(repository, walletClient, roomClient, roomturnoverrewardservice.WithRewardNotice(rewardNoticeSvc))

	// Tencent IM 是区域广播、房间礼物播报和红包播报的外部发布器。
	// 如果配置要求启动广播 worker，就必须先确认 publisher 可用，避免 worker 启动后只写 outbox 不发消息。
	var broadcastPublisher broadcastservice.Publisher
	if cfg.TencentIM.Enabled {
		client, err := tencentim.NewRESTClient(cfg.TencentIM.RESTConfig())
		if err != nil {
			return nil, err
		}
		broadcastPublisher = client
	}
	if cfg.Broadcast.Enabled && broadcastPublisher == nil {
		return nil, errors.New("broadcast worker requires tencent_im.enabled")
	}

	broadcastSvc := broadcastservice.New(broadcastservice.Config{
		NodeID:                cfg.NodeID,
		AppCodes:              cfg.Broadcast.AppCodes,
		SuperGiftMinValue:     cfg.Broadcast.SuperGiftMinValue,
		GameWinMinCoins:       cfg.Broadcast.GameWinMinCoins,
		GameWinConfigCacheTTL: cfg.Broadcast.GameWinConfigCacheTTL,
		WorkerBatchSize:       cfg.Broadcast.WorkerBatchSize,
		WorkerLockTTL:         cfg.Broadcast.WorkerLockTTL,
		WorkerMaxRetry:        cfg.Broadcast.WorkerMaxRetry,
		WorkerPollInterval:    cfg.Broadcast.WorkerPollInterval,
		EnsureGroupsOnStartup: cfg.Broadcast.EnsureGroupsOnStartup,
		EnsureGroupsInterval:  cfg.Broadcast.EnsureGroupsInterval,
		GroupIDPrefix:         cfg.TencentIM.GroupIDPrefix,
	}, repository, broadcastPublisher, regionSource)
	userProfileSource := activityclient.NewGRPCUserProfileSource(clients.userConn)
	broadcastSvc.SetSenderProfileSource(userProfileSource)
	broadcastSvc.SetRoomSnapshotSource(activityclient.NewGRPCRoomBroadcastSource(clients.roomConn))
	broadcastSvc.SetRewardNoticeNotifier(rewardNoticeSvc)
	wheelSvc := wheelservice.New(repository, wheelservice.WithWallet(walletClient))
	firstRechargeRewardSvc := firstrechargeservice.New(repository, walletClient)
	firstRechargeRewardSvc.BindRewardNotice(rewardNoticeSvc)
	cumulativeRechargeSvc := cumulativerechargeservice.New(repository, walletClient)
	cumulativeRechargeSvc.BindRewardNotice(rewardNoticeSvc)
	inviteActivityRewardSvc := inviteactivityservice.New(repository, walletClient, activityclient.NewGRPCInviteAttributionSource(clients.userConn))

	return &serviceBundle{
		activity:                    activitySvc,
		activityTemplate:            activityTemplateSvc,
		message:                     messageSvc,
		actionConfirm:               actionConfirmSvc,
		task:                        taskSvc,
		registrationReward:          registrationRewardSvc,
		sevenDayCheckIn:             sevenDayCheckInSvc,
		growth:                      growthSvc,
		achievement:                 achievementSvc,
		agencyOpening:               agencyOpeningSvc,
		weeklyStar:                  weeklyStarSvc,
		cpWeeklyRank:                cpWeeklyRankSvc,
		roomTurnoverReward:          roomTurnoverRewardSvc,
		broadcast:                   broadcastSvc,
		wheel:                       wheelSvc,
		firstRechargeReward:         firstRechargeRewardSvc,
		cumulativeRecharge:          cumulativeRechargeSvc,
		inviteActivityReward:        inviteActivityRewardSvc,
		userLeaderboard:             userLeaderboardStore,
		broadcastPublisherAvailable: broadcastPublisher != nil,
	}, nil
}
