package quota

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

	"dudu/server/internal/store"
)

// InvalidateBalance 删除 Redis 余额键 → 下次读取从 DB 冗余列懒加载（购买入账后调用）。
func (m *Manager) InvalidateBalance(ctx context.Context, uid string) error {
	return m.rdb.Del(ctx, store.KeyQuotaBalance(uid)).Err()
}

// Reconcile 对账：users.balance_seconds 必须等于 SUM(balance_ledger.delta_seconds)。
// fix=true 时以 ledger 为准修正冗余列并失效 Redis 键。返回发现的偏差数。
func (m *Manager) Reconcile(ctx context.Context, fix bool) (int, error) {
	type row struct {
		ID      string
		Balance int64
		Ledger  int64
	}
	var rows []row
	err := m.db.WithContext(ctx).Raw(`
		SELECT u.id, u.balance_seconds AS balance, COALESCE(SUM(l.delta_seconds), 0) AS ledger
		FROM users u LEFT JOIN balance_ledgers l ON l.user_id = u.id
		GROUP BY u.id, u.balance_seconds
		HAVING u.balance_seconds <> COALESCE(SUM(l.delta_seconds), 0)`).Scan(&rows).Error
	if err != nil {
		return 0, err
	}
	for _, r := range rows {
		slog.Error("balance drift detected", "user", r.ID, "redundant", r.Balance, "ledger", r.Ledger)
		if fix {
			if err := m.db.WithContext(ctx).Model(&store.User{}).
				Where("id = ?", r.ID).UpdateColumn("balance_seconds", r.Ledger).Error; err != nil {
				return len(rows), err
			}
			_ = m.InvalidateBalance(ctx, r.ID)
		}
	}
	return len(rows), nil
}

// StartDailyReconcile 每日对账任务（凌晨 4 点 CST）。
func (m *Manager) StartDailyReconcile(ctx context.Context) {
	go func() {
		for {
			now := time.Now().In(time.FixedZone("CST", 8*3600))
			next := time.Date(now.Year(), now.Month(), now.Day(), 4, 0, 0, 0, now.Location())
			if !next.After(now) {
				next = next.Add(24 * time.Hour)
			}
			select {
			case <-ctx.Done():
				return
			case <-time.After(time.Until(next)):
				if n, err := m.Reconcile(ctx, true); err != nil {
					slog.Error("reconcile failed", "err", err)
				} else if n > 0 {
					slog.Warn("reconcile fixed drifts", "count", n)
				}
			}
		}
	}()
}
