"""Unit tests for tree validation logic.

Tests validate_tree_structure, can_publish_tree, and edge cases.
No database required.
"""

from app.core.tree_validation import (
    TreeValidationError,
    can_publish_tree,
    validate_tree_structure,
)


class TestValidateTreeStructure:

    def test_valid_solution_tree(self):
        valid, errors = validate_tree_structure({
            "id": "root", "type": "solution", "title": "Done"
        })
        assert valid
        assert errors == []

    def test_valid_decision_tree_with_branches(self):
        valid, errors = validate_tree_structure({
            "id": "root",
            "type": "decision",
            "question": "Is it on?",
            "children": [
                {"id": "yes", "type": "solution", "title": "Great"},
                {"id": "no", "type": "action", "title": "Turn it on"},
            ],
        })
        assert valid
        assert errors == []

    def test_empty_structure_fails(self):
        valid, errors = validate_tree_structure({})
        assert not valid
        assert any("empty" in e["message"].lower() for e in errors)

    def test_missing_id_on_root(self):
        valid, errors = validate_tree_structure({"type": "solution", "title": "X"})
        assert not valid
        assert any("id" in e["field"] for e in errors)

    def test_missing_type_on_root(self):
        valid, errors = validate_tree_structure({"id": "root"})
        assert not valid
        assert any("type" in e["field"] for e in errors)

    def test_decision_missing_question(self):
        valid, errors = validate_tree_structure({
            "id": "root", "type": "decision"
        })
        assert not valid
        assert any("question" in e["message"].lower() for e in errors)

    def test_decision_with_empty_question(self):
        valid, errors = validate_tree_structure({
            "id": "root", "type": "decision", "question": ""
        })
        assert not valid

    def test_decision_with_one_child_fails(self):
        valid, errors = validate_tree_structure({
            "id": "root",
            "type": "decision",
            "question": "Q?",
            "children": [
                {"id": "only", "type": "solution", "title": "S"},
            ],
        })
        assert not valid
        assert any("2 branches" in e["message"] for e in errors)

    def test_decision_with_zero_children_passes(self):
        """Decision with no children is valid (leaf decision)."""
        valid, errors = validate_tree_structure({
            "id": "root", "type": "decision", "question": "Q?"
        })
        assert valid

    def test_action_missing_title_field(self):
        valid, errors = validate_tree_structure({
            "id": "root", "type": "action"
        })
        assert not valid
        assert any("title" in e["message"].lower() for e in errors)

    def test_action_with_empty_title(self):
        valid, errors = validate_tree_structure({
            "id": "root", "type": "action", "title": ""
        })
        assert not valid

    def test_solution_missing_title_field(self):
        valid, errors = validate_tree_structure({
            "id": "root", "type": "solution"
        })
        assert not valid
        assert any("title" in e["message"].lower() for e in errors)

    def test_solution_with_empty_title(self):
        valid, errors = validate_tree_structure({
            "id": "root", "type": "solution", "title": ""
        })
        assert not valid

    def test_unknown_node_type(self):
        valid, errors = validate_tree_structure({
            "id": "root", "type": "banana"
        })
        assert not valid
        assert any("unknown" in e["message"].lower() for e in errors)

    def test_child_missing_id(self):
        valid, errors = validate_tree_structure({
            "id": "root",
            "type": "decision",
            "question": "Q?",
            "children": [
                {"type": "solution", "title": "S1"},
                {"id": "c2", "type": "solution", "title": "S2"},
            ],
        })
        assert not valid
        assert any("id" in e["field"] for e in errors)

    def test_child_missing_type(self):
        valid, errors = validate_tree_structure({
            "id": "root",
            "type": "decision",
            "question": "Q?",
            "children": [
                {"id": "c1"},
                {"id": "c2", "type": "solution", "title": "S2"},
            ],
        })
        assert not valid
        assert any("type" in e["field"] for e in errors)

    def test_deeply_nested_validation(self):
        """Validates 3 levels deep."""
        valid, errors = validate_tree_structure({
            "id": "root",
            "type": "decision",
            "question": "Level 1?",
            "children": [
                {
                    "id": "l2a",
                    "type": "decision",
                    "question": "Level 2?",
                    "children": [
                        {"id": "l3a", "type": "solution", "title": "Deep"},
                        {"id": "l3b", "type": "solution"},  # Missing title
                    ],
                },
                {"id": "l2b", "type": "solution", "title": "Shallow"},
            ],
        })
        assert not valid
        assert any("l3b" in str(e) or "children[1]" in e["field"] for e in errors)

    def test_multiple_errors_collected(self):
        valid, errors = validate_tree_structure({
            "id": "root",
            "type": "decision",
            "question": "Q?",
            "children": [
                {"id": "c1", "type": "solution"},  # missing title
                {"id": "c2", "type": "action"},  # missing title
            ],
        })
        assert not valid
        assert len(errors) >= 2


class TestCanPublishTree:

    def test_valid_tree_can_publish(self):
        can, errors = can_publish_tree(
            {"id": "root", "type": "solution", "title": "Done"},
            "My Tree"
        )
        assert can
        assert errors == []

    def test_empty_name_cannot_publish(self):
        can, errors = can_publish_tree(
            {"id": "root", "type": "solution", "title": "Done"},
            ""
        )
        assert not can
        assert any("name" in e["field"] for e in errors)

    def test_whitespace_name_cannot_publish(self):
        can, errors = can_publish_tree(
            {"id": "root", "type": "solution", "title": "Done"},
            "   "
        )
        assert not can

    def test_none_name_cannot_publish(self):
        can, errors = can_publish_tree(
            {"id": "root", "type": "solution", "title": "Done"},
            None
        )
        assert not can

    def test_invalid_structure_cannot_publish(self):
        can, errors = can_publish_tree({}, "My Tree")
        assert not can
        assert len(errors) >= 1

    def test_both_name_and_structure_errors(self):
        can, errors = can_publish_tree({}, "")
        assert not can
        assert len(errors) >= 2  # name error + structure error


class TestTreeValidationError:

    def test_exception_attributes(self):
        err = TreeValidationError("field.name", "is required")
        assert err.field == "field.name"
        assert err.message == "is required"
        assert "field.name" in str(err)
