"""Tests for catalog loading, validation, and schema extraction."""

import os
import tempfile
from pathlib import Path

import pytest
import yaml

from opencypher_compliance.catalog import (
    load_catalog,
    validate_catalog,
    extract_schema,
    _BUILTIN_CATALOG_DIR,
)


@pytest.fixture
def sample_catalog_dir(tmp_path):
    """Create a temporary catalog directory with a sample YAML file."""
    data = {
        "tests": [
            {
                "name": "MATCH",
                "type": "clause",
                "query": "MATCH (n) RETURN n",
                "expect_success": True,
            },
            {
                "name": "RETURN",
                "type": "clause",
                "query": "RETURN 1 AS x",
                "expected_columns": ["x"],
                "expected_rows": [[1]],
            },
        ]
    }
    yaml_file = tmp_path / "test.yaml"
    yaml_file.write_text(yaml.dump(data))
    return tmp_path


class TestLoadCatalog:
    def test_load_from_directory(self, sample_catalog_dir):
        tests = load_catalog(sample_catalog_dir)
        assert len(tests) == 2
        assert tests[0]["name"] == "MATCH"
        assert tests[1]["name"] == "RETURN"

    def test_source_file_added(self, sample_catalog_dir):
        tests = load_catalog(sample_catalog_dir)
        assert tests[0]["_source_file"] == "test.yaml"

    def test_load_multiple_files(self, tmp_path):
        for name in ["a.yaml", "b.yaml"]:
            data = {"tests": [{"name": name, "type": "clause", "query": "RETURN 1", "expect_success": True}]}
            (tmp_path / name).write_text(yaml.dump(data))
        tests = load_catalog(tmp_path)
        assert len(tests) == 2

    def test_nonexistent_directory_raises(self):
        with pytest.raises(FileNotFoundError, match="not found"):
            load_catalog("/nonexistent/path")

    def test_empty_directory_raises(self, tmp_path):
        with pytest.raises(FileNotFoundError, match="No YAML"):
            load_catalog(tmp_path)

    def test_load_builtin_catalog(self):
        tests = load_catalog()
        assert len(tests) > 100  # Should have ~133 tests
        # Check we have all expected types
        types = {t["type"] for t in tests}
        assert types == {"clause", "operator", "function", "expression", "data_type"}


class TestValidateCatalog:
    def test_valid_catalog(self, sample_catalog_dir):
        tests = load_catalog(sample_catalog_dir)
        errors = validate_catalog(tests)
        assert errors == []

    def test_missing_name(self):
        tests = [{"type": "clause", "query": "RETURN 1", "expect_success": True}]
        errors = validate_catalog(tests)
        assert any("missing required field 'name'" in e for e in errors)

    def test_missing_query(self):
        tests = [{"name": "X", "type": "clause", "expect_success": True}]
        errors = validate_catalog(tests)
        assert any("missing required field 'query'" in e for e in errors)

    def test_invalid_type(self):
        tests = [{"name": "X", "type": "invalid", "query": "RETURN 1", "expect_success": True}]
        errors = validate_catalog(tests)
        assert any("invalid type" in e for e in errors)

    def test_missing_validation_field(self):
        tests = [{"name": "X", "type": "clause", "query": "RETURN 1"}]
        errors = validate_catalog(tests)
        assert any("validation field" in e for e in errors)

    def test_duplicate_names(self):
        tests = [
            {"name": "X", "type": "clause", "query": "RETURN 1", "expect_success": True},
            {"name": "X", "type": "clause", "query": "RETURN 2", "expect_success": True},
        ]
        errors = validate_catalog(tests)
        assert any("duplicate" in e for e in errors)

    def test_builtin_catalog_is_valid(self):
        tests = load_catalog()
        errors = validate_catalog(tests)
        assert errors == [], f"Built-in catalog has validation errors: {errors}"


class TestExtractSchema:
    def test_extract_node_labels(self):
        tests = [
            {"name": "t", "query": "MATCH (n:Person) RETURN n", "setup": ["CREATE (:Person {name: 'Alice'})"]}
        ]
        schema = extract_schema(tests)
        assert "Person" in schema["tags"]
        assert "name" in schema["tags"]["Person"]

    def test_extract_edge_types(self):
        tests = [
            {"name": "t", "query": "MATCH ()-[r:KNOWS]->() RETURN r", "setup": ["CREATE (:A)-[:KNOWS]->(:B)"]}
        ]
        schema = extract_schema(tests)
        assert "KNOWS" in schema["edges"]

    def test_extract_properties(self):
        tests = [
            {"name": "t", "query": "RETURN 1", "setup": ["CREATE (:Person {name: 'Alice', age: 30})"]}
        ]
        schema = extract_schema(tests)
        assert "name" in schema["tags"]["Person"]
        assert "age" in schema["tags"]["Person"]

    def test_extract_from_builtin_catalog(self):
        tests = load_catalog()
        schema = extract_schema(tests)
        assert len(schema["tags"]) > 0
        assert "Person" in schema["tags"]
