import pytest


def test_table_synonym_resolves_to_real_table(fake_vocab):
    resolved = fake_vocab.resolve_table("staff")
    assert resolved is not None
    assert resolved.schema_name == "employees"


def test_plural_form_resolves(fake_vocab):
    resolved = fake_vocab.resolve_table("employees")
    assert resolved is not None
    assert resolved.schema_name == "employees"


def test_unknown_table_synonym_returns_none(fake_vocab):
    assert fake_vocab.resolve_table("spaceship") is None


def test_column_synonym_resolves_scoped_to_table(fake_vocab):
    results = fake_vocab.resolve_column("pay", table_hint="employees")
    assert any(r.schema_name == "employees.salary" for r in results)


def test_balance_synonym_resolves_on_invoices(fake_vocab):
    results = fake_vocab.resolve_column("outstanding", table_hint="invoices")
    assert any(r.schema_name == "invoices.balance" for r in results)


def test_column_synonym_without_table_hint_returns_all_candidates(fake_vocab):
    results = fake_vocab.resolve_column("name")
    schema_names = {r.schema_name for r in results}
    assert "employees.name" in schema_names
    assert "customers.name" in schema_names
    assert "departments.name" in schema_names


# --- Fuzzy fallback resolution (typos / unmapped phrasing) ---------------------

def test_fuzzy_table_resolution_handles_minor_typo(fake_vocab):
    """'employes' (missing an 'e') has no exact/synonym entry but is close to 'employees'."""
    resolved = fake_vocab.resolve_table("employes")
    assert resolved is not None
    assert resolved.schema_name == "employees"


def test_fuzzy_table_resolution_returns_none_below_cutoff(fake_vocab):
    resolved = fake_vocab.resolve_table("xyzabc123")
    assert resolved is None


def test_fuzzy_column_resolution_handles_minor_typo(fake_vocab):
    """'salery' has no synonym entry but is close to the real 'salary' column."""
    results = fake_vocab.resolve_column("salery", table_hint="employees")
    assert len(results) == 1
    assert results[0].schema_name == "employees.salary"


def test_fuzzy_column_resolution_scoped_to_table_hint(fake_vocab):
    """A typo of 'phone' should not match anything on employees (no phone column there)."""
    results = fake_vocab.resolve_column("phon", table_hint="employees")
    assert results == []


def test_fuzzy_column_resolution_finds_match_on_correct_table(fake_vocab):
    results = fake_vocab.resolve_column("phon", table_hint="customers")
    assert any(r.schema_name == "customers.phone" for r in results)


def test_fuzzy_resolution_of_empty_string_returns_nothing(fake_vocab):
    assert fake_vocab.resolve_table("") is None
    assert fake_vocab.resolve_column("", table_hint="employees") == []


# --- Ambiguity: fuzzy matching must never silently pick between tied candidates ---

def test_fuzzy_table_resolution_raises_on_tie():
    """
    Two distinct real tables ('vendor', 'vendoz') are equally similar to the
    typo 'vendo' - the vocabulary must refuse to guess and ask for
    clarification instead of silently choosing one.
    """
    from app.schema.registry import ColumnDescriptor, SchemaSnapshot, TableDescriptor
    from app.schema.vocabulary import build_vocabulary_index
    from app.core.exceptions import AmbiguousRequestError

    pk = ColumnDescriptor(name="id", type="integer", nullable=False, primary_key=True, description="id")
    snapshot = SchemaSnapshot(
        tables={
            "vendor": TableDescriptor(table="vendor", description="Vendor records", columns={"id": pk}),
            "vendoz": TableDescriptor(table="vendoz", description="Vendoz records", columns={"id": pk}),
        },
        relationships=[],
        built_at=0.0,
    )
    vocab = build_vocabulary_index(snapshot)

    with pytest.raises(AmbiguousRequestError) as exc_info:
        vocab.resolve_table("vendo")
    assert "vendor" in exc_info.value.question
    assert "vendoz" in exc_info.value.question
