# tests/schemas/syndarix/test_project_schemas.py
"""
Tests for Project schema validation.
"""

import uuid

import pytest
from pydantic import ValidationError

from app.schemas.syndarix import (
    AutonomyLevel,
    ProjectCreate,
    ProjectStatus,
    ProjectUpdate,
)


class TestProjectCreateValidation:
    """Tests for ProjectCreate schema validation."""

    def test_valid_project_create(self, valid_project_data):
        """Test creating project with valid data."""
        project = ProjectCreate(**valid_project_data)

        assert project.name == "Test Project"
        assert project.slug == "test-project"
        assert project.description == "A test project"

    def test_project_create_defaults(self):
        """Test that defaults are applied correctly."""
        project = ProjectCreate(
            name="Minimal Project",
            slug="minimal-project",
        )

        assert project.autonomy_level == AutonomyLevel.MILESTONE
        assert project.status == ProjectStatus.ACTIVE
        assert project.settings == {}
        assert project.owner_id is None

    def test_project_create_with_owner(self, valid_project_data):
        """Test creating project with owner ID."""
        owner_id = uuid.uuid4()
        project = ProjectCreate(
            **valid_project_data,
            owner_id=owner_id,
        )

        assert project.owner_id == owner_id

    def test_project_create_name_empty_fails(self):
        """Test that empty name raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            ProjectCreate(
                name="",
                slug="valid-slug",
            )

        errors = exc_info.value.errors()
        assert any("name" in str(e) for e in errors)

    def test_project_create_name_whitespace_only_fails(self):
        """Test that whitespace-only name raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            ProjectCreate(
                name="   ",
                slug="valid-slug",
            )

        errors = exc_info.value.errors()
        assert any("name" in str(e) for e in errors)

    def test_project_create_name_stripped(self):
        """Test that name is stripped of leading/trailing whitespace."""
        project = ProjectCreate(
            name="  Padded Name  ",
            slug="padded-slug",
        )

        assert project.name == "Padded Name"

    def test_project_create_slug_required(self):
        """Test that slug is required for create."""
        with pytest.raises(ValidationError) as exc_info:
            ProjectCreate(name="No Slug Project")

        errors = exc_info.value.errors()
        assert any("slug" in str(e).lower() for e in errors)


class TestProjectSlugValidation:
    """Tests for Project slug validation."""

    def test_valid_slugs(self):
        """Test various valid slug formats."""
        valid_slugs = [
            "simple",
            "with-hyphens",
            "has123numbers",
            "mix3d-with-hyphen5",
            "a",  # Single character
        ]

        for slug in valid_slugs:
            project = ProjectCreate(
                name="Test Project",
                slug=slug,
            )
            assert project.slug == slug

    def test_invalid_slug_uppercase(self):
        """Test that uppercase letters in slug raise ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            ProjectCreate(
                name="Test Project",
                slug="Invalid-Uppercase",
            )

        errors = exc_info.value.errors()
        assert any("slug" in str(e).lower() for e in errors)

    def test_invalid_slug_special_chars(self):
        """Test that special characters in slug raise ValidationError."""
        invalid_slugs = [
            "has_underscore",
            "has.dot",
            "has@symbol",
            "has space",
            "has/slash",
        ]

        for slug in invalid_slugs:
            with pytest.raises(ValidationError):
                ProjectCreate(
                    name="Test Project",
                    slug=slug,
                )

    def test_invalid_slug_starts_with_hyphen(self):
        """Test that slug starting with hyphen raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            ProjectCreate(
                name="Test Project",
                slug="-invalid-start",
            )

        errors = exc_info.value.errors()
        assert any("hyphen" in str(e).lower() for e in errors)

    def test_invalid_slug_ends_with_hyphen(self):
        """Test that slug ending with hyphen raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            ProjectCreate(
                name="Test Project",
                slug="invalid-end-",
            )

        errors = exc_info.value.errors()
        assert any("hyphen" in str(e).lower() for e in errors)

    def test_invalid_slug_consecutive_hyphens(self):
        """Test that consecutive hyphens in slug raise ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            ProjectCreate(
                name="Test Project",
                slug="invalid--consecutive",
            )

        errors = exc_info.value.errors()
        assert any("consecutive" in str(e).lower() for e in errors)


class TestProjectUpdateValidation:
    """Tests for ProjectUpdate schema validation."""

    def test_project_update_partial(self):
        """Test updating only some fields."""
        update = ProjectUpdate(
            name="Updated Name",
        )

        assert update.name == "Updated Name"
        assert update.slug is None
        assert update.description is None
        assert update.autonomy_level is None
        assert update.status is None

    def test_project_update_all_fields(self):
        """Test updating all fields."""
        owner_id = uuid.uuid4()
        update = ProjectUpdate(
            name="Updated Name",
            slug="updated-slug",
            description="Updated description",
            autonomy_level=AutonomyLevel.AUTONOMOUS,
            status=ProjectStatus.PAUSED,
            settings={"key": "value"},
            owner_id=owner_id,
        )

        assert update.name == "Updated Name"
        assert update.slug == "updated-slug"
        assert update.autonomy_level == AutonomyLevel.AUTONOMOUS
        assert update.status == ProjectStatus.PAUSED

    def test_project_update_empty_name_fails(self):
        """Test that empty name in update raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            ProjectUpdate(name="")

        errors = exc_info.value.errors()
        assert any("name" in str(e) for e in errors)

    def test_project_update_slug_validation(self):
        """Test that slug validation applies to updates too."""
        with pytest.raises(ValidationError):
            ProjectUpdate(slug="Invalid-Slug")


class TestProjectEnums:
    """Tests for Project enum validation."""

    def test_valid_autonomy_levels(self):
        """Test all valid autonomy levels."""
        for level in AutonomyLevel:
            # Replace underscores with hyphens for valid slug
            slug_suffix = level.value.replace("_", "-")
            project = ProjectCreate(
                name="Test Project",
                slug=f"project-{slug_suffix}",
                autonomy_level=level,
            )
            assert project.autonomy_level == level

    def test_invalid_autonomy_level(self):
        """Test that invalid autonomy level raises ValidationError."""
        with pytest.raises(ValidationError):
            ProjectCreate(
                name="Test Project",
                slug="invalid-autonomy",
                autonomy_level="invalid",  # type: ignore
            )

    def test_valid_project_statuses(self):
        """Test all valid project statuses."""
        for status in ProjectStatus:
            project = ProjectCreate(
                name="Test Project",
                slug=f"project-status-{status.value}",
                status=status,
            )
            assert project.status == status

    def test_invalid_project_status(self):
        """Test that invalid project status raises ValidationError."""
        with pytest.raises(ValidationError):
            ProjectCreate(
                name="Test Project",
                slug="invalid-status",
                status="invalid",  # type: ignore
            )


class TestProjectSettings:
    """Tests for Project settings validation."""

    def test_settings_empty_dict(self):
        """Test that empty settings dict is valid."""
        project = ProjectCreate(
            name="Test Project",
            slug="empty-settings",
            settings={},
        )
        assert project.settings == {}

    def test_settings_complex_structure(self):
        """Test that complex settings structure is valid."""
        complex_settings = {
            "mcp_servers": ["gitea", "slack"],
            "webhooks": {
                "on_issue_created": "https://example.com",
            },
            "flags": True,
            "count": 42,
        }
        project = ProjectCreate(
            name="Test Project",
            slug="complex-settings",
            settings=complex_settings,
        )
        assert project.settings == complex_settings

    def test_settings_default_to_empty_dict(self):
        """Test that settings default to empty dict when not provided."""
        project = ProjectCreate(
            name="Test Project",
            slug="default-settings",
        )
        assert project.settings == {}
