"""Tests for the certificate parsing module."""

from __future__ import annotations

import pytest
from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import rsa

from ecp_auth.exceptions import CertificateParsingError
from ecp_auth.parsers import (
    CertificateInfo,
    extract_certificate_info,
    get_asn1_certificate_info,
    parse_der_certificate,
    parse_pem_certificate,
    parse_pkcs12,
)


class TestParsePKCS12:
    """Tests for PKCS#12 parsing."""

    def test_parse_pkcs12_with_password(
        self, pkcs12_data: bytes, rsa_private_key: rsa.RSAPrivateKey
    ) -> None:
        """Test parsing a PKCS#12 file with a password."""
        key, cert, cas = parse_pkcs12(pkcs12_data, "testpassword")

        assert key is not None
        assert cert is not None
        assert isinstance(cert, x509.Certificate)
        assert isinstance(cas, list)

    def test_parse_pkcs12_without_password(
        self, pkcs12_data_no_password: bytes
    ) -> None:
        """Test parsing a PKCS#12 file without password."""
        key, cert, cas = parse_pkcs12(pkcs12_data_no_password, None)

        assert key is not None
        assert cert is not None

    def test_parse_pkcs12_wrong_password(self, pkcs12_data: bytes) -> None:
        """Test that wrong password raises CertificateParsingError."""
        with pytest.raises(CertificateParsingError):
            parse_pkcs12(pkcs12_data, "wrongpassword")

    def test_parse_pkcs12_invalid_data(self) -> None:
        """Test that invalid data raises CertificateParsingError."""
        with pytest.raises(CertificateParsingError):
            parse_pkcs12(b"not-a-pkcs12-file", "password")

    def test_parse_pkcs12_empty_data(self) -> None:
        """Test that empty data raises CertificateParsingError."""
        with pytest.raises(CertificateParsingError):
            parse_pkcs12(b"", None)


class TestParsePEMCertificate:
    """Tests for PEM certificate parsing."""

    def test_parse_pem_certificate(
        self, self_signed_certificate: x509.Certificate
    ) -> None:
        """Test parsing a PEM-encoded certificate."""
        from cryptography.hazmat.primitives.serialization import Encoding

        pem_data = self_signed_certificate.public_bytes(Encoding.PEM)
        cert = parse_pem_certificate(pem_data)

        assert isinstance(cert, x509.Certificate)
        assert cert.serial_number == self_signed_certificate.serial_number

    def test_parse_pem_string(
        self, self_signed_certificate: x509.Certificate
    ) -> None:
        """Test parsing PEM data provided as a string."""
        from cryptography.hazmat.primitives.serialization import Encoding

        pem_str = self_signed_certificate.public_bytes(Encoding.PEM).decode()
        cert = parse_pem_certificate(pem_str)

        assert isinstance(cert, x509.Certificate)

    def test_parse_invalid_pem(self) -> None:
        """Test that invalid PEM data raises CertificateParsingError."""
        with pytest.raises(CertificateParsingError):
            parse_pem_certificate(b"not-a-pem-certificate")


class TestParseDERCertificate:
    """Tests for DER certificate parsing."""

    def test_parse_der_certificate(
        self, self_signed_certificate: x509.Certificate
    ) -> None:
        """Test parsing a DER-encoded certificate."""
        from cryptography.hazmat.primitives.serialization import Encoding

        der_data = self_signed_certificate.public_bytes(Encoding.DER)
        cert = parse_der_certificate(der_data)

        assert isinstance(cert, x509.Certificate)

    def test_parse_invalid_der(self) -> None:
        """Test that invalid DER data raises CertificateParsingError."""
        with pytest.raises(CertificateParsingError):
            parse_der_certificate(b"not-a-der-certificate")


class TestExtractCertificateInfo:
    """Tests for certificate info extraction."""

    def test_extract_basic_info(
        self, self_signed_certificate: x509.Certificate
    ) -> None:
        """Test extracting basic certificate information."""
        info = extract_certificate_info(self_signed_certificate)

        assert isinstance(info, CertificateInfo)
        assert info.common_name == "Test User"
        assert info.organization == "Test Organization"
        assert info.organizational_unit == "IT"
        assert info.email == "test@example.com"
        assert info.is_self_signed is True
        assert len(info.fingerprint_sha256) == 64
        assert len(info.serial_number) > 0

    def test_extract_key_usage(
        self, self_signed_certificate: x509.Certificate
    ) -> None:
        """Test extracting key usage flags."""
        info = extract_certificate_info(self_signed_certificate)

        assert "digital_signature" in info.key_usage
        assert "key_encipherment" in info.key_usage

    def test_extract_pem_and_der(
        self, self_signed_certificate: x509.Certificate
    ) -> None:
        """Test that PEM and DER data are included."""
        info = extract_certificate_info(self_signed_certificate)

        assert info.pem_data.startswith("-----BEGIN CERTIFICATE-----")
        assert len(info.der_data) > 0

    def test_extract_validity_dates(
        self, self_signed_certificate: x509.Certificate
    ) -> None:
        """Test that validity dates are timezone-aware."""
        info = extract_certificate_info(self_signed_certificate)

        assert info.not_valid_before.tzinfo is not None
        assert info.not_valid_after.tzinfo is not None
        assert info.not_valid_before < info.not_valid_after


class TestASN1CertificateInfo:
    """Tests for ASN.1 certificate parsing."""

    def test_get_asn1_info(
        self, self_signed_certificate: x509.Certificate
    ) -> None:
        """Test ASN.1 certificate field extraction."""
        from cryptography.hazmat.primitives.serialization import Encoding

        der_data = self_signed_certificate.public_bytes(Encoding.DER)
        info = get_asn1_certificate_info(der_data)

        assert "version" in info
        assert "serial_number" in info
        assert "signature_algorithm" in info
        assert "subject" in info
        assert "issuer" in info

    def test_asn1_invalid_data(self) -> None:
        """Test ASN.1 parsing with invalid data."""
        with pytest.raises(CertificateParsingError):
            get_asn1_certificate_info(b"invalid")
