"""Unit tests for error controller module.

Tests custom 404 and 500 error handlers for both API and HTML responses.
"""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from fastapi import HTTPException, Request

from src.server.controllers.error_controller import (
    not_found_handler,
    server_error_handler,
)


def _make_request(path: str = "/") -> MagicMock:
    """Create a mock Request object with a given path."""
    request = MagicMock(spec=Request)
    url = MagicMock()
    url.path = path
    request.url = url
    return request


class TestNotFoundHandler:
    """Tests for the 404 not_found_handler."""

    @pytest.mark.asyncio
    async def test_api_path_returns_json(self):
        """API paths get a JSON response with 404 status."""
        request = _make_request("/api/anime/123")
        exc = HTTPException(status_code=404)
        resp = await not_found_handler(request, exc)
        assert resp.status_code == 404
        assert resp.body is not None

    @pytest.mark.asyncio
    @patch("src.server.controllers.error_controller.render_template")
    async def test_web_path_renders_template(self, mock_render):
        """Non-API paths render the error template."""
        mock_render.return_value = MagicMock(status_code=404)
        request = _make_request("/anime/details")
        exc = HTTPException(status_code=404)
        await not_found_handler(request, exc)
        mock_render.assert_called_once_with(
            "error.html",
            request,
            context={"error": "Page not found", "status_code": 404},
            title="404 - Not Found",
        )

    @pytest.mark.asyncio
    async def test_api_json_structure(self):
        """API 404 response has 'detail' field."""
        import json
        request = _make_request("/api/missing")
        exc = HTTPException(status_code=404)
        resp = await not_found_handler(request, exc)
        body = json.loads(resp.body)
        assert body["detail"] == "API endpoint not found"


class TestServerErrorHandler:
    """Tests for the 500 server_error_handler."""

    @pytest.mark.asyncio
    async def test_api_path_returns_json(self):
        """API paths get a JSON response with 500 status."""
        request = _make_request("/api/download")
        exc = RuntimeError("crash")
        resp = await server_error_handler(request, exc)
        assert resp.status_code == 500

    @pytest.mark.asyncio
    @patch("src.server.controllers.error_controller.render_template")
    async def test_web_path_renders_template(self, mock_render):
        """Non-API paths render the error template."""
        mock_render.return_value = MagicMock(status_code=500)
        request = _make_request("/settings")
        exc = RuntimeError("crash")
        await server_error_handler(request, exc)
        mock_render.assert_called_once_with(
            "error.html",
            request,
            context={"error": "Internal server error", "status_code": 500},
            title="500 - Server Error",
        )

    @pytest.mark.asyncio
    async def test_api_error_does_not_expose_stack_trace(self):
        """API 500 response doesn't contain the actual error message."""
        import json
        request = _make_request("/api/vulnerable")
        exc = RuntimeError("secret database credentials")
        resp = await server_error_handler(request, exc)
        body = json.loads(resp.body)
        assert "secret" not in body["detail"]
        assert body["detail"] == "Internal server error"

    @pytest.mark.asyncio
    async def test_api_path_detection(self):
        """Correctly distinguishes API vs web paths."""
        api_request = _make_request("/api/test")
        web_request = _make_request("/dashboard")

        # API path returns JSONResponse
        from fastapi.responses import JSONResponse
        resp = await server_error_handler(api_request, Exception("err"))
        assert isinstance(resp, JSONResponse)
