"""Tests for app/views/status.py module."""

from unittest.mock import MagicMock, Mock, mock_open, patch

import pytest


@pytest.mark.unit
class TestRequireAdmin:
    """Unit tests for the _require_admin helper in views/status.py."""

    def test_returns_none_when_no_user_in_session(self):
        """_require_admin returns None when session has no user."""
        from app.views.status import _require_admin

        mock_request = MagicMock()
        mock_request.session = {}

        result = _require_admin(mock_request)
        assert result is None

    def test_returns_none_for_non_admin_user(self):
        """_require_admin returns None when user is not an admin."""
        from app.views.status import _require_admin

        mock_request = MagicMock()
        mock_request.session = {"user": {"email": "user@example.com", "is_admin": False}}

        result = _require_admin(mock_request)
        assert result is None

    def test_logs_warning_for_non_admin(self):
        """_require_admin logs a warning when a non-admin attempts access."""
        from app.views.status import _require_admin

        mock_request = MagicMock()
        mock_request.session = {"user": {"email": "user@example.com", "is_admin": False}}

        with patch("app.views.status.logger") as mock_logger:
            _require_admin(mock_request)
            mock_logger.warning.assert_called_once()

    def test_logs_warning_when_no_user(self):
        """_require_admin logs a warning when there is no user in the session."""
        from app.views.status import _require_admin

        mock_request = MagicMock()
        mock_request.session = {}

        with patch("app.views.status.logger") as mock_logger:
            _require_admin(mock_request)
            mock_logger.warning.assert_called_once()

    def test_returns_user_for_admin(self):
        """_require_admin returns the user dict when the user is an admin."""
        from app.views.status import _require_admin

        admin_user = {"email": "admin@example.com", "is_admin": True}
        mock_request = MagicMock()
        mock_request.session = {"user": admin_user}

        result = _require_admin(mock_request)
        assert result == admin_user


@pytest.mark.integration
class TestStatusViews:
    """Tests for status view routes."""

    def test_status_dashboard(self, client):
        """Test status dashboard page returns 200 (auth disabled in tests)."""
        response = client.get("/status")
        assert response.status_code == 200


@pytest.mark.unit
class TestStatusDashboard:
    """Tests for status_dashboard function."""

    @pytest.mark.asyncio
    async def test_redirects_non_admin_to_home(self):
        """status_dashboard redirects to '/' when user is not an admin."""
        from fastapi.responses import RedirectResponse

        from app.views.status import status_dashboard

        mock_request = MagicMock()
        mock_request.session = {"user": {"email": "user@example.com", "is_admin": False}}

        result = await status_dashboard(mock_request)

        assert isinstance(result, RedirectResponse)
        assert result.status_code == 302
        assert result.headers["location"] == "/"

    @pytest.mark.asyncio
    async def test_redirects_when_no_user_in_session(self):
        """status_dashboard redirects to '/' when no user is in the session."""
        from fastapi.responses import RedirectResponse

        from app.views.status import status_dashboard

        mock_request = MagicMock()
        mock_request.session = {}

        result = await status_dashboard(mock_request)

        assert isinstance(result, RedirectResponse)
        assert result.status_code == 302

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @patch("app.views.status.os.path.exists")
    @pytest.mark.asyncio
    async def test_returns_template_for_admin_user(self, mock_exists, mock_settings, mock_templates, mock_providers):
        """status_dashboard renders the template for an authenticated admin user."""
        from app.views.status import status_dashboard

        mock_exists.return_value = False
        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.debug = False
        mock_settings.git_sha = "abc123"
        mock_settings.notification_urls = []

        mock_request = MagicMock()
        mock_request.session = {"user": {"email": "admin@example.com", "is_admin": True}}

        await status_dashboard(mock_request)

        mock_templates.TemplateResponse.assert_called_once()
        call_args = mock_templates.TemplateResponse.call_args
        assert call_args[0][0] == "status_dashboard.html"

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @patch("app.views.status.os.path.exists")
    @pytest.mark.asyncio
    async def test_status_dashboard_returns_template(self, mock_exists, mock_settings, mock_templates, mock_providers):
        """Test status dashboard returns template response."""
        from app.views.status import status_dashboard

        mock_exists.return_value = False
        mock_providers.return_value = {
            "AI Provider": {"configured": True, "status": "success"},
            "Azure AI": {"configured": False},
        }
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.debug = False
        mock_settings.git_sha = "abc123"
        mock_settings.runtime_info = "Python 3.11"
        mock_settings.notification_urls = []

        mock_request = Mock()

        result = await status_dashboard(mock_request)

        mock_templates.TemplateResponse.assert_called_once()
        call_args = mock_templates.TemplateResponse.call_args
        assert call_args[0][0] == "status_dashboard.html"

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @patch("app.views.status.os.path.exists")
    @patch("builtins.open", new_callable=mock_open, read_data="12:docker:/container_id")
    @pytest.mark.asyncio
    async def test_detects_docker_environment(
        self, mock_file, mock_exists, mock_settings, mock_templates, mock_providers
    ):
        """Test detects Docker environment."""
        from app.views.status import status_dashboard

        mock_exists.return_value = True
        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.git_sha = "abc123"
        mock_settings.notification_urls = []

        mock_request = Mock()

        await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        assert context["container_info"]["is_docker"] is True

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @patch("app.views.status.os.path.exists")
    @pytest.mark.asyncio
    async def test_handles_non_docker_environment(self, mock_exists, mock_settings, mock_templates, mock_providers):
        """Test handles non-Docker environment."""
        from app.views.status import status_dashboard

        mock_exists.return_value = False
        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.git_sha = "abc123"
        mock_settings.notification_urls = []

        mock_request = Mock()

        await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        assert context["container_info"]["is_docker"] is False

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @pytest.mark.asyncio
    async def test_includes_git_sha_in_context(self, mock_settings, mock_templates, mock_providers):
        """Test includes git SHA in context."""
        from app.views.status import status_dashboard

        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.git_sha = "abc1234567890"
        mock_settings.notification_urls = []

        mock_request = Mock()

        await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        assert "git_sha" in context["container_info"]

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @pytest.mark.asyncio
    async def test_includes_notification_urls(self, mock_settings, mock_templates, mock_providers):
        """Test includes notification URLs in context."""
        from app.views.status import status_dashboard

        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.git_sha = "abc123"
        mock_settings.notification_urls = ["https://webhook.example.com/notify"]

        mock_request = Mock()

        await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        assert context["settings"]["notification_urls"] == ["https://webhook.example.com/notify"]


@pytest.mark.unit
class TestContainerInfoDetection:
    """Tests for container information detection logic."""

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @patch("app.views.status.os.path.exists")
    @patch("builtins.open", side_effect=IOError("Permission denied"))
    @pytest.mark.asyncio
    async def test_handles_cgroup_read_error(
        self, mock_file, mock_exists, mock_settings, mock_templates, mock_providers
    ):
        """Test handles cgroup file read errors."""
        from app.views.status import status_dashboard

        mock_exists.return_value = True  # Docker env exists
        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.git_sha = "abc123"
        mock_settings.notification_urls = []

        mock_request = Mock()

        await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        # Should handle error gracefully and set id to Unknown
        assert context["container_info"]["is_docker"] is True
        assert context["container_info"]["id"] == "Unknown"

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @patch("app.views.status.os.path.exists")
    @patch("builtins.open", new_callable=mock_open, read_data="12:cpuset:/system.slice\n13:memory:/user.slice")
    @pytest.mark.asyncio
    async def test_handles_cgroup_without_docker(
        self, mock_file, mock_exists, mock_settings, mock_templates, mock_providers
    ):
        """Test handles cgroup without docker in path."""
        from app.views.status import status_dashboard

        mock_exists.return_value = True  # Docker env exists
        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.git_sha = "abc123"
        mock_settings.notification_urls = []

        mock_request = Mock()

        await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        # When docker not found in cgroup, id is not set in the code
        # The loop completes without setting id, so it won't be in container_info
        assert context["container_info"]["is_docker"] is True

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @patch("app.views.status.os.path.exists")
    @patch("builtins.open", new_callable=mock_open, read_data="12:docker:/abc123456789")
    @pytest.mark.asyncio
    async def test_handles_unknown_git_sha_string(
        self, mock_file, mock_exists, mock_settings, mock_templates, mock_providers
    ):
        """Test handles 'unknown' git_sha string value."""
        from app.views.status import status_dashboard

        mock_exists.return_value = True
        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.git_sha = "unknown"
        mock_settings.notification_urls = []

        mock_request = Mock()

        await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        # Should handle "unknown" string and set to Unknown
        assert context["container_info"]["git_sha"] == "Unknown"

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @patch("app.views.status.os.path.exists")
    @pytest.mark.asyncio
    async def test_handles_complete_exception_in_container_info(
        self, mock_exists, mock_settings, mock_templates, mock_providers
    ):
        """Test handles complete exception in container info extraction."""
        from app.views.status import status_dashboard

        # Simulate exception when checking Docker env
        mock_exists.side_effect = Exception("Unexpected error")
        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.notification_urls = []

        mock_request = Mock()

        await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        # Should have fallback container_info
        assert context["container_info"]["is_docker"] is False
        assert context["container_info"]["id"] == "Unknown"
        assert context["container_info"]["git_sha"] == "Unknown"

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.settings")
    @patch("app.views.status.os.path.exists")
    @pytest.mark.asyncio
    async def test_handles_null_git_sha(self, mock_exists, mock_settings, mock_templates, mock_providers):
        """Test handles None/null git_sha value."""
        from app.views.status import status_dashboard

        mock_exists.return_value = False  # Not in Docker
        mock_providers.return_value = {}
        mock_settings.version = "1.0.0"
        mock_settings.build_date = "2024-01-01"
        mock_settings.git_sha = None  # None value
        mock_settings.notification_urls = []

        mock_request = Mock()

        await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        # Should handle None and set to Unknown
        assert context["container_info"]["git_sha"] == "Unknown"

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.os.path.exists")
    @patch("builtins.open", new_callable=mock_open, read_data="12:docker:/abc123456789")
    @pytest.mark.asyncio
    async def test_handles_git_sha_exception_in_docker(self, mock_file, mock_exists, mock_templates, mock_providers):
        """Test handles exception when reading git_sha in Docker environment (lines 53-54)."""
        import types

        from app.views.status import status_dashboard

        mock_exists.return_value = True
        mock_providers.return_value = {}
        # SimpleNamespace without git_sha causes AttributeError on access → triggers except block
        fake_settings = types.SimpleNamespace(version="1.0.0")

        with patch("app.views.status.settings", fake_settings):
            mock_request = Mock()
            await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        assert context["container_info"]["is_docker"] is True
        assert context["container_info"]["git_sha"] == "Unknown"

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.os.path.exists")
    @patch("builtins.open", new_callable=mock_open, read_data="12:docker:/abc123456789")
    @pytest.mark.asyncio
    async def test_handles_runtime_info_exception_in_docker(
        self, mock_file, mock_exists, mock_templates, mock_providers
    ):
        """Test handles exception when reading runtime_info in Docker environment (lines 59-60)."""
        import types

        from app.views.status import status_dashboard

        mock_exists.return_value = True
        mock_providers.return_value = {}
        # SimpleNamespace has git_sha but not runtime_info → triggers except block at line 59-60
        fake_settings = types.SimpleNamespace(version="1.0.0", git_sha="abc1234567890")

        with patch("app.views.status.settings", fake_settings):
            mock_request = Mock()
            await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        assert context["container_info"]["is_docker"] is True
        # runtime_info should not be in container_info when it raises an exception
        assert "runtime_info" not in context["container_info"]

    @patch("app.views.status.get_provider_status")
    @patch("app.views.status.templates")
    @patch("app.views.status.os.path.exists")
    @pytest.mark.asyncio
    async def test_handles_git_sha_exception_in_non_docker(self, mock_exists, mock_templates, mock_providers):
        """Test handles exception when reading git_sha in non-Docker environment (lines 68-69)."""
        import types

        from app.views.status import status_dashboard

        mock_exists.return_value = False  # Not in Docker
        mock_providers.return_value = {}
        # SimpleNamespace without git_sha causes AttributeError on access → triggers except block
        fake_settings = types.SimpleNamespace(version="1.0.0")

        with patch("app.views.status.settings", fake_settings):
            mock_request = Mock()
            await status_dashboard(mock_request)

        call_args = mock_templates.TemplateResponse.call_args
        context = call_args[0][1]
        assert context["container_info"]["is_docker"] is False
        assert context["container_info"]["git_sha"] == "Unknown"


@pytest.mark.integration
class TestStatusEndpointsRequireAuth:
    """Tests for status endpoint authentication."""

    def test_status_dashboard_requires_login(self, client):
        """Test status dashboard requires authentication."""
        # Should return 200 or redirect to login
        response = client.get("/status", follow_redirects=False)
        assert response.status_code in [200, 302, 401]
