package teamsalarysettlement

import (
	"context"
	"log/slog"
	"strings"
	"sync"
	"time"
)

const autoRunnerInterval = time.Hour

// AutoRunner 是 admin-server 内的轻量自动结算循环；真正的幂等不靠 runner，而靠 wallet 结算进度表。
type AutoRunner struct {
	service *Service
	appCode string
	worker  string
	cancel  context.CancelFunc
	wg      sync.WaitGroup
}

// NewAutoRunner 只保存运行参数，不立刻启动 goroutine，便于 main 按 jobs.enabled 控制生命周期。
func NewAutoRunner(service *Service, appCode string, worker string) *AutoRunner {
	return &AutoRunner{
		service: service,
		appCode: normalizeAppCode(appCode),
		worker:  strings.TrimSpace(worker),
	}
}

// Start 启动后台循环；重复调用不创建第二个 worker，避免同进程内重复扫描。
func (r *AutoRunner) Start() {
	if r == nil || r.service == nil || r.cancel != nil {
		return
	}
	ctx, cancel := context.WithCancel(context.Background())
	r.cancel = cancel
	r.wg.Add(1)
	go r.loop(ctx)
}

// Close 停止后台循环并等待退出，保证 admin-server 优雅关闭时不会截断正在执行的事务。
func (r *AutoRunner) Close() {
	if r == nil || r.cancel == nil {
		return
	}
	r.cancel()
	r.wg.Wait()
	r.cancel = nil
}

// loop 启动后先立即检查一次，再按小时检查；是否真正发薪由 RunAutomaticDue 按日期判断。
func (r *AutoRunner) loop(ctx context.Context) {
	defer r.wg.Done()
	r.runOnce(ctx, time.Now())
	ticker := time.NewTicker(autoRunnerInterval)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case now := <-ticker.C:
			r.runOnce(ctx, now)
		}
	}
}

// runOnce 给单次自动结算设置超时，避免数据库异常时 goroutine 永久卡住。
func (r *AutoRunner) runOnce(parent context.Context, now time.Time) {
	ctx, cancel := context.WithTimeout(parent, 10*time.Minute)
	defer cancel()
	// 多节点同时触发时不单独抢锁；结算进度行和 command_id 唯一索引会保证同一周期同一用户只发一次。
	result, err := r.service.RunAutomaticDue(ctx, r.appCode, now)
	if err != nil {
		slog.Error("team_salary_auto_settlement_failed", "worker", r.worker, "error", err)
		return
	}
	if !result.Due {
		return
	}
	slog.Info("team_salary_auto_settlement_completed",
		"worker", r.worker,
		"cycle_key", result.CycleKey,
		"bd_success", result.Results[policyTypeBD].SuccessCount,
		"bd_skipped", result.Results[policyTypeBD].SkippedCount,
		"admin_success", result.Results[policyTypeAdmin].SuccessCount,
		"admin_skipped", result.Results[policyTypeAdmin].SkippedCount,
	)
}

// normalizeAppCode 兜底默认应用编码，保证自动任务和后台手动入口都使用同一 app 维度。
func normalizeAppCode(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	if value == "" {
		return "lalu"
	}
	return value
}
