from fastapi.testclient import TestClient

from src.fmatch.saas.main import app
from src.fmatch.saas.api.v1 import deps as v1_deps


client = TestClient(app)


def test_missing_api_key_unauthorized():
    r = client.post('/api/v1/match', json={"profile": "balanced", "source": {}, "candidates": []})
    assert r.status_code == 401
    assert r.json().get('error') == 'UNAUTHORIZED' or r.json().get('detail', {}).get('error') == 'UNAUTHORIZED'


def _stub_ctx():
    class Ctx:
        api_key_prefix = 'testkey'
        account_id = 'acct_test'
        tier = 'pro'
        qps_limit = 100
    return Ctx()


def test_sync_match_path_monkeypatched(monkeypatch):
    app.dependency_overrides[v1_deps.require_api_key] = lambda: _stub_ctx()

    calls = {"n": 0}

    async def _fake_sync(self, body, profile):
        calls["n"] += 1
        return {
            "best_match": {"id": "x"},
            "confidence": 0.9,
            "explanations": [],
            "all_scores": [0.9],
            "recommendation": "merge",
            "decision_id": "dec_test",
        }

    from src.fmatch.saas.services.public_match_service import PublicMatchService
    monkeypatch.setattr(PublicMatchService, "_is_sync", lambda self, body: True)
    monkeypatch.setattr(PublicMatchService, "_sync_match", _fake_sync)

    payload = {"profile": "balanced", "source": {"id": 1}, "candidates": [{"id": 2}]}
    r = client.post('/api/v1/match', headers={"X-API-Key": "dummy"}, json=payload)
    assert r.status_code == 200
    data = r.json()
    assert data["recommendation"] in ("merge", "review", "no_match")
    assert calls["n"] == 1


def test_cache_key_uses_hashes(monkeypatch):
    app.dependency_overrides[v1_deps.require_api_key] = lambda: _stub_ctx()
    used_keys = []

    from src.fmatch.saas.services import public_match_service as pms_mod
    from src.fmatch.saas.api.intelligence_client import IntelligenceClient
    from src.fmatch.saas.services.engine_client import EngineClient

    async def fake_set(self, key, value):
        used_keys.append(key)

    async def fake_get(self, key):
        return None

    async def fake_policy(self, account_id=None, segment=None):
        return {"suggest": 0.7, "auto_link": 0.8}

    async def fake_auto(self, df_src, df_ref, goal='balanced', **kwargs):
        return {"field_mappings": [{"source": "Company", "reference": "Name", "algorithm": "WRatio"}]}

    async def fake_match(self, source_data, reference_data, mappings, threshold=0.7, **kwargs):
        import pandas as pd
        df = pd.DataFrame([{"score": 0.8, "Name": "Acme Inc"}])
        class R:
            matches = df
            engine_version = 'x'
            processing_time_ms = 1
        return R()

    monkeypatch.setattr(pms_mod.HybridCache, "set", fake_set, raising=False)
    monkeypatch.setattr(pms_mod.HybridCache, "get", fake_get, raising=False)
    # Force is_sync=True and stub intelligence+engine
    from src.fmatch.saas.services.public_match_service import PublicMatchService
    monkeypatch.setattr(PublicMatchService, "_is_sync", lambda self, body: True)
    monkeypatch.setattr(IntelligenceClient, "get_policy", fake_policy, raising=False)
    monkeypatch.setattr(IntelligenceClient, "auto_configure", fake_auto, raising=False)
    monkeypatch.setattr(EngineClient, "match_records", fake_match, raising=False)

    payload = {"profile": "balanced", "source": {"Company": "Acme"}, "candidates": [{"id": 1, "Name": "Acme"}]}
    r = client.post('/api/v1/match', headers={"X-API-Key": "dummy"}, json=payload)
    assert r.status_code == 200
    # We can't intercept the exact key creation due to fake_sync, but ensure no raw payload leaked in used keys
    for k in used_keys:
        assert "Acme" not in k
        assert "Company" not in k


def test_async_accept_and_idempotency(monkeypatch):
    app.dependency_overrides[v1_deps.require_api_key] = lambda: _stub_ctx()

    async def _fake_async(self, body, profile, idem_key):
        return {"status": "processing", "job_id": f"job_{idem_key}", "poll_url": f"/api/v1/jobs/job_{idem_key}"}

    from src.fmatch.saas.services.public_match_service import PublicMatchService
    monkeypatch.setattr(PublicMatchService, "_is_sync", lambda self, body: False)
    monkeypatch.setattr(PublicMatchService, "_async_match", _fake_async)

    payload = {"profile": "balanced", "source_url": "s3://x", "reference_url": "s3://y"}
    h = {"X-API-Key": "dummy", "Idempotency-Key": "abc123"}
    r1 = client.post('/api/v1/match', headers=h, json=payload)
    r2 = client.post('/api/v1/match', headers=h, json=payload)
    assert r1.status_code == 200 and r2.status_code == 200
    assert r1.json()["job_id"] == r2.json()["job_id"]


def test_feedback_ok():
    app.dependency_overrides[v1_deps.require_api_key] = lambda: _stub_ctx()
    r = client.post('/api/v1/feedback', headers={"X-API-Key": "dummy"}, json={"decision_id": "dec_1", "outcome": "accepted"})
    assert r.status_code == 200
    assert r.json().get("ok") is True
