package usecase

import (
	"context"
	"strings"
	"testing"

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

func TestImportManualOpportunities_CreatesWithManualImportSource(t *testing.T) {
	ctx := context.Background()
	mem := repository.NewMemory()
	svc := New(mem)

	results, err := svc.ImportManualOpportunities(ctx, 1, []ManualImportItem{
		{URL: "https://www.threads.net/@seeker/post/abc123", Text: "台北找廚房翻修師傅，請問多少錢？急！"},
	})
	if err != nil {
		t.Fatalf("import: %v", err)
	}
	if len(results) != 1 {
		t.Fatalf("results len = %d, want 1", len(results))
	}
	r := results[0]
	if r.Status != domain.OppQualified && r.Status != domain.OppRejected {
		t.Fatalf("status = %q, want qualified or rejected", r.Status)
	}
	if r.OpportunityID == "" {
		t.Fatalf("expected opportunity id to be set: %+v", r)
	}

	saved, err := mem.GetOpportunity(ctx, r.OpportunityID)
	if err != nil {
		t.Fatalf("get saved: %v", err)
	}
	if saved.Source != domain.OppSourceManualImport {
		t.Fatalf("source = %q, want %q", saved.Source, domain.OppSourceManualImport)
	}
	if saved.OwnerUID != 1 {
		t.Fatalf("owner_uid = %d, want 1", saved.OwnerUID)
	}
	if saved.PostedAt == 0 {
		t.Fatal("expected posted_at to default to import time")
	}
	if err := domain.ValidateReasons(saved.Reasons); err != nil {
		t.Fatalf("reasons incomplete: %v", err)
	}
}

func TestImportManualOpportunities_DedupSameURLSkipsSecondCall(t *testing.T) {
	ctx := context.Background()
	mem := repository.NewMemory()
	svc := New(mem)
	item := ManualImportItem{URL: "https://www.threads.net/@a/post/dup1", Text: "需要找人幫忙裝潢，預算五萬"}

	first, err := svc.ImportManualOpportunities(ctx, 2, []ManualImportItem{item})
	if err != nil {
		t.Fatalf("first import: %v", err)
	}
	firstID := first[0].OpportunityID

	second, err := svc.ImportManualOpportunities(ctx, 2, []ManualImportItem{item})
	if err != nil {
		t.Fatalf("second import: %v", err)
	}
	if second[0].Status != ImportStatusSkipped {
		t.Fatalf("status = %q, want skipped", second[0].Status)
	}
	if second[0].OpportunityID != firstID {
		t.Fatalf("opportunity id changed on dedupe: got %q want %q", second[0].OpportunityID, firstID)
	}

	// Different owner importing the same URL is not a duplicate — each owner has their own pipeline.
	third, err := svc.ImportManualOpportunities(ctx, 3, []ManualImportItem{item})
	if err != nil {
		t.Fatalf("third import: %v", err)
	}
	if third[0].Status == ImportStatusSkipped {
		t.Fatalf("cross-owner import should not be treated as duplicate: %+v", third[0])
	}
}

func TestImportManualOpportunities_RowLevelValidationDoesNotFailWholeBatch(t *testing.T) {
	ctx := context.Background()
	mem := repository.NewMemory()
	svc := New(mem)

	results, err := svc.ImportManualOpportunities(ctx, 4, []ManualImportItem{
		{URL: "", Text: "缺網址"},
		{URL: "not-a-url", Text: "網址格式錯"},
		{URL: "https://www.threads.net/@ok/post/1", Text: "台中需要清潔服務，這週可以嗎？"},
	})
	if err != nil {
		t.Fatalf("import: %v", err)
	}
	if len(results) != 3 {
		t.Fatalf("results len = %d, want 3", len(results))
	}
	if results[0].Status != ImportStatusFailed || results[0].Error == "" {
		t.Fatalf("row 0 = %+v, want failed with reason", results[0])
	}
	if results[1].Status != ImportStatusFailed || results[1].Error == "" {
		t.Fatalf("row 1 = %+v, want failed with reason", results[1])
	}
	if results[2].OpportunityID == "" {
		t.Fatalf("row 2 should have imported successfully: %+v", results[2])
	}
}

func TestImportManualOpportunities_RejectsEmptyOrOversizedBatch(t *testing.T) {
	ctx := context.Background()
	mem := repository.NewMemory()
	svc := New(mem)

	if _, err := svc.ImportManualOpportunities(ctx, 1, nil); err == nil {
		t.Fatal("expected error for empty batch")
	}

	items := make([]ManualImportItem, MaxManualImportBatch+1)
	for i := range items {
		items[i] = ManualImportItem{URL: "https://www.threads.net/@x/post/" + string(rune('a'+i)), Text: "測試"}
	}
	if _, err := svc.ImportManualOpportunities(ctx, 1, items); err == nil || !strings.Contains(err.Error(), "rows") {
		t.Fatalf("expected batch size error, got %v", err)
	}
}

func TestImportManualOpportunities_RequiresOwner(t *testing.T) {
	ctx := context.Background()
	mem := repository.NewMemory()
	svc := New(mem)

	if _, err := svc.ImportManualOpportunities(ctx, 0, []ManualImportItem{{URL: "https://x.com/a", Text: "t"}}); err == nil {
		t.Fatal("expected error for missing owner")
	}
}
