"""
helix_cdc/routing/route_receipt.py
==================================

Generate and validate tamper-evident receipts for route selection.

Receipt Schema (helix_route:v1):
{
    "receipt_type": "ROUTE",
    "schema": "helix_route:v1",
    "schema_version": "v1",
    "qubo": {
        "n_vars": int,
        "qubo_sha256": str
    },
    "solution": {
        "selected_specialists": [str],
        "selected_backend": str,
        "selected_world": str,
        "energy": float
    },
    "constraints": {
        "k_target": int,
        "k_actual": int,
        "constraints_satisfied": bool,
        "fidelity_band": str,
        "vram_budget_mb": int
    },
    "costs": {
        "latency_cost": float,
        "memory_cost": float,
        "thermal_cost": float,
        "se_penalty": float,
        "fidelity_penalty": float
    },
    "solver": {
        "backend": str,
        "seed": int,
        "sweeps": int,
        "solve_time_ms": float
    },
    "provenance": {
        "git_commit": str,
        "timestamp_utc": str
    },
    "receipt_sha256": str
}
"""

from __future__ import annotations

import json
import hashlib
import time
import subprocess
from pathlib import Path
from typing import Dict, Any, Optional, List
from dataclasses import asdict

from .qubo_route_builder import QUBOMetadata
from .route_solver import RouteSolution


def generate_route_receipt(
    Q: Dict[tuple, float],
    metadata: QUBOMetadata,
    solution: RouteSolution,
    costs: Optional[Dict[str, float]] = None,
    sweeps: int = 1000,
) -> Dict[str, Any]:
    """
    Generate route receipt from QUBO and solution.

    Args:
        Q: QUBO dict
        metadata: QUBOMetadata from builder
        solution: RouteSolution from solver
        costs: Optional cost breakdown dict
        sweeps: Number of sweeps used

    Returns:
        Receipt dict (not yet finalized - call finalize_route_receipt)
    """
    if costs is None:
        costs = _estimate_costs(Q, solution, metadata)

    receipt = {
        "receipt_type": "ROUTE",
        "schema": "helix_route:v1",
        "qubo": {
            "n_vars": metadata.n_vars,
            "n_specialists": metadata.n_specialists,
            "n_backends": metadata.n_backends,
            "n_worlds": metadata.n_worlds,
            "qubo_sha256": metadata.qubo_sha256,
        },
        "solution": {
            "selected_specialists": solution.selected_specialists,
            "selected_backend": solution.selected_backend,
            "selected_world": solution.selected_world,
            "energy": round(solution.energy, 6),
            "solution_sha256": solution.solution_sha256,
        },
        "constraints": {
            "k_target": metadata.k_target,
            "k_actual": solution.k_actual,
            "constraints_satisfied": solution.constraints_satisfied,
            "fidelity_band": metadata.fidelity_band,
            "vram_budget_mb": metadata.vram_budget_mb,
            "se_target": metadata.se_target,
        },
        "costs": costs,
        "solver": {
            "backend": solution.backend,
            "seed": solution.seed,
            "sweeps": sweeps,
            "solve_time_ms": round(solution.solve_time_ms, 3),
        },
        "provenance": {
            "git_commit": _get_git_commit(),
            "timestamp_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
        },
    }

    return receipt


def finalize_route_receipt(receipt: Dict[str, Any]) -> Dict[str, Any]:
    """
    Finalize receipt by adding schema_version and receipt_sha256.

    Args:
        receipt: Receipt dict

    Returns:
        Receipt with schema_version and receipt_sha256 added
    """
    # Add schema_version
    if "schema_version" not in receipt:
        receipt["schema_version"] = "v1"

    # Compute receipt_sha256 (excluding the field itself)
    receipt_clean = {k: v for k, v in receipt.items() if k != "receipt_sha256"}
    canonical = json.dumps(receipt_clean, sort_keys=True, separators=(",", ":"))
    receipt["receipt_sha256"] = hashlib.sha256(canonical.encode()).hexdigest()

    return receipt


def validate_route_receipt(
    receipt: Dict[str, Any],
    strict: bool = True,
) -> Dict[str, Any]:
    """
    Validate route receipt integrity and structure.

    Args:
        receipt: Receipt dict to validate
        strict: If True, require all fields

    Returns:
        Validation report: {"valid": bool, "errors": [...], "sha256_match": bool}
    """
    errors = []

    # Check required fields
    required = [
        "receipt_type", "schema", "qubo", "solution",
        "constraints", "solver", "provenance"
    ]

    for field in required:
        if field not in receipt:
            errors.append(f"Missing required field: {field}")

    # Check receipt_type
    if receipt.get("receipt_type") != "ROUTE":
        errors.append(f"Invalid receipt_type: {receipt.get('receipt_type')}")

    # Check schema
    if not receipt.get("schema", "").startswith("helix_route:"):
        errors.append(f"Invalid schema: {receipt.get('schema')}")

    # Check solution structure
    solution = receipt.get("solution", {})
    if not isinstance(solution.get("selected_specialists"), list):
        errors.append("solution.selected_specialists must be a list")
    if not isinstance(solution.get("selected_backend"), str):
        errors.append("solution.selected_backend must be a string")
    if not isinstance(solution.get("selected_world"), str):
        errors.append("solution.selected_world must be a string")

    # Check constraints
    constraints = receipt.get("constraints", {})
    if not isinstance(constraints.get("k_target"), int):
        errors.append("constraints.k_target must be an integer")
    if not isinstance(constraints.get("k_actual"), int):
        errors.append("constraints.k_actual must be an integer")

    # Verify receipt_sha256
    sha256_match = None
    if "receipt_sha256" in receipt:
        receipt_clean = {k: v for k, v in receipt.items() if k != "receipt_sha256"}
        canonical = json.dumps(receipt_clean, sort_keys=True, separators=(",", ":"))
        expected = hashlib.sha256(canonical.encode()).hexdigest()
        actual = receipt["receipt_sha256"]
        sha256_match = (expected == actual)
        if not sha256_match:
            errors.append(f"receipt_sha256 mismatch: expected {expected[:16]}..., got {actual[:16]}...")

    return {
        "valid": len(errors) == 0,
        "errors": errors,
        "sha256_match": sha256_match,
    }


def _estimate_costs(
    Q: Dict[tuple, float],
    solution: RouteSolution,
    metadata: QUBOMetadata,
) -> Dict[str, float]:
    """
    Estimate cost breakdown from QUBO solution.

    This is an approximation since the QUBO combines multiple costs.
    """
    costs = {
        "total_energy": round(solution.energy, 6),
        "latency_cost": 0.0,
        "memory_cost": 0.0,
        "thermal_cost": 0.0,
        "se_penalty": 0.0,
        "fidelity_penalty": 0.0,
        "constraint_penalty": 0.0,
    }

    # Estimate constraint violation penalty
    if not solution.constraints_satisfied:
        k_diff = abs(solution.k_actual - metadata.k_target)
        costs["constraint_penalty"] = k_diff * 100.0  # Approximate

    # Backend latency (rough estimate)
    backend_latency = {
        "cpu": 50.0,
        "gpu": 20.0,
        "qpu": 800.0,
    }
    costs["latency_cost"] = backend_latency.get(solution.selected_backend, 50.0) / 1000.0

    # Memory from number of specialists
    costs["memory_cost"] = solution.k_actual * 0.05  # Rough estimate

    return costs


def _get_git_commit() -> str:
    """Get current git commit hash."""
    try:
        base = Path(__file__).resolve().parent.parent.parent
        result = subprocess.run(
            ["git", "-C", str(base), "rev-parse", "--short", "HEAD"],
            capture_output=True,
            text=True,
            check=True,
        )
        return result.stdout.strip()
    except Exception:
        return "unknown"


def save_route_receipt(
    receipt: Dict[str, Any],
    path: Path,
    finalize: bool = True,
) -> Path:
    """
    Save route receipt to file.

    Args:
        receipt: Receipt dict
        path: Output path
        finalize: If True, finalize receipt before saving

    Returns:
        Path to saved receipt
    """
    if finalize:
        receipt = finalize_route_receipt(receipt)

    path = Path(path)
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(receipt, indent=2))

    return path


def load_route_receipt(path: Path) -> Dict[str, Any]:
    """Load route receipt from file."""
    return json.loads(Path(path).read_text())
