"""
Receipt generation and schema for Helix CDC operations

Receipts prove:
- What changed (blocks touched, bytes written)
- Determinism (seed, overlay, spec hashes)
- Impl version (SHA256 pinned)
- Timing (wall-clock)
- Environment (git commit, CPU flags, PYTHONHASHSEED)
"""
import hashlib
import json
import time
import os
import subprocess
import platform
from typing import Dict, Any, List, Optional
from pathlib import Path

# Frozen impl SHA256 for v0.1.0
IMPL_SHA256 = "helix64_min:v1:20251021"

# Receipt schema version (bump on breaking changes)
SCHEMA_VERSION = "0.1.0"


def _get_git_commit() -> Optional[str]:
    """Get current git commit SHA (returns None if not a git repo)"""
    try:
        result = subprocess.run(
            ["git", "rev-parse", "HEAD"],
            capture_output=True,
            text=True,
            timeout=2
        )
        if result.returncode == 0:
            return result.stdout.strip()
    except Exception:
        pass
    return None


def _get_cpu_flags() -> str:
    """Get CPU flags (x86: AVX, AVX2, SSE; ARM: NEON)"""
    try:
        if platform.machine() in ["x86_64", "AMD64"]:
            # Check /proc/cpuinfo on Linux
            if os.path.exists("/proc/cpuinfo"):
                with open("/proc/cpuinfo") as f:
                    for line in f:
                        if line.startswith("flags"):
                            flags = line.split(":")[1].strip()
                            # Extract key flags
                            key_flags = []
                            for flag in ["avx2", "avx", "sse4_2", "sse4_1"]:
                                if flag in flags:
                                    key_flags.append(flag)
                            return ",".join(key_flags) if key_flags else "none"
        elif "arm" in platform.machine().lower() or "aarch64" in platform.machine().lower():
            # ARM: check for NEON
            if os.path.exists("/proc/cpuinfo"):
                with open("/proc/cpuinfo") as f:
                    cpuinfo = f.read()
                    if "neon" in cpuinfo.lower():
                        return "neon"
    except Exception:
        pass
    return "unknown"


def _compute_schema_sha256() -> str:
    """Compute SHA256 of receipt schema (for schema versioning)"""
    # Hash the SCHEMA_VERSION + key field names
    schema_definition = {
        "version": SCHEMA_VERSION,
        "fields": [
            "protocol_version", "schema_version", "timestamp_utc",
            "codec", "cdc_operations", "determinism", "timing",
            "files", "environment", "provenance"
        ]
    }
    canonical = json.dumps(schema_definition, sort_keys=True)
    return hashlib.sha256(canonical.encode()).hexdigest()

def generate_receipt(
    capsule_path: str,
    overlay_path: str,
    output_path: str,
    cdc_meta: Dict[str, Any],
    timing: Dict[str, float],
    **kwargs
) -> Dict[str, Any]:
    """
    Generate a Helix CDC receipt

    Args:
        capsule_path: Input capsule path
        overlay_path: Overlay JSON path
        output_path: Output capsule path
        cdc_meta: CDC operations metadata (blocks touched, etc.)
        timing: Timing data (total_seconds, per_block_ms)
        **kwargs: Additional metadata

    Returns:
        Receipt dict (also written to {output_path}.receipt.json)
    """

    # Read overlay for hash
    with open(overlay_path, 'rb') as f:
        overlay_bytes = f.read()
        overlay_sha256 = hashlib.sha256(overlay_bytes).hexdigest()

    # Read spec if provided
    spec_sha256 = kwargs.get('spec_yaml_sha256', None)
    if 'spec_path' in kwargs:
        with open(kwargs['spec_path'], 'rb') as f:
            spec_sha256 = hashlib.sha256(f.read()).hexdigest()

    # Build receipt
    receipt = {
        "protocol_version": "helix_cdc_v1",
        "schema_version": "0.1.0",
        "timestamp_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),

        "codec": {
            "codec_id": cdc_meta.get("codec_id", "helix64:v1"),
            "codec_algo_id_hex": cdc_meta.get("codec_algo_id_hex", "0x33"),
            "impl_sha256": IMPL_SHA256,
            "random_access": True,
            "frame_index_present": cdc_meta.get("frame_index_present", False),
            "mode": cdc_meta.get("mode", "native")
        },

        "cdc_operations": {
            "blocks_total": cdc_meta["blocks_total"],
            "blocks_touched": cdc_meta["blocks_touched"],
            "blocks_touched_count": len(cdc_meta["blocks_touched"]),
            "blocks_touched_ratio": len(cdc_meta["blocks_touched"]) / cdc_meta["blocks_total"],
            "bytes_written": cdc_meta["bytes_written"],
            "block_size": cdc_meta.get("block_size", 32768),
            "write_method": "block_cdc"
        },

        "determinism": {
            "seed_string": cdc_meta.get("seed_string", ""),
            "overlay_sha256": overlay_sha256,
            "spec_yaml_sha256": spec_sha256,
            "impl_sha256": IMPL_SHA256
        },

        "timing": {
            "total_seconds": timing["total_seconds"],
            "per_block_ms": timing.get("per_block_ms", 0),
            "wall_clock_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
        },

        "files": {
            "input_capsule": str(Path(capsule_path).name),
            "overlay": str(Path(overlay_path).name),
            "output_capsule": str(Path(output_path).name)
        },

        "environment": {
            "python_version": kwargs.get("python_version", platform.python_version()),
            "platform": platform.system().lower(),
            "machine": platform.machine(),
            "helix_cdc_version": "0.1.0"
        },

        "provenance": {
            "git_commit": _get_git_commit(),
            "cpu_flags": _get_cpu_flags(),
            "pythonhashseed": os.environ.get("PYTHONHASHSEED", "random"),
            "schema_sha256": _compute_schema_sha256(),
            "impl_sha256": IMPL_SHA256,
            "deterministic_build": os.environ.get("PYTHONHASHSEED") is not None
        }
    }

    # Write receipt
    receipt_path = str(output_path) + ".receipt.json"
    with open(receipt_path, 'w') as f:
        json.dump(receipt, f, indent=2, sort_keys=True)

    return receipt


def receipt_id(receipt: Dict[str, Any]) -> str:
    """
    Generate stable receipt ID for referencing

    Format: {codec_id}:cdc_{timestamp}_{hash8}
    Example: helix64:v1:cdc_20251021_a1b2c3d4
    """
    codec_id = receipt["codec"]["codec_id"]
    timestamp = receipt["timestamp_utc"][:10].replace("-", "")

    # Hash receipt deterministically
    canonical = json.dumps(receipt, sort_keys=True, separators=(',', ':'))
    hash8 = hashlib.sha256(canonical.encode()).hexdigest()[:8]

    return f"{codec_id}:cdc_{timestamp}_{hash8}"


def claim_check(receipt: Dict[str, Any]) -> Dict[str, Any]:
    """
    Validate receipt claims against acceptance gates

    Returns:
        {
            "passes": bool,
            "gates": {
                "impl_pinned": bool,
                "blocks_ratio_ok": bool,
                "determinism_ok": bool,
                "provenance_ok": bool
            },
            "issues": [str, ...]
        }
    """
    issues = []
    gates = {}

    # Gate 1: Impl SHA256 must match frozen version
    gates["impl_pinned"] = (
        receipt["codec"]["impl_sha256"] == IMPL_SHA256 and
        receipt["determinism"]["impl_sha256"] == IMPL_SHA256 and
        receipt.get("provenance", {}).get("impl_sha256") == IMPL_SHA256
    )
    if not gates["impl_pinned"]:
        issues.append(f"Impl SHA256 mismatch (expected {IMPL_SHA256})")

    # Gate 2: Blocks touched ratio < 1.0 (CDC should reduce writes)
    ratio = receipt["cdc_operations"]["blocks_touched_ratio"]
    gates["blocks_ratio_ok"] = ratio < 1.0
    if not gates["blocks_ratio_ok"]:
        issues.append(f"CDC wrote 100% of blocks (ratio={ratio:.3f})")

    # Gate 3: Determinism triple-hash present
    det = receipt["determinism"]
    gates["determinism_ok"] = (
        det["seed_string"] and
        det["overlay_sha256"] and
        det["impl_sha256"]
    )
    if not gates["determinism_ok"]:
        issues.append("Determinism triple-hash incomplete")

    # Gate 4: Provenance present (git commit, schema hash, cpu flags)
    prov = receipt.get("provenance", {})
    gates["provenance_ok"] = (
        prov.get("git_commit") is not None and
        prov.get("schema_sha256") and
        prov.get("cpu_flags") != "unknown"
    )
    if not gates["provenance_ok"]:
        issues.append("Provenance incomplete (missing git_commit, schema_sha256, or cpu_flags)")

    # Overall pass/fail
    passes = all(gates.values())

    return {
        "passes": passes,
        "gates": gates,
        "issues": issues
    }
