import asyncio
import importlib
import sys
from types import SimpleNamespace


normalize_company_op = importlib.import_module("fmatch.saas.operators.normalize_company")


def test_normalize_company_detects_unicode_person_names():
    assert normalize_company_op._is_person_like("\u041c\u0430\u0440\u0438\u044f \u0418\u0432\u0430\u043d\u043e\u0432\u0430") is True
    assert normalize_company_op._is_person_like("Jos\u00e9 Garc\u00eda") is True
    assert normalize_company_op._is_person_like("OpenAI Research") is False


def test_normalize_company_cache_is_bounded_lru(monkeypatch):
    cache = normalize_company_op._FG_DOMAIN_CACHE
    original_limit = normalize_company_op._CACHE_LIMITS[id(cache)]
    cache.clear()
    monkeypatch.setitem(normalize_company_op._CACHE_LIMITS, id(cache), 2)

    normalize_company_op._cache_set(cache, "a.com", {"name": "A"})
    normalize_company_op._cache_set(cache, "b.com", {"name": "B"})
    assert normalize_company_op._cache_get(cache, "a.com", 3600) == {"name": "A"}
    normalize_company_op._cache_set(cache, "c.com", {"name": "C"})

    assert list(cache.keys()) == ["a.com", "c.com"]
    monkeypatch.setitem(normalize_company_op._CACHE_LIMITS, id(cache), original_limit)
    cache.clear()


def test_normalize_company_redirect_failures_are_cached(monkeypatch):
    cache = normalize_company_op._FG_REDIRECT_CACHE
    cache.clear()
    calls = {"count": 0}

    def fake_get(*args, **kwargs):
        calls["count"] += 1
        raise RuntimeError("network down")

    monkeypatch.setattr(normalize_company_op, "_FG_REDIRECT_RETRIES", 2)
    monkeypatch.setitem(sys.modules, "requests", SimpleNamespace(get=fake_get))

    assert normalize_company_op._resolve_redirect_domain("example.com") is None
    assert normalize_company_op._resolve_redirect_domain("example.com") is None
    assert calls["count"] == 2
    cache.clear()


def test_normalize_company_run_async_reuses_background_runner():
    async def exercise() -> None:
        assert normalize_company_op._run_async(asyncio.sleep(0, result="one")) == "one"
        runner_thread = normalize_company_op._ASYNC_RUNNER_THREAD
        assert runner_thread is not None
        assert normalize_company_op._run_async(asyncio.sleep(0, result="two")) == "two"
        assert normalize_company_op._ASYNC_RUNNER_THREAD is runner_thread

    asyncio.run(exercise())


def test_normalize_company_domain_score_varies_by_name_alignment():
    account = {"account_name": "Acme Corporation"}
    exact = normalize_company_op._score_sfdc_domain_match(
        "Acme Corporation", account, explicit_domain=True
    )
    weak = normalize_company_op._score_sfdc_domain_match(
        "Random Prospect", account, explicit_domain=True
    )

    assert exact >= normalize_company_op._SFDC_MATCH_HIGH
    assert weak < exact
    assert weak < 0.98


def test_normalize_company_lookup_canonical_names_uses_redirect_fallback(monkeypatch):
    monkeypatch.setattr(normalize_company_op, "_FG_REDIRECT_FALLBACK", True)
    monkeypatch.setattr(
        normalize_company_op,
        "_resolve_redirect_domains",
        lambda domains: asyncio.sleep(0, result={"acme.co": "acme.com"}),
    )
    monkeypatch.setattr(
        normalize_company_op,
        "lookup_companies_by_domain",
        lambda domains: (
            {"acme.com": {"label": "Acme", "wikidata_id": "Q1"}}
            if "acme.com" in domains
            else {}
        ),
    )
    normalize_company_op._FG_DOMAIN_CACHE.clear()

    result = asyncio.run(normalize_company_op._lookup_canonical_names(["acme.co"]))

    assert result["acme.co"]["name"] == "Acme"
    assert result["acme.co"]["source"] == "foundrygraph_bq_redirect"
