from sqlalchemy import create_engine, text


def test_connector_contract_lists_mainstream_sources_without_auto_alerts():
    from services.external_market_offer_service import build_connector_contracts

    payload = build_connector_contracts()

    assert payload["success"] is True
    assert payload["plain_summary"] == "所有外部市場資料都先轉成同一份商品報價格式，再進作戰清單。"
    sources = {source["code"]: source for source in payload["sources"]}
    assert sources["momo_reference"]["status_label"] == "正在使用"
    assert sources["shopee"]["status_label"] == "先暫停"
    assert sources["coupang"]["status_label"] == "先暫停"
    assert {
        "shopee",
        "lazada",
        "amazon",
        "google_merchant",
        "tiktok_shop",
        "line_shopping",
        "rakuten",
        "yahoo_shopping",
        "ruten",
        "shopify_brand_store",
        "meta_commerce",
        "coupang",
    }.issubset(sources)
    assert all(
        sources[code]["status_code"] == "paused"
        for code in sources
        if code != "momo_reference"
    )
    assert "手動 CSV" in sources["shopee"]["input_methods"]
    assert "官方 API" in sources["coupang"]["input_methods"]
    assert "官方報表" in sources["google_merchant"]["input_methods"]
    assert "商品 Feed" in sources["shopify_brand_store"]["input_methods"]
    assert sources["tiktok_shop"]["plain_note"].startswith("保留短影音電商")
    assert sources["ruten"]["display_name"] == "露天"
    assert "price" in payload["manual_csv"]["required_headers"]


def test_normalized_offer_payload_validates_plain_required_fields():
    from services.external_market_offer_service import normalize_external_offer_payload

    record, errors = normalize_external_offer_payload({
        "source_code": "momo_reference",
        "source_product_id": "MOMO-1",
        "title": "外部參考商品",
        "price": "899",
        "observed_at": "2026-06-15T10:00:00",
        "ingestion_method": "manual_csv",
        "quality_score": 82,
        "quality_note": "人工確認同款",
    })

    assert errors == []
    assert record is not None
    assert record.to_record()["source_offer_key"] == "momo_reference:MOMO-1"
    assert record.to_record()["data_quality_status"] == "needs_review"

    missing_record, missing_errors = normalize_external_offer_payload({
        "source_code": "shopee",
        "price": 0,
    })

    assert missing_record is None
    assert "缺少外部商品 ID" in missing_errors
    assert "售價必須大於 0" in missing_errors


def test_external_offer_csv_dry_run_accepts_chinese_headers_and_classifies_rows():
    from services.external_market_offer_service import dry_run_external_offer_csv

    csv_text = "\n".join([
        "資料來源,外部商品ID,商品名稱,售價,資料時間,取得方式,PChome商品ID,同款狀態,資料可信度",
        "momo_reference,MOMO-1,可用商品,899,2026-06-15T10:00:00,manual_csv,PCH-1,verified,88",
        "shopee,SHP-1,暫停來源商品,799,2026-06-15T10:01:00,manual_csv,PCH-2,verified,90",
        "momo_reference,MOMO-2,缺價格商品,,2026-06-15T10:02:00,manual_csv,PCH-3,verified,90",
    ])

    payload = dry_run_external_offer_csv(csv_text)

    assert payload["success"] is True
    assert payload["message"] == "CSV 預檢完成，尚未寫入資料。"
    assert payload["summary"] == {
        "total_rows": 3,
        "ready_count": 1,
        "review_count": 1,
        "blocked_count": 1,
    }
    rows = payload["rows"]
    assert rows[0]["status_label"] == "可使用"
    assert rows[1]["status_label"] == "需人工確認"
    assert "這個來源目前先暫停，不進告警" in rows[1]["reasons"]
    assert rows[2]["status_label"] == "不能使用"
    assert "缺少售價" in rows[2]["reasons"]


def test_external_offer_csv_dry_run_accepts_plain_business_headers_and_values():
    from services.external_market_offer_service import dry_run_external_offer_csv

    csv_text = "\n".join([
        "來源,平台商品編號,商品名稱,售價,資料時間,取得方式,PChome商品編號,是否同款,可信度",
        "MOMO,MOMO-1,可用商品,899,2026-06-15T10:00:00,備用資料,PCH-1,是,88",
        "MOMO,MOMO-2,待確認商品,799,2026-06-15T10:01:00,備援資料,,待確認,60",
    ])

    payload = dry_run_external_offer_csv(csv_text)

    assert payload["success"] is True
    assert payload["summary"]["total_rows"] == 2
    assert payload["summary"]["ready_count"] == 1
    assert payload["summary"]["review_count"] == 1
    assert payload["summary"]["blocked_count"] == 0
    rows = payload["rows"]
    assert rows[0]["source_code"] == "momo_reference"
    assert rows[0]["status_label"] == "可使用"
    assert rows[1]["source_code"] == "momo_reference"
    assert rows[1]["status_label"] == "需人工確認"
    assert "尚未確認同款" in rows[1]["reasons"]
    assert "缺少 PChome 商品 ID，無法連到業績" in rows[1]["reasons"]


def test_external_offer_csv_dry_run_reports_empty_file_plainly():
    from services.external_market_offer_service import dry_run_external_offer_csv

    payload = dry_run_external_offer_csv("")

    assert payload["success"] is False
    assert payload["errors"] == ["CSV 內容是空的"]
    assert payload["summary"]["blocked_count"] == 0


def _seed_external_offer_sync_tables(engine):
    with engine.begin() as conn:
        conn.execute(text("""
            CREATE TABLE external_market_sources (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                code TEXT UNIQUE NOT NULL,
                display_name TEXT NOT NULL,
                platform_code TEXT NOT NULL,
                source_kind TEXT NOT NULL,
                status TEXT NOT NULL DEFAULT 'paused',
                enabled BOOLEAN NOT NULL DEFAULT 0,
                write_enabled BOOLEAN NOT NULL DEFAULT 0,
                allowed_input_methods_json TEXT,
                quality_policy_json TEXT,
                plain_note TEXT,
                created_at TEXT DEFAULT CURRENT_TIMESTAMP,
                updated_at TEXT DEFAULT CURRENT_TIMESTAMP
            )
        """))
        conn.execute(text("""
            CREATE TABLE external_offers (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                source_code TEXT NOT NULL,
                platform_code TEXT NOT NULL,
                source_product_id TEXT NOT NULL,
                source_offer_key TEXT NOT NULL,
                title TEXT NOT NULL,
                brand TEXT,
                category_text TEXT,
                product_url TEXT,
                image_url TEXT,
                price REAL,
                original_price REAL,
                currency TEXT NOT NULL DEFAULT 'TWD',
                stock_status TEXT,
                sold_count INTEGER,
                rating REAL,
                review_count INTEGER,
                observed_at TEXT NOT NULL,
                expires_at TEXT,
                ingestion_method TEXT NOT NULL,
                connector_key TEXT,
                pchome_product_id TEXT,
                momo_sku TEXT,
                match_status TEXT NOT NULL DEFAULT 'unmatched',
                quality_score REAL NOT NULL DEFAULT 0,
                data_quality_status TEXT NOT NULL DEFAULT 'needs_review',
                quality_notes_json TEXT,
                raw_payload_json TEXT,
                created_at TEXT DEFAULT CURRENT_TIMESTAMP,
                updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
                UNIQUE (source_code, source_product_id, observed_at, ingestion_method)
            )
        """))
        conn.execute(text(
            "CREATE TABLE competitor_prices ("
            "sku TEXT, source TEXT, competitor_product_id TEXT, competitor_product_name TEXT, "
            "price REAL, match_score REAL, tags TEXT, crawled_at TEXT, expires_at TEXT)"
        ))
        conn.execute(text(
            "CREATE TABLE products ("
            "id INTEGER PRIMARY KEY, i_code TEXT, name TEXT, url TEXT, image_url TEXT, status TEXT)"
        ))
        conn.execute(text(
            "CREATE TABLE price_records ("
            "id INTEGER PRIMARY KEY, product_id INTEGER, price REAL, timestamp TEXT)"
        ))
        conn.execute(text("""
            INSERT INTO competitor_prices
                (sku, source, competitor_product_id, competitor_product_name,
                 price, match_score, tags, crawled_at, expires_at)
            VALUES
                ('MOMO-1', 'pchome', 'PCH-1', 'PChome 商品',
                 1000, 0.91, '["identity_v2"]', '2026-06-15 09:30:00', NULL),
                ('MOMO-2', 'pchome', 'PCH-2', '低信心商品',
                 600, 0.60, '["identity_v2"]', '2026-06-15 09:30:00', NULL)
        """))
        conn.execute(text(
            "INSERT INTO products (id, i_code, name, url, image_url, status) "
            "VALUES (1, 'MOMO-1', 'MOMO 同款商品', 'https://momo.test/1', 'https://img.test/1.jpg', 'ACTIVE')"
        ))
        conn.execute(text(
            "INSERT INTO price_records (id, product_id, price, timestamp) "
            "VALUES (1, 1, 900, '2026-06-15 10:00:00')"
        ))


def test_sync_legacy_momo_reference_offers_writes_verified_cache_to_external_offers(monkeypatch):
    from services import external_market_offer_service as service

    stale_marks = []
    monkeypatch.setattr(service, "mark_pchome_growth_cache_stale", lambda: stale_marks.append(True))

    engine = create_engine("sqlite:///:memory:")
    _seed_external_offer_sync_tables(engine)

    payload = service.sync_legacy_momo_reference_offers(engine, limit=20)

    assert payload["success"] is True
    assert payload["status"] == "synced"
    assert payload["candidate_count"] == 1
    assert payload["written_count"] == 1
    with engine.connect() as conn:
        rows = conn.execute(text("""
            SELECT source_code, platform_code, source_product_id, title, price,
                   pchome_product_id, momo_sku, match_status, quality_score,
                   data_quality_status, ingestion_method
            FROM external_offers
        """)).mappings().all()

    assert len(rows) == 1
    row = dict(rows[0])
    assert row["source_code"] == "momo_reference"
    assert row["platform_code"] == "momo"
    assert row["source_product_id"] == "MOMO-1"
    assert row["pchome_product_id"] == "PCH-1"
    assert row["title"] == "MOMO 同款商品"
    assert row["price"] == 900
    assert row["match_status"] == "verified"
    assert row["quality_score"] == 91
    assert row["data_quality_status"] == "verified"
    assert row["ingestion_method"] == "legacy_competitor_cache"
    assert stale_marks == [True]


def test_sync_legacy_momo_reference_offers_dry_run_does_not_write():
    from services.external_market_offer_service import sync_legacy_momo_reference_offers

    engine = create_engine("sqlite:///:memory:")
    _seed_external_offer_sync_tables(engine)

    payload = sync_legacy_momo_reference_offers(engine, limit=20, dry_run=True)

    assert payload["success"] is True
    assert payload["status"] == "dry_run"
    assert payload["candidate_count"] == 1
    assert payload["written_count"] == 0
    with engine.connect() as conn:
        count = conn.execute(text("SELECT COUNT(*) FROM external_offers")).scalar()
    assert count == 0


def test_sync_targeted_momo_candidates_writes_unit_price_offer(monkeypatch):
    from services import external_market_offer_service as service

    stale_marks = []
    monkeypatch.setattr(service, "mark_pchome_growth_cache_stale", lambda: stale_marks.append(True))

    engine = create_engine("sqlite:///:memory:")
    _seed_external_offer_sync_tables(engine)

    payload = service.sync_targeted_momo_candidates_to_external_offers(engine, [
        {
            "product_id": "10833188",
            "name": "MOMO B5 修復霜 40ml",
            "price": 468,
            "original_price": 835,
            "product_url": "https://momo.test/10833188",
            "image_url": "https://img.test/10833188.jpg",
            "brand": "理膚寶水",
            "target_pchome_product_id": "PCH-1",
            "target_pchome_name": "PChome B5 修復霜 40ml",
            "target_pchome_price": 920,
            "target_match_score": 0.74,
            "auto_compare_type": "unit_price",
            "target_price_basis": "unit_price",
            "target_match_reasons": ["unit_comparable"],
            "target_comparison_mode": "unit_comparable",
            "target_unit_price_comparison": {
                "comparable": True,
                "unit_label": "ml",
                "momo_total_quantity": 40,
                "competitor_total_quantity": 40,
                "momo_unit_price": 11.7,
                "competitor_unit_price": 23.0,
                "unit_gap_pct": -49.13,
            },
        },
        {
            "product_id": "REVIEW-1",
            "name": "仍需人工商品",
            "price": 500,
            "target_pchome_product_id": "PCH-2",
            "auto_compare_type": "manual_review",
        },
    ])

    assert payload["success"] is True
    assert payload["status"] == "synced"
    assert payload["candidate_count"] == 2
    assert payload["written_count"] == 1
    assert payload["unit_price_count"] == 1
    assert payload["skipped_reasons"] == {"不是可自動使用的候選": 1}

    with engine.connect() as conn:
        row = conn.execute(text("""
            SELECT source_product_id, price, pchome_product_id, match_status,
                   quality_score, data_quality_status, ingestion_method,
                   raw_payload_json
            FROM external_offers
        """)).mappings().one()

    raw_payload = __import__("json").loads(row["raw_payload_json"])
    assert row["source_product_id"] == "10833188"
    assert row["price"] == 468
    assert row["pchome_product_id"] == "PCH-1"
    assert row["match_status"] == "verified"
    assert row["quality_score"] == 82
    assert row["data_quality_status"] == "verified"
    assert row["ingestion_method"] == "targeted_momo_search"
    assert raw_payload["price_basis"] == "unit_price"
    assert raw_payload["pchome_public_price"] == 920
    assert raw_payload["unit_price_comparison"]["unit_gap_pct"] == -49.13
    assert stale_marks == [True]


def test_sync_targeted_momo_candidates_skips_total_price_identity_review(monkeypatch):
    from services import external_market_offer_service as service

    stale_marks = []
    monkeypatch.setattr(service, "mark_pchome_growth_cache_stale", lambda: stale_marks.append(True))

    engine = create_engine("sqlite:///:memory:")
    _seed_external_offer_sync_tables(engine)

    payload = service.sync_targeted_momo_candidates_to_external_offers(engine, [
        {
            "product_id": "12876190",
            "name": "【LAURA MERCIER 蘿拉蜜思】煥顏透明蜜粉 29g(#Rose-國際航空版)",
            "price": 809,
            "target_pchome_product_id": "PCH-LAURA",
            "target_pchome_name": "【Laura Mercier 蘿拉蜜思】煥顏透明蜜粉 29g",
            "target_pchome_price": 899,
            "target_match_score": 0.98,
            "auto_compare_type": "total_price",
            "target_price_basis": "manual_review",
            "target_alert_tier": "identity_review",
            "target_match_reasons": ["variant_selection_review", "strong_exact_spec_match"],
            "target_comparison_mode": "exact_identity",
        },
    ])

    assert payload["success"] is True
    assert payload["candidate_count"] == 1
    assert payload["written_count"] == 0
    assert payload["skipped_reasons"] == {"候選仍需人工確認": 1}
    assert stale_marks == []
    with engine.connect() as conn:
        count = conn.execute(text("SELECT COUNT(*) FROM external_offers")).scalar()
    assert count == 0


def test_sync_targeted_momo_review_candidates_writes_needs_review_offer(monkeypatch):
    from services import external_market_offer_service as service

    stale_marks = []
    monkeypatch.setattr(service, "mark_pchome_growth_cache_stale", lambda: stale_marks.append(True))

    engine = create_engine("sqlite:///:memory:")
    _seed_external_offer_sync_tables(engine)

    payload = service.sync_targeted_momo_review_candidates_to_external_offers(engine, [
        {
            "product_id": "14917079",
            "name": "【cle de peau 肌膚之鑰】光采柔焦蜜粉 24g (國際航空版)",
            "price": 2618,
            "target_pchome_product_id": "PCH-CDP",
            "target_pchome_name": "cle de peau 光采柔焦蜜粉 24g #1",
            "target_pchome_price": 2790,
            "target_match_score": 1.0,
            "auto_compare_type": "manual_review",
            "target_price_basis": "none",
            "target_alert_tier": "identity_review",
            "target_match_type": "exact",
            "target_match_reasons": ["variant_selection_review", "strong_exact_spec_match"],
            "target_comparison_mode": "exact_identity",
            "target_gap_pct": -6.16,
        },
        {
            "product_id": "LOW-SCORE",
            "name": "低分候選",
            "price": 100,
            "target_pchome_product_id": "PCH-LOW",
            "target_match_score": 0.4,
            "target_alert_tier": "identity_review",
        },
    ])

    assert payload["success"] is True
    assert payload["status"] == "synced"
    assert payload["candidate_count"] == 2
    assert payload["written_count"] == 1
    assert payload["skipped_reasons"] == {"人工確認候選分數過低": 1}

    with engine.connect() as conn:
        row = conn.execute(text("""
            SELECT source_product_id, price, pchome_product_id, match_status,
                   quality_score, data_quality_status, ingestion_method,
                   raw_payload_json
            FROM external_offers
        """)).mappings().one()

    readiness = service.build_external_source_readiness(engine)

    raw_payload = __import__("json").loads(row["raw_payload_json"])
    assert row["source_product_id"] == "14917079"
    assert row["price"] == 2618
    assert row["pchome_product_id"] == "PCH-CDP"
    assert row["match_status"] == "needs_review"
    assert row["quality_score"] == 100
    assert row["data_quality_status"] == "needs_review"
    assert row["ingestion_method"] == "targeted_momo_review"
    assert raw_payload["review_state"] == "needs_review"
    assert raw_payload["price_basis"] == "none"
    assert raw_payload["alert_tier"] == "identity_review"
    assert "needs_review" in raw_payload["tags"]
    assert readiness["review_offer_count"] == 1
    assert stale_marks == [True]


def test_momo_review_candidate_queue_can_confirm_candidate(monkeypatch):
    from services import external_market_offer_service as service

    stale_marks = []
    monkeypatch.setattr(service, "mark_pchome_growth_cache_stale", lambda: stale_marks.append(True))
    monkeypatch.setattr(
        service,
        "_fetch_pchome_public_image_map",
        lambda product_ids: {"PCH-CDP": "https://cs-a.ecimg.tw/items/PCH-CDP/000001.jpg"},
    )

    engine = create_engine("sqlite:///:memory:")
    _seed_external_offer_sync_tables(engine)
    service.sync_targeted_momo_review_candidates_to_external_offers(engine, [
        {
            "product_id": "14917079",
            "name": "【cle de peau 肌膚之鑰】光采柔焦蜜粉 24g (國際航空版)",
            "price": 2618,
            "target_pchome_product_id": "PCH-CDP",
            "target_pchome_name": "cle de peau 光采柔焦蜜粉 24g #1",
            "target_pchome_price": 2790,
            "target_match_score": 1.0,
            "target_price_basis": "none",
            "target_alert_tier": "identity_review",
            "target_match_type": "exact",
            "target_match_reasons": ["variant_selection_review"],
            "target_gap_pct": -6.16,
        },
    ])

    queue = service.list_momo_review_candidates(engine)

    assert queue["success"] is True
    assert queue["count"] == 1
    candidate = queue["rows"][0]
    assert candidate["pchome_product_name"] == "cle de peau 光采柔焦蜜粉 24g #1"
    assert candidate["momo_sku"] == "14917079"
    assert candidate["plain_status"] == "待確認同款或色號"
    assert candidate["pchome_image_url"] == "https://cs-a.ecimg.tw/items/PCH-CDP/000001.jpg"
    assert "momo_image_url" in candidate

    updated = service.update_momo_review_candidate(engine, candidate["id"], "confirm", note="同款 #1")

    assert updated["success"] is True
    assert updated["match_status"] == "verified"
    assert service.list_momo_review_candidates(engine)["count"] == 0
    with engine.connect() as conn:
        row = conn.execute(text("""
            SELECT match_status, data_quality_status, raw_payload_json
            FROM external_offers
            WHERE id = :id
        """), {"id": candidate["id"]}).mappings().one()
    raw_payload = __import__("json").loads(row["raw_payload_json"])
    assert row["match_status"] == "verified"
    assert row["data_quality_status"] == "verified"
    assert raw_payload["review_action"] == "confirm"
    assert stale_marks == [True, True]


def test_sync_targeted_momo_candidates_keeps_best_unit_quantity_match(monkeypatch):
    from services import external_market_offer_service as service

    monkeypatch.setattr(service, "mark_pchome_growth_cache_stale", lambda: None)

    engine = create_engine("sqlite:///:memory:")
    _seed_external_offer_sync_tables(engine)

    payload = service.sync_targeted_momo_candidates_to_external_offers(engine, [
        {
            "product_id": "MOMO-SINGLE",
            "name": "雪之上 全效合一水凝霜 80g/瓶",
            "price": 1380,
            "target_pchome_product_id": "PCH-YUKINOUE",
            "target_pchome_name": "雪之上 全效合一水凝霜 80g 3入組",
            "target_pchome_price": 2960,
            "target_match_score": 0.74,
            "auto_compare_type": "unit_price",
            "target_price_basis": "unit_price",
            "target_match_reasons": ["count_conflict", "unit_comparable"],
            "target_comparison_mode": "unit_comparable",
            "target_unit_price_comparison": {
                "comparable": True,
                "unit_label": "g",
                "momo_total_quantity": 80,
                "competitor_total_quantity": 240,
                "momo_unit_price": 17.25,
                "competitor_unit_price": 12.33,
                "unit_gap_pct": 39.86,
            },
        },
        {
            "product_id": "MOMO-THREE",
            "name": "雪之上 全效合一水凝霜 80g X 3入瓶裝",
            "price": 2680,
            "target_pchome_product_id": "PCH-YUKINOUE",
            "target_pchome_name": "雪之上 全效合一水凝霜 80g 3入組",
            "target_pchome_price": 2960,
            "target_match_score": 0.74,
            "auto_compare_type": "unit_price",
            "target_price_basis": "unit_price",
            "target_match_reasons": ["unit_comparable"],
            "target_comparison_mode": "unit_comparable",
            "target_unit_price_comparison": {
                "comparable": True,
                "unit_label": "g",
                "momo_total_quantity": 240,
                "competitor_total_quantity": 240,
                "momo_unit_price": 11.17,
                "competitor_unit_price": 12.33,
                "unit_gap_pct": -9.46,
            },
        },
    ])

    assert payload["success"] is True
    assert payload["candidate_count"] == 2
    assert payload["written_count"] == 1
    with engine.connect() as conn:
        row = conn.execute(text("""
            SELECT source_product_id, raw_payload_json
            FROM external_offers
        """)).mappings().one()
    raw_payload = __import__("json").loads(row["raw_payload_json"])
    assert row["source_product_id"] == "MOMO-THREE"
    assert raw_payload["unit_price_comparison"]["momo_total_quantity"] == 240


def test_external_source_readiness_uses_legacy_momo_reference_cache():
    from services.external_market_offer_service import build_external_source_readiness

    engine = create_engine("sqlite:///:memory:")
    with engine.begin() as conn:
        conn.execute(text(
            "CREATE TABLE competitor_prices ("
            "source TEXT, competitor_product_id TEXT, price REAL, match_score REAL, "
            "tags TEXT, crawled_at TEXT, expires_at TEXT)"
        ))
        conn.execute(text("""
            INSERT INTO competitor_prices
                (source, competitor_product_id, price, match_score, tags, crawled_at, expires_at)
            VALUES
                ('pchome', 'PCH-1', 1000, 0.91, '["identity_v2"]', '2026-06-15 10:00:00', NULL),
                ('pchome', 'PCH-2', 500, 0.50, '["identity_v2"]', '2026-06-15 10:00:00', NULL)
        """))

    payload = build_external_source_readiness(engine)

    assert payload["success"] is True
    assert payload["schema_ready"] is False
    sources = {source["code"]: source for source in payload["sources"]}
    assert sources["momo_reference"]["usable_offer_count"] == 1
    assert sources["momo_reference"]["plain_state"] == "已接入，可進作戰清單"
    assert sources["shopee"]["plain_state"] == "先保留接口，不進告警"
    assert sources["tiktok_shop"]["plain_state"] == "先保留接口，不進告警"
    assert sources["line_shopping"]["plain_state"] == "先保留接口，不進告警"
    assert sources["shopify_brand_store"]["plain_state"] == "先保留接口，不進告警"
    assert payload["plain_summary"] == "MOMO 先用；其他主流平台已列管，未接合法穩定來源前不進告警。"


def test_external_market_migration_creates_source_and_offer_tables():
    from pathlib import Path

    migration = Path("migrations/044_external_market_offer_normalization.sql").read_text(encoding="utf-8")

    assert "CREATE TABLE IF NOT EXISTS external_market_sources" in migration
    assert "CREATE TABLE IF NOT EXISTS external_offers" in migration
    assert "momo_reference" in migration
    assert "shopee" in migration
    assert "coupang" in migration
    assert "DROP " not in migration.upper()
    assert "TRUNCATE " not in migration.upper()


def test_external_offer_csv_dry_run_route_is_registered_as_post_only():
    from pathlib import Path

    route_source = Path("routes/ai_routes.py").read_text(encoding="utf-8")

    assert "@ai_bp.route('/api/ai/pchome-growth/external-offers/csv-dry-run', methods=['POST'])" in route_source
    assert "dry_run_external_offer_csv" in route_source
    assert "@ai_bp.route('/api/ai/pchome-growth/review-candidates')" in route_source
    assert "@ai_bp.route('/api/ai/pchome-growth/review-candidates/<int:offer_id>', methods=['POST'])" in route_source
    assert "list_momo_review_candidates" in route_source
    assert "update_momo_review_candidate" in route_source


def test_external_offer_sync_is_registered_in_scheduler():
    from pathlib import Path

    scheduler_source = Path("scheduler.py").read_text(encoding="utf-8")
    run_scheduler_source = Path("run_scheduler.py").read_text(encoding="utf-8")

    assert "def run_external_offer_sync_task" in scheduler_source
    assert "sync_legacy_momo_reference_offers" in scheduler_source
    assert "def run_pchome_growth_momo_backfill_task" in scheduler_source
    assert "run_pchome_growth_momo_backfill" in scheduler_source
    assert "run_external_offer_sync_task" in run_scheduler_source
    assert "run_pchome_growth_momo_backfill_task" in run_scheduler_source
    assert "external_offer_sync" in run_scheduler_source
    assert "pchome_growth_momo_backfill" in run_scheduler_source
