package placement

import (
	"testing"
	"time"

	libkg "haixun-backend/internal/library/knowledge"
)

func TestHasCategoryConflictLaundryVsDish(t *testing.T) {
	ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
		ProductName: "天然洗衣精",
		MatchTags:   []string{"洗衣精", "衣物清潔"},
	}, nil)
	text := "請問大家洗碗機清潔劑有推薦嗎？碗盤油污洗不掉"
	if !HasCategoryConflict(text, ctx) {
		t.Fatal("expected dishwasher post to conflict with laundry product")
	}
	if PassesPostScanFilters(text, "洗衣精 推薦", "", QueryRecency, 80, ctx) {
		t.Fatal("expected conflicting post to be filtered out")
	}
}

func TestPassesPostScanFiltersAcceptsMatchingLaundry(t *testing.T) {
	ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
		ProductName: "天然洗衣精",
		MatchTags:   []string{"洗衣精", "衣物"},
		Pillars:     []string{"衣物清潔", "敏感皮膚洗衣"},
	}, []string{"洗碗精等其他品類"})
	text := "最近衣服洗不乾淨，洗衣精有推薦嗎？污漬一直洗不掉"
	if !PassesPostScanFilters(text, "洗衣精 推薦", time.Now().Format("2006-01-02"), QueryRecency, 70, ctx) {
		t.Fatal("expected matching laundry post to pass")
	}
}

func TestPassesPostAgeAllowsMissingDateOnRelevance(t *testing.T) {
	if !PassesPostAge("", QueryRelevance, 0) {
		t.Fatal("expected missing posted_at to pass age gate at ingest")
	}
}

func TestPassesDiscoverFiltersAllowsWebSnippetWithoutDate(t *testing.T) {
	ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
		ProductName: "天然洗衣精",
		MatchTags:   []string{"洗衣精", "衣物"},
	}, nil)
	text := "最近衣服洗不乾淨，洗衣精有推薦嗎？污漬一直洗不掉"
	if !PassesDiscoverFilters(text, "洗衣精 推薦", "", QueryRelevance, 70, 0, ctx) {
		t.Fatal("expected web snippet without date to pass discover filters")
	}
}

func TestPassesPostAgeRejectsOldRelevancePost(t *testing.T) {
	old := time.Now().AddDate(0, 0, -20).Format("2006-01-02")
	if PassesPostAge(old, QueryRelevance, 0) {
		t.Fatal("expected old relevance post to fail age gate")
	}
}

func TestHasPainPointDemandRejectsCasualChat(t *testing.T) {
	if HasPainPointDemand("今天天氣真好，晒照給大家看") {
		t.Fatal("expected casual chat without demand to fail")
	}
}

func TestHasPainPointDemandAcceptsHelpSeeking(t *testing.T) {
	text := "最近皮膚一直癢，沐浴乳有推薦嗎？"
	if !HasPainPointDemand(text) {
		t.Fatal("expected help-seeking post with pain signal to pass")
	}
}

func TestPostSolvedByProductRejectsCategoryMismatch(t *testing.T) {
	ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
		ProductName: "天然洗衣精",
		MatchTags:   []string{"洗衣精"},
	}, nil)
	text := "洗碗精有推薦嗎？碗盤油污洗不掉"
	if PostSolvedByProduct(text, 80, ctx) {
		t.Fatal("expected dish post not to be solved by laundry product")
	}
}

func TestHasTangentialTopicMismatchRejectsWashingMachine(t *testing.T) {
	ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
		ProductName:     "抗敏無香洗衣精",
		ProductFeatures: "完全無香、抗敏、適合敏感肌與化療族群",
		MatchTags:       []string{"無香", "抗敏", "洗衣精"},
		Pillars:         []string{"化療後衣物清潔", "無香洗衣"},
	}, nil)
	text := "請問大家有推薦的洗衣機嗎？想買洗脫烘"
	if !HasTangentialTopicMismatch(text, ctx) {
		t.Fatal("expected washing machine post to be tangential mismatch for hypoallergenic detergent")
	}
	if PassesPostScanFilters(text, "無香 洗衣精 推薦", time.Now().Format("2006-01-02"), QueryRecency, 80, ctx) {
		t.Fatal("expected washing machine post to be filtered out")
	}
}

func TestHasTangentialTopicMismatchAcceptsDetergentPain(t *testing.T) {
	ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
		ProductName:     "抗敏無香洗衣精",
		ProductFeatures: "完全無香、抗敏",
		MatchTags:       []string{"無香", "洗衣精"},
	}, nil)
	text := "化療後對香味很敏感，有無香洗衣精推薦嗎？"
	if HasTangentialTopicMismatch(text, ctx) {
		t.Fatal("expected detergent pain post to pass tangential check")
	}
}

func TestHasTangentialTopicMismatchAcceptsMachineWithDetergentNeed(t *testing.T) {
	ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
		ProductName:     "抗敏無香洗衣精",
		ProductFeatures: "完全無香、抗敏",
		MatchTags:       []string{"無香", "洗衣精"},
	}, nil)
	text := "買了洗衣機，但想找不會讓皮膚癢的無香洗衣精"
	if HasTangentialTopicMismatch(text, ctx) {
		t.Fatal("expected combined machine + detergent pain post to pass")
	}
}

func TestPostSolvedByProductAcceptsAlignedDemand(t *testing.T) {
	ctx := PostScanContextFromPatrolInput(libkg.PatrolTagInput{
		ProductName: "天然洗衣精",
		MatchTags:   []string{"洗衣精", "衣物"},
		Pillars:     []string{"衣物清潔"},
	}, nil)
	text := "衣服污漬洗不掉，洗衣精有推薦嗎？"
	if !PostSolvedByProduct(text, 70, ctx) {
		t.Fatal("expected aligned laundry demand to be solved by product")
	}
}

func TestExpandExclusionTokensIncludesDishSynonyms(t *testing.T) {
	tokens := ExpandExclusionTokens([]string{"洗碗精等其他品類"})
	found := false
	for _, token := range tokens {
		if token == "洗碗機" || token == "洗碗精" {
			found = true
			break
		}
	}
	if !found {
		t.Fatalf("expected dish synonyms, got %v", tokens)
	}
}
