import httpx
import pytest

from src import main


REQUIRED_HEADERS = {
    "x-content-type-options": "nosniff",
    "referrer-policy": "no-referrer",
    "x-frame-options": "DENY",
}


def assert_browser_security_boundary(response: httpx.Response) -> None:
    for name, value in REQUIRED_HEADERS.items():
        assert response.headers[name] == value
    permissions_policy = response.headers["permissions-policy"]
    assert "camera=()" in permissions_policy
    assert "microphone=(self)" in permissions_policy
    assert "microphone=()" not in permissions_policy
    policy = response.headers["content-security-policy"]
    assert "default-src 'self'" in policy
    assert "script-src 'self'" in policy
    assert "object-src 'none'" in policy
    assert "frame-ancestors 'none'" in policy
    script_policy = next(part for part in policy.split(";") if "script-src" in part)
    assert "'unsafe-inline'" not in script_policy
    assert "'unsafe-eval'" not in script_policy


@pytest.mark.anyio
async def test_security_boundary_covers_pages_health_static_and_api_errors(monkeypatch):
    monkeypatch.setenv("STACKCHAIN_DASHBOARD_ACCESS_TOKEN", "access-token-with-at-least-thirty-two-characters")
    monkeypatch.setenv("STACKCHAIN_DASHBOARD_SESSION_SECRET", "session-secret-with-at-least-thirty-two-characters")
    monkeypatch.setenv("STACKCHAIN_DASHBOARD_AUTH_MODE", "operator")
    monkeypatch.setenv("STACKCHAIN_DASHBOARD_PUBLIC_ORIGIN", "https://test")
    transport = httpx.ASGITransport(app=main.app)

    async with httpx.AsyncClient(transport=transport, base_url="https://test") as client:
        responses = [
            await client.get("/healthz"),
            await client.get("/login"),
            await client.get("/static/dashboard.js"),
            await client.get("/api/v1/context"),
        ]

    assert [response.status_code for response in responses] == [200, 200, 200, 401]
    for response in responses:
        assert_browser_security_boundary(response)
        assert response.headers["strict-transport-security"] == "max-age=31536000"


@pytest.mark.anyio
async def test_security_boundary_covers_fail_closed_authentication_response(monkeypatch):
    monkeypatch.delenv("STACKCHAIN_DASHBOARD_ACCESS_TOKEN", raising=False)
    monkeypatch.delenv("STACKCHAIN_DASHBOARD_SESSION_SECRET", raising=False)
    monkeypatch.delenv("STACKCHAIN_DASHBOARD_AUTH_MODE", raising=False)
    transport = httpx.ASGITransport(app=main.app)

    async with httpx.AsyncClient(transport=transport, base_url="https://test") as client:
        response = await client.get("/login")

    assert response.status_code == 503
    assert_browser_security_boundary(response)


@pytest.mark.anyio
async def test_insecure_local_mode_does_not_emit_hsts():
    transport = httpx.ASGITransport(app=main.app, client=("127.0.0.1", 123))
    async with httpx.AsyncClient(transport=transport, base_url="http://localhost") as client:
        response = await client.get("/healthz")

    assert response.status_code == 200
    assert "strict-transport-security" not in response.headers
