"""Tests for the streaming generator."""

from __future__ import annotations

from pathlib import Path

import pytest

from prisme.generators.backend.streaming import StreamingGenerator
from prisme.generators.base import GeneratorContext
from prisme.spec.fields import FieldSpec, FieldType
from prisme.spec.model import ModelSpec
from prisme.spec.project import (
    ExposureConfig,
    ProjectSpec,
    SSEConfig,
    StreamingBusConfig,
    WebSocketConfig,
)
from prisme.spec.stack import StackSpec


@pytest.fixture
def streaming_stack_spec() -> StackSpec:
    """Create a stack spec for streaming tests."""
    return StackSpec(
        name="test-project",
        version="1.0.0",
        description="Test project",
        models=[
            ModelSpec(
                name="Customer",
                description="Customer entity",
                fields=[
                    FieldSpec(name="name", type=FieldType.STRING, required=True),
                    FieldSpec(name="email", type=FieldType.STRING, required=True),
                ],
            ),
            ModelSpec(
                name="Order",
                description="Order entity",
                fields=[
                    FieldSpec(name="total", type=FieldType.FLOAT, required=True),
                ],
            ),
        ],
    )


@pytest.fixture
def streaming_project_spec() -> ProjectSpec:
    """Create a project spec with streaming enabled."""
    return ProjectSpec(
        name="test-project",
        exposure=ExposureConfig(
            streaming=StreamingBusConfig(enabled=True),
            sse=SSEConfig(enabled=True),
            websocket=WebSocketConfig(enabled=True),
        ),
    )


@pytest.fixture
def streaming_bus_only_project_spec() -> ProjectSpec:
    """Create a project spec with only event bus enabled (no SSE/WS)."""
    return ProjectSpec(
        name="test-project",
        exposure=ExposureConfig(
            streaming=StreamingBusConfig(enabled=True),
        ),
    )


class TestStreamingGenerator:
    """Tests for StreamingGenerator."""

    def test_generates_event_bus(
        self,
        streaming_stack_spec: StackSpec,
        streaming_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """Event bus should always be generated when streaming is enabled."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        event_bus_files = [f for f in files if "event_bus.py" in str(f.path)]
        assert len(event_bus_files) >= 1, "Should generate event_bus.py"

    def test_generates_sse_when_enabled(
        self,
        streaming_stack_spec: StackSpec,
        streaming_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """SSE handler and routes should be generated when SSE is enabled."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        sse_files = [f for f in files if "sse_" in str(f.path)]
        assert len(sse_files) >= 2, "Should generate sse_handler.py and sse_routes.py"

    def test_generates_ws_when_enabled(
        self,
        streaming_stack_spec: StackSpec,
        streaming_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """WebSocket handler and routes should be generated when WS is enabled."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        ws_files = [f for f in files if "ws_" in str(f.path)]
        assert len(ws_files) >= 2, "Should generate ws_handler.py and ws_routes.py"

    def test_skips_sse_when_disabled(
        self,
        streaming_stack_spec: StackSpec,
        streaming_bus_only_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """SSE files should not be generated when SSE is disabled."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_bus_only_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        sse_files = [f for f in files if "sse_" in str(f.path)]
        assert len(sse_files) == 0, "Should not generate SSE files when disabled"

    def test_skips_ws_when_disabled(
        self,
        streaming_stack_spec: StackSpec,
        streaming_bus_only_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """WebSocket files should not be generated when WS is disabled."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_bus_only_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        ws_files = [f for f in files if "ws_" in str(f.path)]
        assert len(ws_files) == 0, "Should not generate WS files when disabled"

    def test_event_bus_contains_event_type_enum(
        self,
        streaming_stack_spec: StackSpec,
        streaming_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """Event bus should contain EventType enum with CRUD events."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        event_bus_file = next(f for f in files if "event_bus.py" in str(f.path))
        assert "class EventType" in event_bus_file.content
        assert "CREATED" in event_bus_file.content
        assert "UPDATED" in event_bus_file.content
        assert "DELETED" in event_bus_file.content

    def test_event_bus_contains_stream_event(
        self,
        streaming_stack_spec: StackSpec,
        streaming_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """Event bus should contain StreamEvent dataclass."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        event_bus_file = next(f for f in files if "event_bus.py" in str(f.path))
        assert "class StreamEvent" in event_bus_file.content
        assert "class EventBus" in event_bus_file.content
        assert "event_bus = EventBus()" in event_bus_file.content

    def test_sse_routes_have_per_model_endpoints(
        self,
        streaming_stack_spec: StackSpec,
        streaming_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """SSE routes should have per-model endpoints."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        sse_routes = next(f for f in files if "sse_routes.py" in str(f.path))
        assert "customer" in sse_routes.content.lower()
        assert "order" in sse_routes.content.lower()

    def test_ws_routes_have_per_model_endpoints(
        self,
        streaming_stack_spec: StackSpec,
        streaming_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """WebSocket routes should have per-model endpoints."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        ws_routes = next(f for f in files if "ws_routes.py" in str(f.path))
        assert "customer" in ws_routes.content.lower()
        assert "order" in ws_routes.content.lower()

    def test_generates_init_files(
        self,
        streaming_stack_spec: StackSpec,
        streaming_project_spec: ProjectSpec,
        tmp_path: Path,
    ) -> None:
        """Should generate __init__.py files."""
        context = GeneratorContext(
            domain_spec=streaming_stack_spec,
            output_dir=tmp_path,
            project_spec=streaming_project_spec,
        )
        gen = StreamingGenerator(context)
        files = gen.generate_files()

        init_files = [f for f in files if f.path.name == "__init__.py"]
        assert len(init_files) >= 2, "Should generate at least 2 __init__.py files"
