"""Tests for certificate generation (bvd/core/certs.py)."""
from __future__ import annotations

import os
import ssl
import tempfile
from pathlib import Path
from unittest.mock import patch

from bvd.core.certs import (
    _generate_domain_cert,
    generate_ca,
    generate_server_cert,
)

# ── Helpers ───────────────────────────────────────────────────────────────────

def _isolated(tmp_path: Path):
    """Patch settings.data_dir to a temp directory for the duration of the block."""
    return patch("bvd.core.certs.settings.data_dir", tmp_path)


# ── CA generation ─────────────────────────────────────────────────────────────

def test_generate_ca_creates_files(tmp_path):
    with _isolated(tmp_path):
        from bvd.core.certs import ca_cert_path, ca_key_path
        generate_ca()
        assert ca_cert_path().exists()
        assert ca_key_path().exists()
        assert oct(ca_key_path().stat().st_mode)[-3:] == "600"


def test_generate_ca_is_self_signed(tmp_path):
    with _isolated(tmp_path):
        ca_cert, _ = generate_ca()
    assert ca_cert.issuer == ca_cert.subject


def test_generate_ca_is_ca(tmp_path):
    from cryptography.x509 import BasicConstraints
    with _isolated(tmp_path):
        ca_cert, _ = generate_ca()
    bc = ca_cert.extensions.get_extension_for_class(BasicConstraints).value
    assert bc.ca is True


# ── Server cert generation ────────────────────────────────────────────────────

def test_generate_server_cert_creates_files(tmp_path):
    with _isolated(tmp_path):
        from bvd.core.certs import server_cert_path, server_key_path
        ca_cert, ca_key = generate_ca()
        generate_server_cert(ca_cert, ca_key)
        assert server_cert_path().exists()
        assert server_key_path().exists()


def test_server_cert_signed_by_ca(tmp_path):
    from cryptography.x509 import load_pem_x509_certificate
    with _isolated(tmp_path):
        from bvd.core.certs import server_cert_path
        ca_cert, ca_key = generate_ca()
        generate_server_cert(ca_cert, ca_key)
        srv = load_pem_x509_certificate(server_cert_path().read_bytes())
    assert srv.issuer == ca_cert.subject


def test_server_cert_has_localhost_san(tmp_path):
    from cryptography.x509 import SubjectAlternativeName, load_pem_x509_certificate
    with _isolated(tmp_path):
        from bvd.core.certs import server_cert_path
        ca_cert, ca_key = generate_ca()
        generate_server_cert(ca_cert, ca_key)
        srv = load_pem_x509_certificate(server_cert_path().read_bytes())

    san = srv.extensions.get_extension_for_class(SubjectAlternativeName).value
    names = [str(n.value) for n in san]
    assert any("localhost" in n or "127.0.0.1" in n for n in names)


# ── Per-domain cert ───────────────────────────────────────────────────────────

def test_domain_cert_for_hostname(tmp_path):
    with _isolated(tmp_path):
        generate_ca()
        cert_pem, key_pem = _generate_domain_cert("api.anthropic.com")

    assert b"BEGIN CERTIFICATE" in cert_pem
    assert b"BEGIN" in key_pem and b"PRIVATE KEY" in key_pem


def test_domain_cert_loadable_as_ssl_context(tmp_path):
    """The generated cert+key must be accepted by ssl.SSLContext."""
    with _isolated(tmp_path):
        generate_ca()
        cert_pem, key_pem = _generate_domain_cert("api.openai.com")

    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    fd, path = tempfile.mkstemp(suffix=".pem")
    try:
        os.write(fd, cert_pem + key_pem)
        os.close(fd)
        ctx.load_cert_chain(path)  # raises ssl.SSLError if invalid
    finally:
        os.unlink(path)


def test_domain_cert_cn_matches_hostname(tmp_path):
    from cryptography.x509 import load_pem_x509_certificate
    from cryptography.x509.oid import NameOID
    with _isolated(tmp_path):
        generate_ca()
        cert_pem, _ = _generate_domain_cert("api.anthropic.com")

    cert = load_pem_x509_certificate(cert_pem)
    cn = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
    assert cn == "api.anthropic.com"
