"""Utility to compute hash of various content types."""

from __future__ import annotations

import hashlib
import json
from typing import Any

from pydantic import BaseModel


def compute_hash(content: Any, algorithm: str = "blake2b") -> str:
    """Compute hash of content.

    Parameters
    ----------
    content : Any
        Content to hash. Supports:
        - str: Direct encoding
        - bytes: Direct use
        - BaseModel: JSON serialization (model_dump_json)
        - dict: JSON serialization with sorted keys
        - Other types: String conversion
    algorithm : str, optional
        Hash algorithm to use, defaults to 'blake2b'
        Supported: 'blake2b', 'blake2s', 'md5', 'sha256'

    Returns
    -------
    str
        Hexadecimal hash string

    Examples
    --------
    >>> from pydantic import BaseModel
    >>> class MyModel(BaseModel):
    ...     name: str
    ...     value: int
    >>> model = MyModel(name="test", value=123)
    >>> # Pydantic model uses model_dump_json for stable hashing
    >>> hash_str = compute_hash(model)
    >>> len(hash_str)
    32
    """
    # Convert content to bytes based on type
    if isinstance(content, bytes):
        content_bytes = content
    elif isinstance(content, str):
        content_bytes = content.encode("utf-8")
    elif isinstance(content, BaseModel):
        # Use Pydantic's JSON serialization for stable hashing
        # exclude_unset=False ensures all fields are included
        # sort_keys not available in model_dump_json, but JSON is deterministic
        json_str = content.model_dump_json(indent=None, exclude_unset=False)
        content_bytes = json_str.encode("utf-8")
    elif isinstance(content, dict):
        # For dicts, use sorted JSON serialization for deterministic hashing
        json_str = json.dumps(content, sort_keys=True, ensure_ascii=False)
        content_bytes = json_str.encode("utf-8")
    else:
        # For other types, convert to string first
        content_bytes = str(content).encode("utf-8")

    # Compute hash based on algorithm
    if algorithm == "blake2b":
        return hashlib.blake2b(content_bytes, digest_size=16).hexdigest()
    elif algorithm == "blake2s":
        return hashlib.blake2s(content_bytes, digest_size=16).hexdigest()
    elif algorithm == "md5":
        return hashlib.md5(content_bytes).hexdigest()
    elif algorithm == "sha256":
        return hashlib.sha256(content_bytes).hexdigest()
    else:
        raise ValueError(f"Unsupported hash algorithm: {algorithm}")
