"""Tests for wiki/ontology/schema_loader.py -- knowledge schema loading and validation."""

from __future__ import annotations

import tempfile
from pathlib import Path

import pytest
import yaml

from pycharter.wiki.ontology.schema_loader import (
    KnowledgeSchema,
    RelationshipTypeInfo,
    _parse_schema,
    get_inverse,
    get_node_kinds,
    get_relationship_types,
    load_knowledge_schema,
    validate_node_kind,
    validate_relationship_type,
)


class TestLoadKnowledgeSchema:
    """Test loading and parsing of knowledge_schema.yaml."""

    def test_loads_bundled_schema(self):
        # Clear lru_cache so we get a fresh load
        load_knowledge_schema.cache_clear()
        schema = load_knowledge_schema()
        assert isinstance(schema, KnowledgeSchema)
        assert schema.version >= 1

    def test_has_relationship_types(self):
        load_knowledge_schema.cache_clear()
        schema = load_knowledge_schema()
        assert len(schema.relationship_types) >= 11

    def test_has_node_kinds(self):
        load_knowledge_schema.cache_clear()
        schema = load_knowledge_schema()
        assert len(schema.node_kinds) >= 8

    def test_relationship_type_info_fields(self):
        load_knowledge_schema.cache_clear()
        schema = load_knowledge_schema()
        is_a = schema.relationship_types.get("is_a")
        assert is_a is not None
        assert isinstance(is_a, RelationshipTypeInfo)
        assert is_a.name == "is_a"
        assert is_a.meaning != ""
        assert is_a.graph_shape == "taxonomy_tree"
        assert is_a.inverse == "has_subtype"
        assert is_a.symmetric is False

    def test_symmetric_flag(self):
        load_knowledge_schema.cache_clear()
        schema = load_knowledge_schema()
        eq = schema.relationship_types.get("equivalent_to")
        assert eq is not None
        assert eq.symmetric is True

    def test_file_not_found(self):
        load_knowledge_schema.cache_clear()
        with pytest.raises(FileNotFoundError):
            load_knowledge_schema(path="/nonexistent/path.yaml")

    def test_custom_path(self):
        load_knowledge_schema.cache_clear()
        data = {
            "version": 99,
            "relationship_types": {
                "test_rel": {
                    "meaning": "Test",
                    "graph_shape": "generic",
                    "inverse": "test_inv",
                },
            },
            "node_kinds": {
                "test_kind": "A test kind",
            },
        }
        with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as fh:
            yaml.dump(data, fh)
            fh.flush()
            schema = load_knowledge_schema(path=fh.name)

        assert schema.version == 99
        assert "test_rel" in schema.relationship_types
        assert schema.node_kinds["test_kind"] == "A test kind"


class TestParseSchema:
    """Test the internal _parse_schema function."""

    def test_minimal(self):
        schema = _parse_schema({"version": 1})
        assert schema.version == 1
        assert schema.relationship_types == {}
        assert schema.node_kinds == {}

    def test_defaults(self):
        schema = _parse_schema({})
        assert schema.version == 1


class TestValidateRelationshipType:
    """Test validate_relationship_type()."""

    def setup_method(self):
        load_knowledge_schema.cache_clear()

    @pytest.mark.parametrize(
        "rel_type",
        ["is_a", "has", "depends_on", "derived_from", "precedes", "causes"],
    )
    def test_valid_types(self, rel_type: str):
        assert validate_relationship_type(rel_type) is True

    def test_invalid_type(self):
        assert validate_relationship_type("nonexistent") is False


class TestValidateNodeKind:
    """Test validate_node_kind()."""

    def setup_method(self):
        load_knowledge_schema.cache_clear()

    @pytest.mark.parametrize(
        "kind",
        [
            "entity",
            "process",
            "event",
            "attribute",
            "rule",
            "system",
            "concept",
            "metric",
        ],
    )
    def test_valid_kinds(self, kind: str):
        assert validate_node_kind(kind) is True

    def test_invalid_kind(self):
        assert validate_node_kind("nonexistent") is False


class TestGetRelationshipTypes:
    """Test get_relationship_types()."""

    def setup_method(self):
        load_knowledge_schema.cache_clear()

    def test_returns_dict(self):
        types = get_relationship_types()
        assert isinstance(types, dict)
        assert len(types) >= 11

    def test_values_are_relationship_type_info(self):
        types = get_relationship_types()
        for info in types.values():
            assert isinstance(info, RelationshipTypeInfo)


class TestGetNodeKinds:
    """Test get_node_kinds()."""

    def setup_method(self):
        load_knowledge_schema.cache_clear()

    def test_returns_dict(self):
        kinds = get_node_kinds()
        assert isinstance(kinds, dict)
        assert len(kinds) >= 8

    def test_values_are_strings(self):
        kinds = get_node_kinds()
        for desc in kinds.values():
            assert isinstance(desc, str)
            assert len(desc) > 0


class TestGetInverse:
    """Test get_inverse()."""

    def setup_method(self):
        load_knowledge_schema.cache_clear()

    @pytest.mark.parametrize(
        "rel, expected",
        [
            ("is_a", "has_subtype"),
            ("has", "part_of"),
            ("depends_on", "depended_on_by"),
            ("equivalent_to", "equivalent_to"),
        ],
    )
    def test_known_types(self, rel: str, expected: str):
        assert get_inverse(rel) == expected

    def test_unknown_returns_self(self):
        assert get_inverse("totally_unknown") == "totally_unknown"
