"""
src/core/pdf_signature_verifier.py
----------------------------------
Đọc và xác minh chữ ký số trong file PDF sử dụng thư viện pyhanko.
Lấy ra MST từ Subject hoặc Extensions của chứng chỉ.
"""
import os
import re
import logging


_NON_FATAL_VALIDATE_ERRORS = (
    "hybrid-reference files",
    "signature object type must be /sig",
    "unable to build a validation path",
    "no issuer matching",
)

def extract_mst_from_dn(dn_str: str) -> str:
    """
    Trích xuất mã số thuế từ chuỗi Distinguished Name (X.509 Subject).
    Ở VN, MST thường được lưu dưới dạng:
    - MST:010...
    - OID.0.9.2342.19200300.100.1.1=010...
    - SERIALNUMBER=010... (Tuỳ theo chuẩn CA)
    - CÔNG TY ABC MST:010...
    """
    # Pattern tìm các con số độ dài 10 hoặc 13-14 ký tự (có thể chứa dấu gạch ngang)
    # Tìm trực tiếp MST:...
    m = re.search(r'MST[\:\s]*([0-9\-]{10,14})', dn_str, re.IGNORECASE)
    if m:
        return m.group(1).replace('-', '')
    
    # Tìm các trường OID phổ biến chứa MST hoặc SerialNumber
    m = re.search(r'(?:OID\.0\.9\.2342\.19200300\.100\.1\.1|UID|SERIALNUMBER)\s*=\s*(?:MST:)?([0-9\-]{10,14})', dn_str, re.IGNORECASE)
    if m:
        return m.group(1).replace('-', '')
        
    return ""

def verify_pdf_signature(pdf_path: str) -> dict:
    """
    Kiểm tra file PDF xem có chứa chữ ký số hay không.
    Trả về thông tin kết quả:
    {
        "is_signed": bool,
        "is_valid": bool,
        "signatures": [
            {
                "subject": str,
                "mst": str,
                "intact": bool,
                "signing_time": str
            }
        ]
    }
    """
    result = {
        "is_signed": False,
        "is_valid": False,
        "signatures": []
    }
    
    if not os.path.exists(pdf_path):
        return result
        
    try:
        from pyhanko.pdf_utils.reader import PdfFileReader
        from pyhanko.sign.validation import validate_pdf_signature

        # pyHanko logs cert-path building failures to stderr even when handled.
        logging.getLogger("pyhanko.sign.validation.generic_cms").setLevel(logging.CRITICAL)
        logging.getLogger("pyhanko_certvalidator").setLevel(logging.CRITICAL)
    except ImportError:
        # Nếu chưa cài pyhanko, bỏ qua (Graceful degradation)
        return result
        
    try:
        with open(pdf_path, 'rb') as f:
            # strict=False: tolerate hybrid-reference PDFs commonly seen in practice.
            reader = PdfFileReader(f, strict=False)
            sig_fields = reader.embedded_regular_signatures

            # Fallback for older/newer pyhanko variants if the property is unavailable.
            if sig_fields is None:
                sig_fields = [
                    s for s in getattr(reader, "embedded_signatures", [])
                    if getattr(s, "sig_object_type", "") == "/Sig"
                ]
            
            if not sig_fields:
                return result
                
            result["is_signed"] = True
            for sig in sig_fields:
                cert = None
                intact = False
                signing_time_str = ""
                try:
                    # Validate basic PKCS7/CMS integrity
                    val_status = validate_pdf_signature(sig, skip_diff=True)

                    # pyHanko returns PdfSignatureStatus (modern API): use signing_cert/signer_reported_dt.
                    cert = getattr(val_status, "signing_cert", None)
                    if cert is None:
                        cert = getattr(sig, "signer_cert", None)

                    # Don't require trust-chain validity here; we only need integrity + signer identity.
                    intact = bool(val_status.intact)
                    signer_dt = getattr(val_status, "signer_reported_dt", None)
                    signing_time_str = str(signer_dt) if signer_dt else ""
                except Exception as e:
                    # Lỗi validate cho 1 chữ ký cụ thể (e.g. hybrid-reference files)
                    err_text = str(e).lower()
                    # Compatibility errors are treated as non-fatal if cert can still be extracted.
                    intact = any(token in err_text for token in _NON_FATAL_VALIDATE_ERRORS)
                    # Vẫn cố ráng lấy chứng thư số để lấy MST
                    try:
                        cert = sig.signer_cert
                        from asn1crypto import cms
                        if hasattr(sig, 'signed_data') and isinstance(sig.signed_data, cms.SignedData):
                            # Try to get basic sign time later if needed
                            pass
                    except Exception as e2:
                        # print(f"Fallback cert error: {e2}")
                        pass
                        
                if cert:
                    try:
                        subject_str = cert.subject.human_friendly
                        mst = extract_mst_from_dn(subject_str)
                        result["signatures"].append({
                            "subject": subject_str,
                            "mst": mst,
                            "intact": bool(intact),
                            "signing_time": signing_time_str
                        })
                    except Exception as e_cert:
                        # print(f"Error parsing cert: {e_cert}")
                        pass
            
            result["is_valid"] = all(s["intact"] for s in result["signatures"]) and len(result["signatures"]) > 0
                    
    except Exception as e:
        # print(f"Error reading PDF signature: {e}")
        pass
        
    return result
