"""Plan hashing using SHA256 of canonical JSON.

This module provides cryptographic hashing of plan content to ensure
integrity and enable deduplication. The hash is computed over the
canonical JSON representation to ensure determinism.
"""

import hashlib
from typing import Any

from cosig.security.canonical_json import to_canonical_json


def compute_plan_hash(plan_content: dict[str, Any] | str) -> str:
    """Compute SHA256 hash of plan content.

    The hash is computed as: SHA256(canonical_json(plan_content))

    Args:
        plan_content: Plan content as dictionary or JSON string

    Returns:
        Hexadecimal SHA256 hash string (64 characters)

    Examples:
        >>> compute_plan_hash({"tool": "bash", "cmd": "ls"})
        'a1b2c3...'  # 64-character hex string
    """
    canonical = to_canonical_json(plan_content)
    hash_bytes = hashlib.sha256(canonical.encode("utf-8")).digest()
    return hash_bytes.hex()


def verify_plan_hash(plan_content: dict[str, Any] | str, expected_hash: str) -> bool:
    """Verify that plan content matches expected hash.

    Args:
        plan_content: Plan content to verify
        expected_hash: Expected SHA256 hash (hex string)

    Returns:
        True if hash matches, False otherwise

    Examples:
        >>> content = {"tool": "bash", "cmd": "ls"}
        >>> hash_val = compute_plan_hash(content)
        >>> verify_plan_hash(content, hash_val)
        True
        >>> verify_plan_hash(content, "wrong_hash")
        False
    """
    if not expected_hash or len(expected_hash) != 64:
        return False

    try:
        actual_hash = compute_plan_hash(plan_content)
        # Use constant-time comparison to prevent timing attacks
        return (
            hashlib.sha256(actual_hash.encode()).digest()
            == hashlib.sha256(expected_hash.encode()).digest()
        )
    except (ValueError, TypeError):
        return False
