from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, timezone

from cryptography import x509
from cryptography.hazmat.primitives.serialization import pkcs12


@dataclass(frozen=True)
class CertificateInfo:
    subject_name: str
    issuer_name: str
    documento_federal: str | None
    valido_de: str
    valido_ate: str
    not_valid_before: datetime
    not_valid_after: datetime


def format_datetime_pt(value: datetime) -> str:
    return value.strftime("%d/%m/%Y %H:%M")


def assert_certificate_usable(cert_info: CertificateInfo) -> None:
    now = datetime.now(timezone.utc).replace(tzinfo=None)
    if cert_info.not_valid_before > now:
        raise ValueError(
            f"Certificado ainda não é válido. Início da validade: {format_datetime_pt(cert_info.not_valid_before)}."
        )
    if cert_info.not_valid_after < now:
        raise ValueError(f"Certificado expirado em {format_datetime_pt(cert_info.not_valid_after)}.")


def _name_to_text(name: x509.Name) -> str:
    return ", ".join(f"{attr.oid._name}={attr.value}" for attr in name)


def _extract_documento(subject: str) -> str | None:
    digits = "".join(ch if ch.isdigit() else " " for ch in subject).split()
    candidates = [item for item in digits if len(item) in (11, 14)]
    return candidates[-1] if candidates else None


def _cert_datetime(cert: x509.Certificate, attr_utc: str, attr_legacy: str) -> datetime:
    value = getattr(cert, attr_utc, None)
    if value is None:
        value = getattr(cert, attr_legacy)
    return value.replace(tzinfo=None)


def validate_pfx(pfx_data: bytes, password: str) -> CertificateInfo:
    try:
        _key, cert, _extra = pkcs12.load_key_and_certificates(
            pfx_data,
            password.encode("utf-8"),
        )
    except Exception as exc:
        raise ValueError("Não foi possível abrir o PFX com a senha informada.") from exc

    if cert is None:
        raise ValueError("PFX não contém certificado principal.")

    subject = _name_to_text(cert.subject)
    not_valid_before = _cert_datetime(cert, "not_valid_before_utc", "not_valid_before")
    not_valid_after = _cert_datetime(cert, "not_valid_after_utc", "not_valid_after")
    cert_info = CertificateInfo(
        subject_name=subject,
        issuer_name=_name_to_text(cert.issuer),
        documento_federal=_extract_documento(subject),
        valido_de=not_valid_before.strftime("%Y-%m-%d %H:%M:%S"),
        valido_ate=not_valid_after.strftime("%Y-%m-%d %H:%M:%S"),
        not_valid_before=not_valid_before,
        not_valid_after=not_valid_after,
    )
    assert_certificate_usable(cert_info)
    return cert_info
