"""Cryptographic and perceptual hashing utilities."""

from __future__ import annotations

import hashlib
import io

import imagehash
from PIL import Image


def compute_sha256(data: bytes) -> str:
    """Return the hex-encoded SHA-256 digest of *data*."""
    return hashlib.sha256(data).hexdigest()


def compute_perceptual_hash(image_bytes: bytes) -> str:
    """Compute a perceptual hash (pHash) of an image.

    Returns the hash as a hex string. Two visually similar images will
    produce hashes with a small Hamming distance.
    """
    image = Image.open(io.BytesIO(image_bytes))
    return str(imagehash.phash(image))


def hamming_distance(hash1: str, hash2: str) -> int:
    """Compute the Hamming distance between two hex-encoded hashes.

    Both hashes must be the same length. The distance equals the number
    of differing bits.
    """
    if len(hash1) != len(hash2):
        raise ValueError(
            f"Hash length mismatch: {len(hash1)} vs {len(hash2)}"
        )
    val1 = int(hash1, 16)
    val2 = int(hash2, 16)
    return bin(val1 ^ val2).count("1")


def compute_hashes(image_bytes: bytes) -> dict[str, str]:
    """Compute both SHA-256 and perceptual hashes for an image.

    Returns a dict with keys ``sha256`` and ``phash``.
    """
    return {
        "sha256": compute_sha256(image_bytes),
        "phash": compute_perceptual_hash(image_bytes),
    }
