from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]


def test_competitor_intel_cache_reuses_memory_and_shared_file(tmp_path, monkeypatch):
    from services import competitor_intel_repository as repo

    monkeypatch.setattr(repo, "_CACHE_FILE", Path(tmp_path) / "competitor_intel_cache.pkl")
    repo._MEM_CACHE.clear()

    calls = {"count": 0}

    def producer():
        calls["count"] += 1
        return {"valid_matches": 7, "match_rate": 0.1}

    first = repo._cached_payload("coverage:test", producer, ttl_seconds=60)
    second = repo._cached_payload("coverage:test", producer, ttl_seconds=60)
    repo._MEM_CACHE.clear()
    third = repo._cached_payload("coverage:test", producer, ttl_seconds=60)

    assert first == second == third == {"valid_matches": 7, "match_rate": 0.1}
    assert calls["count"] == 1


def test_clear_competitor_intel_cache_removes_shared_file(tmp_path, monkeypatch):
    from services import competitor_intel_repository as repo

    cache_file = Path(tmp_path) / "competitor_intel_cache.pkl"
    monkeypatch.setattr(repo, "_CACHE_FILE", cache_file)
    repo._MEM_CACHE["x"] = {"time": 1, "value": {"ok": True}}
    cache_file.write_bytes(b"stale")

    repo.clear_competitor_intel_cache()

    assert repo._MEM_CACHE == {}
    assert not cache_file.exists()


def test_competitor_ppt_results_keep_pending_diagnostics_in_export():
    source = (ROOT / "services" / "competitor_intel_repository.py").read_text(encoding="utf-8")

    assert "LEFT JOIN valid_competitor" in source
    assert "\"found\": found" in source
    assert "\"match_status\"" in source
    assert "\"candidate_count\"" in source
    assert "\"unit_comparison\"" in source
    assert "build_unit_price_comparison" in source
    assert "(vc.pchome_price IS NULL)" in source
    assert "(lm.momo_price - vc.pchome_price) AS price_diff" in source
    assert "((lm.momo_price - vc.pchome_price) / vc.pchome_price * 100) AS price_diff_pct" in source


def test_competitor_ppt_results_use_history_for_dated_reports():
    source = (ROOT / "services" / "competitor_intel_repository.py").read_text(encoding="utf-8")
    route_source = (ROOT / "routes" / "openclaw_bot_routes.py").read_text(encoding="utf-8")

    assert "requested_historical_prices" in source
    assert "use_history_prices" in source
    assert "FROM competitor_price_history cph" in source
    assert "cph.crawled_at >= DATE(:start_date)" in source
    assert "cph.crawled_at < DATE(:end_date) + INTERVAL '1 day'" in source
    assert "pr.timestamp < DATE(:end_date) + INTERVAL '1 day'" in source
    assert "'competitor_price_history' AS competitor_source" in source
    assert "\"competitor_source\"" in source
    assert "\"pc_crawled_at\"" in source
    assert "指定期間 competitor_price_history" in route_source


def test_competitor_coverage_counts_only_active_product_intersection():
    source = (ROOT / "services" / "competitor_intel_repository.py").read_text(encoding="utf-8")
    coverage_source = source.split("def _fetch_competitor_coverage_uncached", 1)[1].split(
        "def _fetch_manual_review_summary", 1
    )[0]

    assert "coverage:v5" in source
    assert "rescore_accepted_count" in coverage_source
    assert "(SELECT COUNT(*) FROM valid_competitor) AS valid_matches" not in coverage_source
    assert "FROM latest_momo lm\n             JOIN valid_competitor vc ON vc.sku = lm.sku" in coverage_source
    assert "FROM products p\n            JOIN LATERAL" in coverage_source
    assert "WHERE p.status = 'ACTIVE'" in coverage_source


def test_competitor_ppt_and_ai_use_momo_minus_pchome_gap_direction():
    ppt_source = (ROOT / "services" / "ppt_generator.py").read_text(encoding="utf-8")
    route_source = (ROOT / "routes" / "openclaw_bot_routes.py").read_text(encoding="utf-8")

    assert 'pc_wins    = [r for r in found if r.get("price_diff", 0) > 10]' in ppt_source
    assert 'mo_wins    = [r for r in found if r.get("price_diff", 0) < -10]' in ppt_source
    assert "平均價差 {avg_pct:+.1f}%（momo - PChome）" in ppt_source
    assert "price_diff = MOMO售價 − PChome售價" in route_source
    assert "正值=MOMO較貴、PChome低價壓力" in route_source


def test_competitor_review_queue_is_canonical_unit_price_handoff():
    source = (ROOT / "services" / "competitor_intel_repository.py").read_text(encoding="utf-8")
    daily_template = (ROOT / "templates" / "daily_sales.html").read_text(encoding="utf-8")
    growth_template = (ROOT / "templates" / "growth_analysis.html").read_text(encoding="utf-8")

    assert "def fetch_competitor_review_queue" in source
    assert "\"review_queue\": fetch_competitor_review_queue" in source
    assert "\"unit_comparable_count\"" in source
    assert "\"rescore_accepted_count\"" in source
    assert "manual_review_summary" in source
    assert "manual_accept_count" in source
    assert "manual_reject_count" in source
    assert "manual_unit_price_count" in source
    assert "manual_rejected" in source
    assert "manual_unit_price_required" in source
    assert "manual_needs_research" in source
    assert "manual_closed" in source
    assert "competitor_match_reviews" in source
    assert "\"status_label\"" in source
    assert "\"action_label\"" in source
    assert "build_unit_price_comparison" in source
    assert "需單位價覆核" in daily_template
    assert "人工採用" in daily_template
    assert "人工否決" in daily_template
    assert "人工單位價" in daily_template
    assert "competitor_intel.review_queue" in daily_template
    assert "coverage.unit_comparable_count" in growth_template
    assert "coverage.rescore_accepted_count" in growth_template
    assert "coverage.manual_accept_count" in growth_template
    assert "coverage.manual_reject_count" in growth_template
    assert "coverage.manual_unit_price_count" in growth_template
    assert "comp_coverage.rescore_accepted_count" in daily_template


def test_competitor_review_filters_split_low_score_operational_buckets():
    from services.competitor_intel_repository import REVIEW_STATUS_FILTER_GROUPS

    assert REVIEW_STATUS_FILTER_GROUPS["recoverable_low_score"] == ("recoverable_low_score",)
    assert REVIEW_STATUS_FILTER_GROUPS["true_low_confidence"] == ("true_low_confidence",)
    assert REVIEW_STATUS_FILTER_GROUPS["legacy_low_score"] == ("low_score", "refresh_low_score")
    assert set(REVIEW_STATUS_FILTER_GROUPS["low_score"]) == {
        "low_score",
        "refresh_low_score",
        "recoverable_low_score",
        "true_low_confidence",
    }


def test_competitor_review_reasons_prefer_json_payload_labels():
    from services.competitor_intel_repository import _format_competitor_review_item

    item = _format_competitor_review_item({
        "sku": "SKU-1",
        "name": "M.A.C Macximal 柔霧唇膏",
        "momo_price": 990,
        "attempt_status": "identity_veto",
        "candidate_count": 1,
        "best_competitor_product_id": "DABC123",
        "best_competitor_product_name": "MAC Macximal 緞光唇膏",
        "best_competitor_price": 880,
        "best_match_score": 0.32,
        "match_diagnostic_json": {
            "match_type": "no_match",
            "price_basis": "none",
            "alert_tier": "suppress",
            "reasons": [
                "makeup_finish_conflict",
                "nail_tool_function_conflict",
                "schick_razor_line_conflict",
            ],
        },
        "error_message": "",
    })

    assert item["match_type_label"] == "非同款"
    assert item["price_basis_label"] == "不可比"
    assert item["alert_tier_label"] == "不告警"
    assert item["diagnostic_reason_text"] == "妝效質地不同、工具功能不同、除毛刀品線不同"
    assert [reason["code"] for reason in item["diagnostic_reasons"]] == [
        "makeup_finish_conflict",
        "nail_tool_function_conflict",
        "schick_razor_line_conflict",
    ]


def test_competitor_ppt_prompt_uses_neutral_ewooc_viewpoint():
    source = (ROOT / "routes" / "openclaw_bot_routes.py").read_text(encoding="utf-8")

    assert "EwoooC 商品營運視角" in source
    assert "待補資料不可當成成功配對" in source
    assert "高信心比對" in source
    assert "待補身份/價格" in source
    assert "需單位價比較" in source
    assert "單位價覆核樣本" in source
    assert "我方 = PChome" not in source
    assert "請以 PChome 視角" not in source


def test_top_competitor_risks_reads_latest_momo_price_after_valid_competitor_filter():
    source = (ROOT / "services" / "competitor_intel_repository.py").read_text(encoding="utf-8")
    risk_source = source.split("def _fetch_top_competitor_risks_uncached", 1)[1].split("def fetch_competitor_review_queue", 1)[0]

    assert "FROM valid_competitor vc" in risk_source
    assert "JOIN LATERAL" in risk_source
    assert "WHERE pr.product_id = p.id" in risk_source
    assert "ROW_NUMBER() OVER (PARTITION BY p.id" not in risk_source
