package usecase

import (
	"context"
	"errors"
	"fmt"

	"apps/backend/internal/module/radar/domain"
)

/*
PlanQuota 提供會員方案的雷達上限（spec §4.9）。

介面留在 radar module 是為了不讓 radar 依賴 usage module；正式環境的實作在
internal/svc 橋接既有方案定義，測試直接給固定值。
*/
type PlanQuota interface {
	RadarQuota(ctx context.Context, ownerUID int64) (maxActiveWatches, maxDailyOpportunities int, err error)
}

// FixedQuota 是測試與離線工具用的固定上限。
type FixedQuota struct {
	MaxActiveWatches      int
	MaxDailyOpportunities int
}

func (q FixedQuota) RadarQuota(context.Context, int64) (int, int, error) {
	return q.MaxActiveWatches, q.MaxDailyOpportunities, nil
}

/*
沒接上 PlanQuota 時的保守預設 = 最低方案。

失敗方向要選「少給」而不是「白送」：漏接線的結果是使用者看到上限提示來問客服，
而不是所有人都拿到 Pro 的常駐監控量。
*/
var fallbackQuota = FixedQuota{MaxActiveWatches: 1, MaxDailyOpportunities: 5}

func (s *Service) quota(ctx context.Context, ownerUID int64) (FixedQuota, error) {
	if s.Quota == nil {
		return fallbackQuota, nil
	}
	maxActive, maxDaily, err := s.Quota.RadarQuota(ctx, ownerUID)
	if err != nil {
		return FixedQuota{}, err
	}
	if maxActive <= 0 {
		maxActive = fallbackQuota.MaxActiveWatches
	}
	if maxDaily <= 0 {
		maxDaily = fallbackQuota.MaxDailyOpportunities
	}
	return FixedQuota{MaxActiveWatches: maxActive, MaxDailyOpportunities: maxDaily}, nil
}

func (s *Service) MaxActiveWatches(ctx context.Context, ownerUID int64) (int, error) {
	q, err := s.quota(ctx, ownerUID)
	if err != nil {
		return 0, err
	}
	return q.MaxActiveWatches, nil
}

func (s *Service) MaxDailyOpportunities(ctx context.Context, ownerUID int64) (int, error) {
	q, err := s.quota(ctx, ownerUID)
	if err != nil {
		return 0, err
	}
	return q.MaxDailyOpportunities, nil
}

/*
assertCanActivateForWatch 是「變成 active」的配額閘（RW-01）。

exceptWatchID 是正在恢復的那一筆：它目前不是 active，所以不會被算進 CountActive，
帶進來只為了在訊息與計算上表達清楚。

既有超額者不強制降級（spec §3.1）：這裡只擋「再多一個」。
*/
func (s *Service) assertCanActivateForWatch(ctx context.Context, ownerUID int64, exceptWatchID string, productWatch bool) error {
	_ = productWatch
	return s.assertCanActivateQuota(ctx, ownerUID, exceptWatchID)
}

func (s *Service) assertCanActivateQuota(ctx context.Context, ownerUID int64, exceptWatchID string) error {
	maxActive, err := s.MaxActiveWatches(ctx, ownerUID)
	if err != nil {
		return err
	}
	active, err := s.Repo.CountActiveWatches(ctx, ownerUID)
	if err != nil {
		return err
	}
	if exceptWatchID != "" {
		if w, err := s.Repo.GetWatch(ctx, exceptWatchID); err == nil && w.Status == domain.WatchActive {
			active--
		} else if err != nil && !errors.Is(err, domain.ErrNotFound) {
			return err
		}
	}
	if active >= int64(maxActive) {
		return fmt.Errorf(
			"%w: active watch limit reached (%d of %d on your plan); pause an existing watch or upgrade your plan",
			domain.ErrValidation, active, maxActive,
		)
	}
	return nil
}
