from __future__ import annotations

import unittest

from fastapi import APIRouter, Response
from fastapi.responses import PlainTextResponse
from fastapi.testclient import TestClient

from govoplan_core.auth import get_api_principal
from govoplan_core.core.registry import PlatformRegistry
from govoplan_core.server.fastapi import create_govoplan_app
from govoplan_core.server.platform import create_platform_router


class ConditionalRequestTests(unittest.TestCase):
    def _client(self) -> TestClient:
        router = APIRouter()

        @router.get("/json")
        def json_payload(value: str = "alpha") -> dict[str, str]:
            return {"value": value}

        @router.get("/cookie")
        def cookie_payload(response: Response) -> dict[str, bool]:
            response.set_cookie("govoplan-test", "changed")
            return {"ok": True}

        @router.get("/text")
        def text_payload() -> PlainTextResponse:
            return PlainTextResponse("plain")

        app = create_govoplan_app(
            title="conditional request test",
            version="test",
            registry=PlatformRegistry(),
            api_router=router,
        )
        return TestClient(app)

    def test_json_get_receives_private_etag_and_matching_request_returns_304(self) -> None:
        with self._client() as client:
            first = client.get("/json", headers={"X-Request-ID": "request-1"})
            self.assertEqual(200, first.status_code, first.text)
            self.assertEqual({"value": "alpha"}, first.json())
            etag = first.headers.get("etag")
            self.assertIsNotNone(etag)
            self.assertTrue(etag.startswith('W/"sha256-'), etag)
            self.assertIn("private", first.headers.get("cache-control", ""))
            self.assertIn("no-cache", first.headers.get("cache-control", ""))
            self.assertIn("authorization", first.headers.get("vary", "").lower())
            self.assertEqual("request-1", first.headers["X-Correlation-ID"])

            second = client.get("/json", headers={"If-None-Match": etag or "", "X-Request-ID": "request-2"})
            self.assertEqual(304, second.status_code, second.text)
            self.assertEqual(b"", second.content)
            self.assertEqual(etag, second.headers.get("etag"))
            self.assertEqual("request-2", second.headers["X-Correlation-ID"])

    def test_changed_json_body_does_not_match_previous_etag(self) -> None:
        with self._client() as client:
            first = client.get("/json?value=alpha")
            etag = first.headers.get("etag")
            self.assertIsNotNone(etag)

            changed = client.get("/json?value=beta", headers={"If-None-Match": etag or ""})
            self.assertEqual(200, changed.status_code, changed.text)
            self.assertEqual({"value": "beta"}, changed.json())
            self.assertNotEqual(etag, changed.headers.get("etag"))

    def test_conditional_middleware_skips_cookie_and_non_json_responses(self) -> None:
        with self._client() as client:
            cookie = client.get("/cookie")
            self.assertEqual(200, cookie.status_code, cookie.text)
            self.assertNotIn("etag", cookie.headers)

            text = client.get("/text")
            self.assertEqual(200, text.status_code, text.text)
            self.assertNotIn("etag", text.headers)

    def test_platform_endpoints_return_304_for_matching_if_none_match(self) -> None:
        api_router = APIRouter(prefix="/api/v1")
        api_router.include_router(create_platform_router())
        app = create_govoplan_app(
            title="conditional platform endpoint test",
            version="test",
            registry=PlatformRegistry(),
            api_router=api_router,
        )
        app.dependency_overrides[get_api_principal] = lambda: object()

        with TestClient(app) as client:
            for path in (
                "/api/v1/platform/status",
                "/api/v1/platform/modules",
                "/api/v1/platform/public-modules",
            ):
                first = client.get(path)
                self.assertEqual(200, first.status_code, first.text)
                etag = first.headers.get("etag")
                self.assertIsNotNone(etag, path)

                second = client.get(path, headers={"If-None-Match": etag or ""})
                self.assertEqual(304, second.status_code, second.text)
                self.assertEqual(b"", second.content)
                self.assertEqual(etag, second.headers.get("etag"))


if __name__ == "__main__":
    unittest.main()
