"""Tests for the Dory sidecar server."""

import pytest
from aiohttp import web
from aiohttp.test_utils import AioHTTPTestCase

from dory.sidecar.server import SidecarServer, SidecarConfig


class TestSidecarServer(AioHTTPTestCase):
    """Test the sidecar server endpoints."""

    async def get_application(self) -> web.Application:
        """Create the test application."""
        config = SidecarConfig(
            health_port=8080,
            app_port=None,  # No app to monitor
        )
        self.server = SidecarServer(config)
        return self.server._create_app()

    async def test_healthz_returns_200(self):
        """Test that /healthz always returns 200."""
        resp = await self.client.request("GET", "/healthz")
        assert resp.status == 200
        data = await resp.json()
        assert data["status"] == "healthy"
        assert data["sidecar"] is True

    async def test_ready_returns_200_without_app(self):
        """Test that /ready returns 200 when no app is configured."""
        resp = await self.client.request("GET", "/ready")
        assert resp.status == 200
        data = await resp.json()
        assert data["status"] == "ready"
        assert data["ready"] is True
        assert data["sidecar"] is True

    async def test_prestop_marks_shutting_down(self):
        """Test that /prestop marks server as shutting down."""
        # First call prestop
        resp = await self.client.request("GET", "/prestop")
        assert resp.status == 200
        data = await resp.json()
        assert data["status"] == "shutting_down"
        assert data["state_saved"] is False  # No SDK = no state

        # Now /ready should return 503
        resp = await self.client.request("GET", "/ready")
        assert resp.status == 503
        data = await resp.json()
        assert data["status"] == "shutting_down"

    async def test_metrics_returns_prometheus_format(self):
        """Test that /metrics returns Prometheus format."""
        resp = await self.client.request("GET", "/metrics")
        assert resp.status == 200
        assert resp.content_type == "text/plain"
        text = await resp.text()
        assert "dory_sidecar_up 1" in text
        assert "dory_sidecar_uptime_seconds" in text

    async def test_state_get_returns_no_state(self):
        """Test that GET /state indicates no state available."""
        resp = await self.client.request("GET", "/state")
        assert resp.status == 200
        data = await resp.json()
        assert data["state"] is None
        assert "does not use Dory SDK" in data["message"]

    async def test_state_post_returns_not_restored(self):
        """Test that POST /state indicates cannot restore."""
        resp = await self.client.request("POST", "/state")
        assert resp.status == 200
        data = await resp.json()
        assert data["restored"] is False
        assert "does not use Dory SDK" in data["message"]


class TestSidecarWithAppMonitoring(AioHTTPTestCase):
    """Test the sidecar server with app monitoring enabled."""

    async def get_application(self) -> web.Application:
        """Create the test application with app monitoring."""
        config = SidecarConfig(
            health_port=8080,
            app_port=9999,  # Non-existent port
            app_host="localhost",
            app_check_timeout=0.5,
            ready_requires_app=False,  # Don't fail ready
        )
        self.server = SidecarServer(config)
        return self.server._create_app()

    async def test_ready_reports_app_status(self):
        """Test that /ready reports app status even when app is down."""
        resp = await self.client.request("GET", "/ready")
        assert resp.status == 200  # Still ready because ready_requires_app=False
        data = await resp.json()
        assert data["ready"] is True
        assert data["app_ok"] is False  # But app check failed
        assert "not responding" in data["app_status"] or "connection failed" in data["app_status"].lower() or "failed" in data["app_status"].lower()


class TestSidecarRequiresApp(AioHTTPTestCase):
    """Test the sidecar when ready_requires_app is True."""

    async def get_application(self) -> web.Application:
        """Create the test application requiring app."""
        config = SidecarConfig(
            health_port=8080,
            app_port=9999,  # Non-existent port
            app_host="localhost",
            app_check_timeout=0.5,
            ready_requires_app=True,  # Fail ready if app is down
        )
        self.server = SidecarServer(config)
        return self.server._create_app()

    async def test_ready_fails_when_app_down(self):
        """Test that /ready returns 503 when app is down and required."""
        resp = await self.client.request("GET", "/ready")
        assert resp.status == 503
        data = await resp.json()
        assert data["ready"] is False
        assert data["status"] == "not_ready"


class TestSidecarConfig:
    """Test SidecarConfig loading."""

    def test_default_config(self):
        """Test default configuration values."""
        config = SidecarConfig()
        assert config.health_port == 8080
        assert config.app_port is None
        assert config.app_host == "localhost"
        assert config.ready_requires_app is False

    def test_config_from_env(self, monkeypatch):
        """Test loading configuration from environment."""
        monkeypatch.setenv("DORY_HEALTH_PORT", "9090")
        monkeypatch.setenv("DORY_APP_PORT", "8081")
        monkeypatch.setenv("DORY_APP_HOST", "127.0.0.1")
        monkeypatch.setenv("DORY_APP_HEALTH_PATH", "/health")
        monkeypatch.setenv("DORY_READY_REQUIRES_APP", "true")

        config = SidecarConfig.from_env()
        assert config.health_port == 9090
        assert config.app_port == 8081
        assert config.app_host == "127.0.0.1"
        assert config.app_health_path == "/health"
        assert config.ready_requires_app is True
