package repository

import (
	"context"
	"fmt"
	"sync"
	"testing"

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

func raceProductMatch(id string, score int, n int) *domain.ProductMatch {
	m := &domain.ProductMatch{BrandID: "b", ProductID: id, ProductFitScore: score, ProductFitBand: domain.ProductFitBandFromScore(score), MatchedAt: int64(n), WatchIDs: []string{fmt.Sprintf("w-%d", n)}, MatchedTerms: []string{fmt.Sprintf("term-%d", n)}, Reasons: []domain.ProductFitReason{
		{Dimension: domain.ProductFitPain, Score: 35, Reason: "pain", CandidateExcerpt: "痛點", ProductBasis: "痛點"},
		{Dimension: domain.ProductFitScenario, Score: 25, Reason: "scenario", CandidateExcerpt: "情境", ProductBasis: "情境"},
		{Dimension: domain.ProductFitAudience, Score: 0, Reason: "unknown"},
		{Dimension: domain.ProductFitCapability, Score: score - 60, Reason: "capability", CandidateExcerpt: "能力", ProductBasis: "能力"},
	}}
	return m
}

func TestConcurrentProductMatchMergeNoDuplicateAndOverrideWins(t *testing.T) {
	ctx := context.Background()
	mem := NewMemory()
	op, err := mem.UpsertByExternalID(ctx, &domain.Opportunity{OwnerUID: 1, ExternalID: "e-race", Source: domain.OppSourceThreads, Status: domain.OppQualified, IntentScore: 80, IntentBand: domain.BandHigh, RegionMatch: domain.RegionUnknown, Reasons: []domain.OpportunityReason{{Dimension: domain.DimAuthenticity, Reason: "a"}, {Dimension: domain.DimIntent, Reason: "i"}, {Dimension: domain.DimRegion, Reason: "r"}, {Dimension: domain.DimFreshness, Reason: "f"}, {Dimension: domain.DimFit, Reason: "fit"}}})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := mem.MergeProductMatch(ctx, 1, op.ID, raceProductMatch("p1", 60, 1)); err != nil {
		t.Fatal(err)
	}
	if _, err := mem.SetPrimaryProduct(ctx, 1, op.ID, "p1", &domain.ProductPrimaryOverride{ActorUID: 1, Reason: "人工確認"}); err != nil {
		t.Fatal(err)
	}
	var wg sync.WaitGroup
	for i := 0; i < 20; i++ {
		wg.Add(2)
		go func(n int) {
			defer wg.Done()
			_, _ = mem.MergeProductMatch(ctx, 1, op.ID, raceProductMatch("p1", 100, n+10))
		}(i)
		go func(n int) {
			defer wg.Done()
			_, _ = mem.MergeProductMatch(ctx, 1, op.ID, raceProductMatch("p2", 80, n+30))
		}(i)
	}
	wg.Wait()
	got, err := mem.GetOpportunity(ctx, op.ID)
	if err != nil {
		t.Fatal(err)
	}
	if len(got.ProductMatches) != 2 || got.PrimaryProductID != "p1" || !got.PrimaryProductOverridden {
		t.Fatalf("race duplicate or override loss: primary=%s overridden=%v matches=%d", got.PrimaryProductID, got.PrimaryProductOverridden, len(got.ProductMatches))
	}
}
