"""Gate ledger hash chain utilities.

Provides functions to compute receipt hashes, validate hash chains, and create
ledgers for the Governance Integrity System. Ensures gates cannot be skipped,
reordered, or tampered with by using cryptographic hash chaining.
"""

from __future__ import annotations

import hashlib
from datetime import datetime, timezone
from typing import Any

from .canonical_hash import canonical_hash_jcs_sha256


def compute_receipt_hash(
    gate_id: str,
    sequence: int,
    inputs_hash: str,
    outputs_hash: str,
    previous_receipt_hash: str | None,
) -> str:
    """Compute the receipt hash for a gate entry.

    Creates a hash chain link by combining the gate's own data with the
    previous receipt hash. This makes it impossible to skip or reorder gates
    without breaking the chain.

    Args:
        gate_id: Gate identifier (e.g., "lint", "test", "schema_validate").
        sequence: Position in chain (0-indexed).
        inputs_hash: SHA-256 hash of gate inputs (with "sha256:" prefix).
        outputs_hash: SHA-256 hash of gate outputs (with "sha256:" prefix).
        previous_receipt_hash: Previous gate's receipt_hash (with "sha256:" prefix),
                               or None for first gate (GENESIS).

    Returns:
        Receipt hash in format "sha256:<64 hex chars>".

    Example:
        >>> compute_receipt_hash(
        ...     "lint", 0, "sha256:abc...", "sha256:def...", None
        ... )
        'sha256:...'
    """
    # Strip "sha256:" prefix if present for cleaner concatenation
    inputs_hex = inputs_hash.replace("sha256:", "")
    outputs_hex = outputs_hash.replace("sha256:", "")
    prev_hex = previous_receipt_hash.replace("sha256:", "") if previous_receipt_hash else "GENESIS"

    # Create canonical representation: gate_id + sequence + inputs + outputs + previous
    # Using pipe separators for clarity and to prevent collision attacks
    chain_data = f"{gate_id}|{sequence}|{inputs_hex}|{outputs_hex}|{prev_hex}"

    # Compute SHA-256 of the chain data
    hash_digest = hashlib.sha256(chain_data.encode("utf-8")).hexdigest()

    return f"sha256:{hash_digest}"


def compute_final_governance_hash(last_receipt_hash: str, ledger_id: str) -> str:
    """Compute the final governance hash for the entire ledger.

    Combines the last receipt hash with the ledger ID to create a single
    integrity proof for the complete gate chain.

    Args:
        last_receipt_hash: The receipt_hash of the final gate in the chain
                          (with "sha256:" prefix).
        ledger_id: UUID of the ledger.

    Returns:
        Final governance hash in format "sha256:<64 hex chars>".

    Example:
        >>> compute_final_governance_hash(
        ...     "sha256:abc...", "550e8400-e29b-41d4-a716-446655440000"
        ... )
        'sha256:...'
    """
    # Strip "sha256:" prefix for cleaner concatenation
    last_hash_hex = last_receipt_hash.replace("sha256:", "")

    # Combine last receipt hash with ledger ID
    final_data = f"{last_hash_hex}|{ledger_id}"

    # Compute SHA-256
    hash_digest = hashlib.sha256(final_data.encode("utf-8")).hexdigest()

    return f"sha256:{hash_digest}"


def validate_chain(gates: list[dict[str, Any]]) -> tuple[bool, str]:
    """Validate the integrity of a gate hash chain.

    Walks the chain and recomputes each receipt_hash, verifying it matches
    the stored value. Detects any skipped, reordered, or tampered gates.

    Args:
        gates: List of gate entry dictionaries (from ledger "gates" array).

    Returns:
        Tuple of (is_valid, message):
        - (True, "Chain valid") if chain is intact
        - (False, "Chain broken at gate {gate_id}, sequence {seq}") if hash mismatch
        - (False, "Sequence discontinuity...") if sequence numbers are wrong
        - (False, "First gate must have previous_receipt_hash=null") if genesis broken

    Example:
        >>> gates = [
        ...     {"gate_id": "lint", "sequence": 0, "previous_receipt_hash": None, ...},
        ...     {"gate_id": "test", "sequence": 1, "previous_receipt_hash": "sha256:...", ...}
        ... ]
        >>> validate_chain(gates)
        (True, 'Chain valid')
    """
    if not gates:
        return True, "Chain valid (empty ledger)"

    previous_hash: str | None = None

    for i, gate in enumerate(gates):
        gate_id = gate["gate_id"]
        sequence = gate["sequence"]
        status = gate["status"]
        inputs_hash = gate["inputs_hash"]
        outputs_hash = gate["outputs_hash"]
        receipt_hash = gate["receipt_hash"]
        previous_receipt_hash = gate["previous_receipt_hash"]

        # Validate sequence is correct (0-indexed, monotonic)
        if sequence != i:
            return (
                False,
                f"Sequence discontinuity at gate '{gate_id}': expected sequence {i}, got {sequence}",
            )

        # First gate (GENESIS) must have previous_receipt_hash = null
        if i == 0:
            if previous_receipt_hash is not None:
                return (
                    False,
                    f"First gate '{gate_id}' must have previous_receipt_hash=null (GENESIS)",
                )
        else:
            # Subsequent gates must link to previous gate's receipt_hash
            if previous_receipt_hash != previous_hash:
                return (
                    False,
                    f"Chain broken at gate '{gate_id}', sequence {sequence}: "
                    f"previous_receipt_hash mismatch (expected {previous_hash}, got {previous_receipt_hash})",
                )

        # Recompute receipt_hash and verify it matches
        computed_hash = compute_receipt_hash(
            gate_id,
            sequence,
            inputs_hash,
            outputs_hash,
            previous_receipt_hash,
        )

        if computed_hash != receipt_hash:
            return (
                False,
                f"Chain broken at gate '{gate_id}', sequence {sequence}: "
                f"receipt_hash mismatch (expected {computed_hash}, got {receipt_hash})",
            )

        # Update previous_hash for next iteration
        previous_hash = receipt_hash

    return True, "Chain valid"


def create_ledger(
    run_id: str,
    repo: str,
    repo_head_sha: str,
    gates: list[dict[str, Any]],
    ledger_id: str | None = None,
) -> dict[str, Any]:
    """Create a gate ledger with validated hash chain.

    Validates that the provided gates form a valid hash chain before creating
    the ledger. Computes final_governance_hash from the last gate.

    Args:
        run_id: Run identifier.
        repo: Repository identifier.
        repo_head_sha: Git SHA-1 hash of repo HEAD (40 hex chars).
        gates: List of gate entries (must already have receipt_hash computed).
        ledger_id: Optional UUID for the ledger. If None, generates a new UUID.

    Returns:
        Complete ledger dictionary conforming to gate_ledger.schema.json.

    Raises:
        ValueError: If chain validation fails.

    Example:
        >>> gates = [
        ...     {
        ...         "gate_id": "lint",
        ...         "gate_version": "1.0.0",
        ...         "sequence": 0,
        ...         "status": "passed",
        ...         "inputs_hash": "sha256:...",
        ...         "outputs_hash": "sha256:...",
        ...         "receipt_hash": "sha256:...",
        ...         "previous_receipt_hash": None,
        ...         "started_at": "2024-01-01T00:00:00Z",
        ...         "completed_at": "2024-01-01T00:01:00Z",
        ...     }
        ... ]
        >>> ledger = create_ledger("run123", "abi-core", "abc...def", gates)
    """
    import uuid

    # Validate chain integrity
    is_valid, message = validate_chain(gates)
    if not is_valid:
        raise ValueError(f"Cannot create ledger: {message}")

    # Generate ledger_id if not provided
    if ledger_id is None:
        ledger_id = str(uuid.uuid4())

    # Compute final_governance_hash
    if gates:
        last_receipt_hash = gates[-1]["receipt_hash"]
        final_hash = compute_final_governance_hash(last_receipt_hash, ledger_id)
    else:
        # Empty ledger: hash of "GENESIS|ledger_id"
        final_data = f"GENESIS|{ledger_id}"
        final_hash_hex = hashlib.sha256(final_data.encode("utf-8")).hexdigest()
        final_hash = f"sha256:{final_hash_hex}"

    # Create ledger
    ledger = {
        "schema_version": "gate-ledger/1.0",
        "ledger_id": ledger_id,
        "run_id": run_id,
        "repo": repo,
        "repo_head_sha": repo_head_sha,
        "gates": gates,
        "final_governance_hash": final_hash,
        "created_at": datetime.now(timezone.utc).isoformat(),
    }

    return ledger
