package broadcast

import (
	"context"
	"log/slog"
	"time"

	"hyapp/pkg/appcode"
	"hyapp/pkg/logx"
)

// RunWorker 先按需补齐播报群，再持续拉取持久化 outbox 投递腾讯 IM。
// 该 worker 只在 broadcast.enabled 且 tencent_im.enabled 时启动，本地默认关闭，防止开发环境误发真实 IM。
func (s *Service) RunWorker(ctx context.Context, options WorkerOptions) {
	options = normalizeWorkerOptions(options, s.cfg)
	if s.cfg.EnsureGroupsOnStartup {
		// 建群失败不退出进程：下一轮部署/定时任务可继续补偿，已经入队的消息仍保留在 MySQL。
		s.reconcileBroadcastGroups(ctx, options)
	}
	ticker := time.NewTicker(options.PollInterval)
	defer ticker.Stop()
	lastReconcile := s.now()

	for {
		// 定时 reconcile 兜底新区域上线和启动时腾讯 REST 抖动：只跑一次的启动补偿失败后，
		// 缺失的区域群会让该区域全部播报静默 failed 沉底,不能等下一次部署才补群。
		if s.cfg.EnsureGroupsInterval > 0 && s.now().Sub(lastReconcile) >= s.cfg.EnsureGroupsInterval {
			lastReconcile = s.now()
			s.reconcileBroadcastGroups(ctx, options)
		}
		if _, err := s.ProcessPendingBroadcasts(ctx, options); err != nil && ctx.Err() == nil {
			// 批处理失败只影响当前轮询，outbox 记录没有被删除，下一轮会按锁和重试状态重新处理。
			logx.Error(ctx, "broadcast_outbox_batch_failed", err, slog.String("worker_id", options.WorkerID))
		}
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
		}
	}
}

func (s *Service) reconcileBroadcastGroups(ctx context.Context, options WorkerOptions) {
	appCodes, err := s.broadcastOutboxAppCodes(ctx)
	if err != nil {
		if ctx.Err() == nil {
			logx.Error(ctx, "broadcast_group_reconcile_apps_failed", err, slog.String("worker_id", options.WorkerID))
		}
		return
	}
	// 没有历史 outbox 的当前 App 仍需要全局/区域群；其他 App 从实际 outbox 租户键发现，避免后台 worker 永远只维护默认 lalu。
	currentApp := appcode.FromContext(ctx)
	foundCurrent := false
	for _, appCode := range appCodes {
		if appCode == currentApp {
			foundCurrent = true
			break
		}
	}
	if !foundCurrent {
		appCodes = append(appCodes, currentApp)
	}
	for _, appCode := range appCodes {
		appCtx := appcode.WithContext(ctx, appCode)
		if _, err := s.EnsureBroadcastGroups(appCtx); err != nil && ctx.Err() == nil {
			logx.Error(appCtx, "broadcast_group_reconcile_failed", err, slog.String("worker_id", options.WorkerID), slog.String("app_code", appCode))
		}
	}
}
