package domain

import "context"

/*
Repository 是 radar module 的持久化介面。實作有兩份：mongo（正式）與 memory（測試），
兩份行為必須一致，尤其是「找不到」一律回 ErrNotFound 而非 nil。
*/
type Repository interface {
	// ServiceProfile：每會員一份
	GetServiceProfile(ctx context.Context, ownerUID int64) (*ServiceProfile, error)
	SaveServiceProfile(ctx context.Context, p *ServiceProfile) error

	// DemandMap is the product-specific, user-editable search contract.
	GetDemandMap(ctx context.Context, ownerUID int64, productID string) (*DemandMap, error)
	SaveDemandMap(ctx context.Context, m *DemandMap, expectedVersion int64) (*DemandMap, error)

	// RadarWatch
	SaveWatch(ctx context.Context, w *RadarWatch) error
	GetWatch(ctx context.Context, id string) (*RadarWatch, error)
	DeleteWatch(ctx context.Context, id string) error
	ListWatches(ctx context.Context, ownerUID int64, f WatchListFilter) ([]*RadarWatch, int64, error)
	// ListActiveWatches 是每日排程的來源，只回 active。
	ListActiveWatches(ctx context.Context, ownerUID int64) ([]*RadarWatch, error)
	// ListAllActiveWatches 跨會員列出全部 active watch，供 worker 每日排程 tick 使用。
	ListAllActiveWatches(ctx context.Context) ([]*RadarWatch, error)
	CountActiveWatches(ctx context.Context, ownerUID int64) (int64, error)
	TouchWatchSweptAt(ctx context.Context, id string, at int64) error

	// Opportunity
	// UpsertByExternalID：同 owner+external_id 只留一筆；命中則併 matched_terms，不重跑判定。
	UpsertByExternalID(ctx context.Context, o *Opportunity) (*Opportunity, error)
	// MergeProductMatch atomically adds a new ProductMatch or merges only its
	// watch/term source sets when that product was already evaluated.
	MergeProductMatch(ctx context.Context, ownerUID int64, opportunityID string, match *ProductMatch) (*Opportunity, error)
	SetPrimaryProduct(ctx context.Context, ownerUID int64, opportunityID, productID string, override *ProductPrimaryOverride) (*Opportunity, error)
	GetOpportunity(ctx context.Context, id string) (*Opportunity, error)
	GetByExternalID(ctx context.Context, ownerUID int64, externalID string) (*Opportunity, error)
	ListOpportunities(ctx context.Context, ownerUID int64, f OpportunityListFilter) ([]*Opportunity, int64, error)
	UpdateOpportunityReviewState(ctx context.Context, ownerUID int64, opportunityID string, patch ReviewStatePatch) (*Opportunity, error)
	// CountToday 回傳 UTC 當日建立的商機數（每日配額池）。
	CountToday(ctx context.Context, ownerUID int64, at int64) (int64, error)
	UpdateOpportunityStatus(ctx context.Context, id string, status string) error
	SetOpportunityOverride(ctx context.Context, id string, ov *OpportunityOverride, newBand, newStatus string) error

	// RadarSweep
	CreateSweep(ctx context.Context, s *RadarSweep) error
	// UpdateSweep 累加計數與 judged_external_ids（去重），供 Job 中途進度與續跑。
	UpdateSweep(ctx context.Context, id string, delta SweepDelta) (*RadarSweep, error)
	GetSweep(ctx context.Context, id string) (*RadarSweep, error)
	GetSweepByJobID(ctx context.Context, jobID string) (*RadarSweep, error)
	ListSweeps(ctx context.Context, ownerUID int64, f SweepListFilter) ([]*RadarSweep, int64, error)

	// ReplyVariant
	SaveReply(ctx context.Context, r *ReplyVariant) error
	ListReplies(ctx context.Context, ownerUID int64, opportunityID string) ([]*ReplyVariant, error)
	// ListRepliesForOpportunities fetches replies for many opportunities at once,
	// keyed by opportunity id, so list pages do not issue one query per row.
	ListRepliesForOpportunities(ctx context.Context, ownerUID int64, opportunityIDs []string) (map[string][]*ReplyVariant, error)
	GetReply(ctx context.Context, id string) (*ReplyVariant, error)
}
