"""Tests for streaming-related spec models."""

from __future__ import annotations

import pytest

from prisme.spec.exposure import (
    GRPCExposure,
    SSEExposure,
    StreamingConfig,
    WebSocketExposure,
)
from prisme.spec.overrides import DeliveryOverrides
from prisme.spec.project import (
    ExposureConfig,
    GRPCConfig,
    ProjectSpec,
    SSEConfig,
    StreamingBusConfig,
    WebSocketConfig,
)


class TestStreamingConfig:
    """Tests for StreamingConfig model."""

    def test_defaults(self) -> None:
        config = StreamingConfig()
        assert config.enabled is False
        assert config.channels == ["graphql", "sse", "websocket"]
        assert config.events == ["created", "updated", "deleted"]
        assert config.custom_events == []
        assert config.include_payload is True

    def test_custom_events(self) -> None:
        config = StreamingConfig(
            enabled=True,
            custom_events=["status_changed", "job_progress"],
        )
        assert config.enabled is True
        assert "status_changed" in config.custom_events
        assert "job_progress" in config.custom_events

    def test_channel_filtering(self) -> None:
        config = StreamingConfig(
            enabled=True,
            channels=["sse"],
        )
        assert config.channels == ["sse"]

    def test_forbids_extra(self) -> None:
        with pytest.raises(ValueError, match="Extra inputs are not permitted"):
            StreamingConfig(enabled=True, unknown_field="bad")  # type: ignore[call-arg]


class TestGRPCExposure:
    """Tests for GRPCExposure model."""

    def test_defaults(self) -> None:
        grpc = GRPCExposure()
        assert grpc.enabled is True
        assert grpc.service_name is None
        assert grpc.streaming_methods is True
        assert grpc.bidirectional_streaming is False
        assert grpc.auth_required is True

    def test_custom_service_name(self) -> None:
        grpc = GRPCExposure(
            service_name="MyCustomService",
            package_name="com.example",
        )
        assert grpc.service_name == "MyCustomService"
        assert grpc.package_name == "com.example"

    def test_bidirectional_streaming(self) -> None:
        grpc = GRPCExposure(bidirectional_streaming=True)
        assert grpc.bidirectional_streaming is True

    def test_operations(self) -> None:
        grpc = GRPCExposure()
        assert grpc.operations.create is True
        assert grpc.operations.read is True
        assert grpc.operations.update is True
        assert grpc.operations.delete is True
        assert grpc.operations.list is True


class TestSSEExposure:
    """Tests for SSEExposure model."""

    def test_defaults(self) -> None:
        sse = SSEExposure()
        assert sse.enabled is True
        assert sse.events == ["created", "updated", "deleted"]
        assert sse.keepalive_interval == 30
        assert sse.path_prefix is None
        assert sse.auth_required is True

    def test_custom_events(self) -> None:
        sse = SSEExposure(events=["created", "updated"])
        assert sse.events == ["created", "updated"]


class TestWebSocketExposure:
    """Tests for WebSocketExposure model."""

    def test_defaults(self) -> None:
        ws = WebSocketExposure()
        assert ws.enabled is True
        assert ws.events == ["created", "updated", "deleted"]
        assert ws.heartbeat_interval == 30
        assert ws.path_prefix is None
        assert ws.auth_required is True

    def test_custom_heartbeat(self) -> None:
        ws = WebSocketExposure(heartbeat_interval=15)
        assert ws.heartbeat_interval == 15


class TestGRPCConfig:
    """Tests for project-level GRPCConfig."""

    def test_defaults(self) -> None:
        config = GRPCConfig()
        assert config.enabled is False
        assert config.port == 50051
        assert config.reflection is True

    def test_enabled(self) -> None:
        config = GRPCConfig(enabled=True, port=9090)
        assert config.enabled is True
        assert config.port == 9090


class TestSSEConfig:
    """Tests for project-level SSEConfig."""

    def test_defaults(self) -> None:
        config = SSEConfig()
        assert config.enabled is False
        assert config.path == "/api/v1/stream"


class TestWebSocketConfig:
    """Tests for project-level WebSocketConfig."""

    def test_defaults(self) -> None:
        config = WebSocketConfig()
        assert config.enabled is False
        assert config.path == "/ws"


class TestStreamingBusConfig:
    """Tests for StreamingBusConfig."""

    def test_defaults(self) -> None:
        config = StreamingBusConfig()
        assert config.enabled is False
        assert config.backend == "memory"

    def test_redis_backend(self) -> None:
        config = StreamingBusConfig(enabled=True, backend="redis")
        assert config.backend == "redis"


class TestExposureConfigIntegration:
    """Tests for new fields on ExposureConfig."""

    def test_exposure_config_has_new_fields(self) -> None:
        config = ExposureConfig()
        assert config.grpc.enabled is False
        assert config.sse.enabled is False
        assert config.websocket.enabled is False
        assert config.streaming.enabled is False

    def test_all_enabled(self) -> None:
        config = ExposureConfig(
            grpc=GRPCConfig(enabled=True),
            sse=SSEConfig(enabled=True),
            websocket=WebSocketConfig(enabled=True),
            streaming=StreamingBusConfig(enabled=True),
        )
        assert config.grpc.enabled is True
        assert config.sse.enabled is True
        assert config.websocket.enabled is True
        assert config.streaming.enabled is True


class TestDeliveryOverridesStreaming:
    """Tests for streaming fields on DeliveryOverrides."""

    def test_streaming_defaults(self) -> None:
        overrides = DeliveryOverrides()
        assert overrides.streaming is None
        assert overrides.streaming_channels is None
        assert overrides.streaming_events is None
        assert overrides.streaming_custom_events is None

    def test_streaming_override(self) -> None:
        overrides = DeliveryOverrides(
            streaming=True,
            streaming_channels=["sse", "websocket"],
            streaming_events=["created", "updated"],
            streaming_custom_events=["status_changed"],
        )
        assert overrides.streaming is True
        assert overrides.streaming_channels == ["sse", "websocket"]
        assert overrides.streaming_events == ["created", "updated"]
        assert overrides.streaming_custom_events == ["status_changed"]

    def test_streaming_disabled(self) -> None:
        overrides = DeliveryOverrides(streaming=False)
        assert overrides.streaming is False


class TestProjectSpecIntegration:
    """Tests for ProjectSpec with new configs."""

    def test_project_spec_defaults(self) -> None:
        spec = ProjectSpec(name="test-project")
        assert spec.exposure.grpc.enabled is False
        assert spec.exposure.sse.enabled is False
        assert spec.exposure.websocket.enabled is False
        assert spec.exposure.streaming.enabled is False

    def test_project_spec_generator_paths(self) -> None:
        spec = ProjectSpec(name="test-project")
        assert spec.generator.grpc_path == "api/grpc"
        assert spec.generator.grpc_generated_path == "api/grpc/_generated"
        assert spec.generator.streaming_path == "streaming"
        assert spec.generator.streaming_generated_path == "streaming/_generated"
