"""Tests for TLS admin API endpoints and CLI commands."""

from __future__ import annotations

import pytest

from turnstone.core.storage import get_storage, init_storage, reset_storage

lacme = pytest.importorskip("lacme")


@pytest.fixture(autouse=True)
def _storage(tmp_path):
    """Initialize ephemeral SQLite storage for each test."""
    reset_storage()
    db = str(tmp_path / "test.db")
    init_storage("sqlite", path=db)
    yield
    reset_storage()


@pytest.fixture
def tls_manager():
    """Create an initialized TLSManager."""
    import asyncio

    from turnstone.console.tls import TLSManager

    mgr = TLSManager(get_storage())
    asyncio.run(mgr.init_ca())
    # Issue a test cert
    asyncio.run(mgr.issue_console_certs(["test.internal", "localhost"]))
    return mgr


# ── Admin API endpoints ───────────────────────────────────────────────────────


def _make_app(tls_manager):
    """Create a minimal Starlette app with TLS endpoints."""
    from starlette.applications import Starlette
    from starlette.middleware import Middleware
    from starlette.middleware.base import BaseHTTPMiddleware
    from starlette.routing import Route

    from turnstone.console.server import (
        tls_ca_cert,
        tls_ca_status,
        tls_delete_cert,
        tls_list_certs,
        tls_renew_cert,
    )
    from turnstone.core.auth import AuthResult

    async def _grant_access(request, call_next):  # type: ignore[no-untyped-def]
        request.state.auth_result = AuthResult(
            user_id="",
            scopes=frozenset({"approve", "service"}),
            token_source="test",
        )
        return await call_next(request)

    app = Starlette(
        routes=[
            Route("/ca", tls_ca_status),
            Route("/ca.pem", tls_ca_cert),
            Route("/certs", tls_list_certs),
            Route("/certs/{domain}/renew", tls_renew_cert, methods=["POST"]),
            Route("/certs/{domain}", tls_delete_cert, methods=["DELETE"]),
        ],
        middleware=[Middleware(BaseHTTPMiddleware, dispatch=_grant_access)],
    )
    app.state.tls_manager = tls_manager
    return app


def test_list_certs(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app(tls_manager))
    resp = client.get("/certs")
    assert resp.status_code == 200
    data = resp.json()
    assert len(data["certs"]) >= 1
    assert data["certs"][0]["domain"] == "test.internal"


def test_renew_cert(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app(tls_manager))
    resp = client.post("/certs/test.internal/renew")
    assert resp.status_code == 200
    data = resp.json()
    assert data["domain"] == "test.internal"


def test_renew_cert_not_found(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app(tls_manager))
    resp = client.post("/certs/nonexistent.internal/renew")
    assert resp.status_code == 404


def test_delete_cert(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app(tls_manager))
    resp = client.delete("/certs/test.internal")
    assert resp.status_code == 200
    assert resp.json()["deleted"] == "test.internal"
    # Verify it's gone
    resp = client.get("/certs")
    domains = [c["domain"] for c in resp.json()["certs"]]
    assert "test.internal" not in domains


def test_delete_cert_not_found(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app(tls_manager))
    resp = client.delete("/certs/nonexistent.internal")
    assert resp.status_code == 404


# ── Auth enforcement ──────────────────────────────────────────────────────────


def _make_app_no_auth(tls_manager):
    """Create app without auth middleware — simulates unauthenticated requests."""
    from starlette.applications import Starlette
    from starlette.routing import Route

    from turnstone.console.server import (
        tls_ca_cert,
        tls_ca_status,
        tls_delete_cert,
        tls_list_certs,
        tls_renew_cert,
    )

    app = Starlette(
        routes=[
            Route("/ca", tls_ca_status),
            Route("/ca.pem", tls_ca_cert),
            Route("/certs", tls_list_certs),
            Route("/certs/{domain}/renew", tls_renew_cert, methods=["POST"]),
            Route("/certs/{domain}", tls_delete_cert, methods=["DELETE"]),
        ],
    )
    app.state.tls_manager = tls_manager
    return app


def _make_app_read_only(tls_manager):
    """Create app with read-only auth — should be rejected by admin endpoints."""
    from starlette.applications import Starlette
    from starlette.middleware import Middleware
    from starlette.middleware.base import BaseHTTPMiddleware
    from starlette.routing import Route

    from turnstone.console.server import (
        tls_ca_cert,
        tls_ca_status,
        tls_delete_cert,
        tls_list_certs,
        tls_renew_cert,
    )
    from turnstone.core.auth import AuthResult

    async def _grant_read(request, call_next):  # type: ignore[no-untyped-def]
        request.state.auth_result = AuthResult(
            user_id="viewer",
            scopes=frozenset({"read"}),
            token_source="jwt",
        )
        return await call_next(request)

    app = Starlette(
        routes=[
            Route("/ca", tls_ca_status),
            Route("/ca.pem", tls_ca_cert),
            Route("/certs", tls_list_certs),
            Route("/certs/{domain}/renew", tls_renew_cert, methods=["POST"]),
            Route("/certs/{domain}", tls_delete_cert, methods=["DELETE"]),
        ],
        middleware=[Middleware(BaseHTTPMiddleware, dispatch=_grant_read)],
    )
    app.state.tls_manager = tls_manager
    return app


def test_unauthenticated_list_certs_401(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app_no_auth(tls_manager))
    resp = client.get("/certs")
    assert resp.status_code == 401


def test_unauthenticated_renew_401(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app_no_auth(tls_manager))
    resp = client.post("/certs/test.internal/renew")
    assert resp.status_code == 401


def test_unauthenticated_delete_401(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app_no_auth(tls_manager))
    resp = client.delete("/certs/test.internal")
    assert resp.status_code == 401


def test_read_only_renew_403(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app_read_only(tls_manager))
    resp = client.post("/certs/test.internal/renew")
    assert resp.status_code == 403


def test_read_only_delete_403(tls_manager):
    from starlette.testclient import TestClient

    client = TestClient(_make_app_read_only(tls_manager))
    resp = client.delete("/certs/test.internal")
    assert resp.status_code == 403


# ── CLI bootstrap ─────────────────────────────────────────────────────────────


def test_cli_bootstrap(tmp_path):
    """Test offline CA bootstrap."""
    import argparse

    from turnstone.admin import _cmd_tls_bootstrap

    out = tmp_path / "certs"
    args = argparse.Namespace(out=str(out), issue=["app.internal", "pg.internal"])
    _cmd_tls_bootstrap(args)

    assert (out / "ca.pem").exists()
    assert b"BEGIN CERTIFICATE" in (out / "ca.pem").read_bytes()
    # Check certs were issued
    assert (out / "certs" / "app.internal").exists()
    assert (out / "certs" / "pg.internal").exists()


def test_cli_bootstrap_no_issue(tmp_path):
    """Bootstrap with no --issue creates CA only."""
    import argparse

    from turnstone.admin import _cmd_tls_bootstrap

    out = tmp_path / "certs"
    args = argparse.Namespace(out=str(out), issue=[])
    _cmd_tls_bootstrap(args)

    assert (out / "ca.pem").exists()
    # No certs dir
    certs_dir = out / "certs"
    if certs_dir.exists():
        assert len(list(certs_dir.iterdir())) == 0


# ── Config parsing ────────────────────────────────────────────────────────────


def test_database_ssl_config_map():
    """Database SSL keys are in the config map."""
    from turnstone.core.config import _CONFIG_MAP

    db_map = _CONFIG_MAP["database"]
    assert "sslmode" in db_map
    assert "sslrootcert" in db_map
    assert "sslcert" in db_map
    assert "sslkey" in db_map


# ── Auth enforcement ──────────────────────────────────────────────────────────


def test_tls_endpoints_require_auth(tls_manager):
    """TLS admin endpoints return 401 without auth."""
    from starlette.applications import Starlette
    from starlette.routing import Route
    from starlette.testclient import TestClient

    from turnstone.console.server import tls_ca_status, tls_list_certs

    # No auth middleware — request.state.auth_result will be missing
    app = Starlette(
        routes=[
            Route("/ca", tls_ca_status),
            Route("/certs", tls_list_certs),
        ]
    )
    app.state.tls_manager = tls_manager

    client = TestClient(app)
    resp = client.get("/ca")
    assert resp.status_code == 401

    resp = client.get("/certs")
    assert resp.status_code == 401


# ── SDK TLS params ────────────────────────────────────────────────────────────


def test_sdk_client_cert_requires_both():
    """SDK raises ValueError if only one of client_cert/client_key provided."""
    from turnstone.sdk._base import _BaseClient

    with pytest.raises(ValueError, match="Both client_cert and client_key"):
        _BaseClient(
            base_url="http://localhost:8080",
            client_cert="/path/to/cert.pem",
        )

    with pytest.raises(ValueError, match="Both client_cert and client_key"):
        _BaseClient(
            base_url="http://localhost:8080",
            client_key="/path/to/key.pem",
        )
