"""Proof certificates for validated fixes.

This module generates cryptographic proof certificates that
a fix has been validated against deterministic replay.
"""

from __future__ import annotations

import hashlib
import hmac
import json
import os
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional


@dataclass
class ValidationRun:
    """Record of a single validation run."""

    run_number: int
    exit_code: int
    passed: bool
    output_hash: str
    timestamp: str


@dataclass
class ProofCertificate:
    """Cryptographic proof that a fix has been validated."""

    # Identifiers
    certificate_id: str
    original_run_id: str
    hypothesis_id: str

    # Hashes
    original_failure_hash: str
    patch_hash: str

    # Validation details
    validation_runs: List[ValidationRun]
    verdict: str  # PROVEN_FIX, NO_EFFECT, NEW_FAILURE, INCONCLUSIVE

    # Metadata
    created_at: str
    validator_version: str

    # Signature
    signature: Optional[str] = None

    @property
    def is_proven(self) -> bool:
        """Check if this certificate proves the fix works."""
        return self.verdict == "PROVEN_FIX"

    @property
    def pass_count(self) -> int:
        """Count of passing validation runs."""
        return sum(1 for r in self.validation_runs if r.passed)

    @property
    def total_runs(self) -> int:
        """Total validation runs."""
        return len(self.validation_runs)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "certificate_id": self.certificate_id,
            "original_run_id": self.original_run_id,
            "hypothesis_id": self.hypothesis_id,
            "original_failure_hash": self.original_failure_hash,
            "patch_hash": self.patch_hash,
            "validation_runs": [
                {
                    "run_number": r.run_number,
                    "exit_code": r.exit_code,
                    "passed": r.passed,
                    "output_hash": r.output_hash,
                    "timestamp": r.timestamp,
                }
                for r in self.validation_runs
            ],
            "verdict": self.verdict,
            "created_at": self.created_at,
            "validator_version": self.validator_version,
            "signature": self.signature,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> ProofCertificate:
        """Create from dictionary."""
        return cls(
            certificate_id=data["certificate_id"],
            original_run_id=data["original_run_id"],
            hypothesis_id=data["hypothesis_id"],
            original_failure_hash=data["original_failure_hash"],
            patch_hash=data["patch_hash"],
            validation_runs=[
                ValidationRun(
                    run_number=r["run_number"],
                    exit_code=r["exit_code"],
                    passed=r["passed"],
                    output_hash=r["output_hash"],
                    timestamp=r["timestamp"],
                )
                for r in data["validation_runs"]
            ],
            verdict=data["verdict"],
            created_at=data["created_at"],
            validator_version=data["validator_version"],
            signature=data.get("signature"),
        )


def _compute_hash(data: Any) -> str:
    """Compute SHA-256 hash of data."""
    if isinstance(data, dict):
        content = json.dumps(data, sort_keys=True, separators=(",", ":"))
    elif isinstance(data, str):
        content = data
    else:
        content = str(data)
    return hashlib.sha256(content.encode("utf-8")).hexdigest()


def _compute_signature(certificate: ProofCertificate, secret_key: str) -> str:
    """Compute HMAC signature for certificate.

    Args:
        certificate: Certificate to sign
        secret_key: Secret key for HMAC

    Returns:
        Hex-encoded signature
    """
    # Create canonical content to sign
    content = {
        "certificate_id": certificate.certificate_id,
        "original_run_id": certificate.original_run_id,
        "hypothesis_id": certificate.hypothesis_id,
        "original_failure_hash": certificate.original_failure_hash,
        "patch_hash": certificate.patch_hash,
        "verdict": certificate.verdict,
        "created_at": certificate.created_at,
        "validation_runs": [
            {"run_number": r.run_number, "passed": r.passed, "output_hash": r.output_hash}
            for r in certificate.validation_runs
        ],
    }

    canonical = json.dumps(content, sort_keys=True, separators=(",", ":"))
    signature = hmac.new(
        secret_key.encode("utf-8"),
        canonical.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()

    return signature


def generate_proof_certificate(
    original_run_id: str,
    original_exception: Dict[str, Any],
    hypothesis_id: str,
    patch_hash: str,
    validation_results: List[Dict[str, Any]],
    verdict: str,
    secret_key: Optional[str] = None,
) -> ProofCertificate:
    """Generate a proof certificate for a validated fix.

    Args:
        original_run_id: ID of the original failing run
        original_exception: Original exception details
        hypothesis_id: ID of the hypothesis
        patch_hash: Hash of the applied patch
        validation_results: List of validation run results
        verdict: Validation verdict
        secret_key: Optional secret key for signing

    Returns:
        ProofCertificate
    """
    # Generate certificate ID
    now = datetime.now(timezone.utc)
    cert_content = f"{original_run_id}:{hypothesis_id}:{now.isoformat()}"
    certificate_id = hashlib.sha256(cert_content.encode()).hexdigest()[:16]

    # Hash original failure
    failure_hash = _compute_hash(original_exception)

    # Create validation run records
    runs = []
    for i, result in enumerate(validation_results):
        output = result.get("output", "") or ""
        runs.append(ValidationRun(
            run_number=i + 1,
            exit_code=result.get("exit_code", -1),
            passed=result.get("passed", False),
            output_hash=_compute_hash(output),
            timestamp=now.isoformat(),
        ))

    certificate = ProofCertificate(
        certificate_id=certificate_id,
        original_run_id=original_run_id,
        hypothesis_id=hypothesis_id,
        original_failure_hash=failure_hash,
        patch_hash=patch_hash,
        validation_runs=runs,
        verdict=verdict,
        created_at=now.isoformat(),
        validator_version="1.0.0",
    )

    # Sign if secret key provided
    if secret_key:
        certificate.signature = _compute_signature(certificate, secret_key)
    else:
        # Use environment variable if available
        env_key = os.environ.get("AUTODEBUG_SIGNING_KEY")
        if env_key:
            certificate.signature = _compute_signature(certificate, env_key)

    return certificate


def verify_proof_certificate(
    certificate: ProofCertificate,
    secret_key: Optional[str] = None,
) -> Dict[str, Any]:
    """Verify a proof certificate.

    Args:
        certificate: Certificate to verify
        secret_key: Optional secret key for signature verification

    Returns:
        Dict with is_valid, checks, and errors
    """
    checks = []
    errors = []

    # Check certificate has required fields
    if not certificate.certificate_id:
        errors.append("Missing certificate_id")
    else:
        checks.append("certificate_id present")

    if not certificate.validation_runs:
        errors.append("No validation runs")
    else:
        checks.append(f"{len(certificate.validation_runs)} validation runs present")

    # Check verdict consistency
    all_pass = all(r.passed for r in certificate.validation_runs)
    if certificate.verdict == "PROVEN_FIX" and not all_pass:
        errors.append("Verdict is PROVEN_FIX but not all runs passed")
    else:
        checks.append("Verdict consistent with runs")

    # Verify signature if possible
    if certificate.signature:
        key = secret_key or os.environ.get("AUTODEBUG_SIGNING_KEY")
        if key:
            expected_sig = _compute_signature(certificate, key)
            if certificate.signature == expected_sig:
                checks.append("Signature valid")
            else:
                errors.append("Signature invalid")
        else:
            checks.append("Signature present (not verified - no key)")
    else:
        checks.append("No signature (unsigned certificate)")

    return {
        "is_valid": len(errors) == 0,
        "checks": checks,
        "errors": errors,
        "certificate_id": certificate.certificate_id,
        "verdict": certificate.verdict,
    }


def format_proof_certificate(certificate: ProofCertificate) -> str:
    """Format certificate as human-readable text.

    Args:
        certificate: Certificate to format

    Returns:
        Formatted string
    """
    lines = [
        "",
        "Proof Certificate",
        "=" * 50,
        "",
        f"Certificate ID: {certificate.certificate_id}",
        f"Original Run:   {certificate.original_run_id}",
        f"Hypothesis:     {certificate.hypothesis_id}",
        f"Created:        {certificate.created_at}",
        "",
        f"Verdict: {certificate.verdict}",
        f"Validation Runs: {certificate.pass_count}/{certificate.total_runs} passed",
        "",
    ]

    if certificate.is_proven:
        lines.append("This fix has been PROVEN to work.")
        lines.append("The patched code passes all validation runs with")
        lines.append("the same inputs that caused the original failure.")
    else:
        lines.append(f"Status: {certificate.verdict}")

    lines.append("")
    lines.append("Hashes:")
    lines.append(f"  Failure: {certificate.original_failure_hash[:16]}...")
    lines.append(f"  Patch:   {certificate.patch_hash[:16]}...")

    if certificate.signature:
        lines.append("")
        lines.append(f"Signature: {certificate.signature[:32]}...")
        lines.append("(Certificate is cryptographically signed)")

    return "\n".join(lines)
