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"
	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"
	luckygiftservice "hyapp/services/activity-service/internal/service/luckygift"
	messageservice "hyapp/services/activity-service/internal/service/message"
	registrationrewardservice "hyapp/services/activity-service/internal/service/registrationreward"
	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
	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
	luckyGift                   *luckygiftservice.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)

	activitySvc := activityservice.New(activityservice.Config{NodeID: cfg.NodeID}, repository)
	messageSvc := messageservice.New(messageservice.Config{NodeID: cfg.NodeID}, repository, messageservice.WithTargetSource(activityclient.NewGRPCUserTargetSource(clients.userConn)))
	actionConfirmSvc := messageservice.NewActionConfirm(messageservice.Config{NodeID: cfg.NodeID}, repository, activityclient.NewGRPCRoleInvitationClient(clients.userConn))
	taskSvc := taskservice.New(repository, walletClient)
	registrationRewardSvc := registrationrewardservice.New(repository, walletClient)
	sevenDayCheckInSvc := sevendaycheckinservice.New(repository, walletClient)
	growthSvc := growthservice.New(repository, walletClient)
	achievementSvc := achievementservice.New(repository, walletClient)
	agencyOpeningSvc := agencyopeningservice.New(repository, walletClient, activityclient.NewGRPCAgencyOpeningSource(clients.userConn), roomClient)
	weeklyStarSvc := weeklystarservice.New(repository, walletClient)
	cpWeeklyRankSvc := cpweeklyrankservice.New(repository, activityclient.NewGRPCCPWeeklyRankSource(clients.userConn), walletClient)
	roomTurnoverRewardSvc := roomturnoverrewardservice.New(repository, walletClient, roomClient, roomturnoverrewardservice.WithNoticeService(messageSvc))

	// Tencent IM 是区域广播、房间礼物播报和红包播报的外部发布器。
	// 如果配置要求启动广播 worker，就必须先确认 publisher 可用，避免 worker 启动后只写 outbox 不发消息。
	var tencentClient *tencentim.RESTClient
	var broadcastPublisher broadcastservice.Publisher
	if cfg.TencentIM.Enabled {
		client, err := tencentim.NewRESTClient(cfg.TencentIM.RESTConfig())
		if err != nil {
			return nil, err
		}
		tencentClient = client
		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,
		SuperGiftMinValue:     cfg.Broadcast.SuperGiftMinValue,
		WorkerBatchSize:       cfg.Broadcast.WorkerBatchSize,
		WorkerLockTTL:         cfg.Broadcast.WorkerLockTTL,
		WorkerMaxRetry:        cfg.Broadcast.WorkerMaxRetry,
		WorkerPollInterval:    cfg.Broadcast.WorkerPollInterval,
		EnsureGroupsOnStartup: cfg.Broadcast.EnsureGroupsOnStartup,
		GroupIDPrefix:         cfg.TencentIM.GroupIDPrefix,
	}, repository, broadcastPublisher, activityclient.NewGRPCRegionSource(clients.userConn))
	userProfileSource := activityclient.NewGRPCUserProfileSource(clients.userConn)
	broadcastSvc.SetSenderProfileSource(userProfileSource)

	luckyGiftOptions := []luckygiftservice.Option{
		luckygiftservice.WithWallet(walletClient),
		luckygiftservice.WithRegionBroadcaster(broadcastSvc),
		luckygiftservice.WithSenderProfileSource(userProfileSource),
	}
	if tencentClient != nil {
		luckyGiftOptions = append(luckyGiftOptions,
			luckygiftservice.WithRoomPublisher(tencentClient),
			luckygiftservice.WithUserPublisher(tencentClient),
		)
	}
	luckyGiftSvc := luckygiftservice.New(repository, luckyGiftOptions...)
	wheelSvc := wheelservice.New(repository, wheelservice.WithWallet(walletClient))
	firstRechargeRewardSvc := firstrechargeservice.New(repository, walletClient)
	cumulativeRechargeSvc := cumulativerechargeservice.New(repository, walletClient)
	inviteActivityRewardSvc := inviteactivityservice.New(repository, walletClient, activityclient.NewGRPCInviteAttributionSource(clients.userConn))

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