"""Tests for the Pydantic models and serialization."""

import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

from jsonschema import validate as jsonschema_validate, ValidationError
from models import MantaraSchema, Menu, Submenu, Table, Column, EnumType


class TestToJsonDict:
    def test_has_schema_marker(self, ams_schema):
        d = ams_schema.to_json_dict()
        assert "$schema" in d
        assert d["$schema"] == "mantara.schema.v1"

    def test_schema_marker_is_first_key(self, ams_schema):
        d = ams_schema.to_json_dict()
        first_key = list(d.keys())[0]
        assert first_key == "$schema"

    def test_excludes_none_values(self):
        schema = MantaraSchema(
            system_name="Test",
            schema_name="tst",
            description="Test.",
            menus=[
                Menu(
                    menu_id=1,
                    menu_name="Main",
                    sequence_number=1,
                    description="Main.",
                    submenus=[
                        Submenu(
                            submenu_id=101,
                            submenu_name="Sub",
                            sequence_number=1,
                            description="Sub.",
                        ),
                    ],
                ),
            ],
            # enum_types is None by default
        )
        d = schema.to_json_dict()
        assert "enum_types" not in d
        # submenu.tables should also be excluded when None
        assert "tables" not in d["menus"][0]["submenus"][0]

    def test_required_fields_present(self, ams_schema):
        d = ams_schema.to_json_dict()
        assert "system_name" in d
        assert "schema_name" in d
        assert "description" in d
        assert "menus" in d


class TestSerializationRoundTrip:
    def test_json_roundtrip(self, ams_schema):
        """Serialize to JSON string and validate against the JSON Schema on disk."""
        d = ams_schema.to_json_dict()
        json_str = json.dumps(d, indent=2)

        # Parse back
        parsed = json.loads(json_str)
        assert parsed["$schema"] == "mantara.schema.v1"
        assert parsed["schema_name"] == "ams"

    def test_validates_against_json_schema(self, ams_schema):
        """Validate serialized output against mantara_schema_v1.json."""
        schema_path = Path(__file__).parent.parent / "schemas" / "mantara_schema_v1.json"
        with open(schema_path) as f:
            json_schema = json.load(f)

        d = ams_schema.to_json_dict()
        # Should not raise
        jsonschema_validate(instance=d, schema=json_schema)

    def test_invalid_schema_name_fails_json_schema(self):
        """A schema_name with uppercase should fail JSON Schema validation."""
        schema = MantaraSchema(
            system_name="Test",
            schema_name="BAD",
            description="Test.",
            menus=[
                Menu(
                    menu_id=1,
                    menu_name="Main",
                    sequence_number=1,
                    description="Main.",
                    submenus=[
                        Submenu(
                            submenu_id=101,
                            submenu_name="Sub",
                            sequence_number=1,
                            description="Sub.",
                        ),
                    ],
                ),
            ],
        )
        d = schema.to_json_dict()

        schema_path = Path(__file__).parent.parent / "schemas" / "mantara_schema_v1.json"
        with open(schema_path) as f:
            json_schema = json.load(f)

        import pytest
        with pytest.raises(ValidationError):
            jsonschema_validate(instance=d, schema=json_schema)


class TestModelConstruction:
    def test_optional_fields_default_none(self):
        col = Column(name="test", type="INT")
        assert col.constraints is None
        assert col.comment is None

    def test_table_optional_fk(self):
        t = Table(
            table_name="test",
            comment="A test table for validating optional FK behavior.",
            columns=[
                Column(name="id", type="SERIAL", constraints="PRIMARY KEY"),
                Column(name="submenu_id", type="INT"),
            ],
        )
        assert t.foreign_keys is None

    def test_enum_optional_description(self):
        e = EnumType(type_name="tst.status_enum", values=["active", "inactive"])
        assert e.description is None

    def test_schema_optional_assumptions(self):
        schema = MantaraSchema(
            system_name="Test",
            schema_name="tst",
            description="Test.",
            menus=[
                Menu(
                    menu_id=1,
                    menu_name="Main",
                    sequence_number=1,
                    description="Main.",
                    submenus=[
                        Submenu(submenu_id=101, submenu_name="Sub",
                                sequence_number=1, description="Sub."),
                    ],
                ),
            ],
        )
        assert schema.assumptions is None
        assert schema.open_questions is None

    def test_assumptions_serialized(self):
        schema = MantaraSchema(
            system_name="Test",
            schema_name="tst",
            description="Test.",
            menus=[
                Menu(
                    menu_id=1,
                    menu_name="Main",
                    sequence_number=1,
                    description="Main.",
                    submenus=[
                        Submenu(submenu_id=101, submenu_name="Sub",
                                sequence_number=1, description="Sub."),
                    ],
                ),
            ],
            assumptions=["Single-tenant system", "UTC timezone"],
            open_questions=["What auth method to use?"],
        )
        d = schema.to_json_dict()
        assert "assumptions" in d
        assert len(d["assumptions"]) == 2
        assert "open_questions" in d
        assert len(d["open_questions"]) == 1

    def test_assumptions_excluded_when_none(self):
        schema = MantaraSchema(
            system_name="Test",
            schema_name="tst",
            description="Test.",
            menus=[
                Menu(
                    menu_id=1,
                    menu_name="Main",
                    sequence_number=1,
                    description="Main.",
                    submenus=[
                        Submenu(submenu_id=101, submenu_name="Sub",
                                sequence_number=1, description="Sub."),
                    ],
                ),
            ],
        )
        d = schema.to_json_dict()
        assert "assumptions" not in d
        assert "open_questions" not in d
