package usecase

import (
	"context"
	"errors"
	"testing"

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

func productWatchService() (*Service, *productSourceFixture) {
	source := &productSourceFixture{brand: &ProductBrand{ID: "b1", OwnerUID: 42, DisplayName: "品牌", UpdatedAt: 1}, product: &ProductCatalogProduct{ID: "p1", BrandID: "b1", OwnerUID: 42, Label: "產品", PainPoints: []string{"敏感肌"}, UpdatedAt: 2}}
	svc := New(repository.NewMemory())
	svc.ProductSource = source
	svc.Quota = FixedQuota{MaxActiveWatches: 5, MaxDailyOpportunities: 20}
	return svc, source
}

func TestCreateProductWatchDoesNotRequireServiceProfile(t *testing.T) {
	svc, _ := productWatchService()
	w, err := svc.CreateWatch(context.Background(), 42, WatchInput{Terms: []string{"敏感肌"}, Enabled: true, BrandID: "b1", ProductID: "p1"})
	if err != nil {
		t.Fatal(err)
	}
	if w.ContextMode != domain.WatchContextProduct || w.BrandID != "b1" || w.ProductID != "p1" || w.Status != domain.WatchActive {
		t.Fatalf("product watch context: %+v", w)
	}
}

func TestGenericWatchKeepsProfileGateAndAssignIsOneWay(t *testing.T) {
	svc, _ := productWatchService()
	ctx := context.Background()
	w, err := svc.CreateWatch(ctx, 42, WatchInput{Terms: []string{"敏感肌"}, Enabled: false})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := svc.ResumeWatch(ctx, 42, w.ID); err != nil {
		t.Fatalf("generic watch without profile should resume: %v", err)
	}
	assigned, err := svc.AssignWatchProduct(ctx, 42, w.ID, "b1", "p1")
	if err != nil {
		t.Fatal(err)
	}
	if assigned.ContextMode != domain.WatchContextProduct {
		t.Fatalf("assign did not bind product: %+v", assigned)
	}
	if _, err := svc.AssignWatchProduct(ctx, 42, w.ID, "b1", "p1"); !errors.Is(err, domain.ErrValidation) {
		t.Fatalf("second assign err=%v, want validation", err)
	}
	if _, err := svc.ResumeWatch(ctx, 42, w.ID); err != nil {
		t.Fatalf("product watch resume should not require profile: %v", err)
	}
}

func TestUnavailableProductWatchCanResumeAfterContextIsRestored(t *testing.T) {
	svc, _ := productWatchService()
	ctx := context.Background()
	w, err := svc.CreateWatch(ctx, 42, WatchInput{Terms: []string{"敏感肌"}, Enabled: true, BrandID: "b1", ProductID: "p1"})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := svc.PauseProductUnavailable(ctx, 42, w.ID, domain.PauseReasonProductUnavailable); err != nil {
		t.Fatal(err)
	}
	resumed, err := svc.ResumeWatch(ctx, 42, w.ID)
	if err != nil {
		t.Fatalf("resume restored product watch: %v", err)
	}
	if resumed.Status != domain.WatchActive || resumed.PauseReason != "" {
		t.Fatalf("stale unavailable guard was not cleared: %+v", resumed)
	}
}

func TestProductWatchRequiresPairedOwnedIDs(t *testing.T) {
	svc, _ := productWatchService()
	if _, err := svc.CreateWatch(context.Background(), 42, WatchInput{Terms: []string{"敏感肌"}, BrandID: "b1", Enabled: false}); !errors.Is(err, domain.ErrValidation) {
		t.Fatalf("unpaired create err=%v", err)
	}
	if _, err := svc.CreateWatch(context.Background(), 42, WatchInput{Terms: []string{"敏感肌"}, BrandID: "b1", ProductID: "missing"}); !errors.Is(err, domain.ErrNotFound) {
		t.Fatalf("missing product err=%v", err)
	}
}
