"""Shared utilities for Novyx Core tools."""

import hashlib
import json
from pathlib import Path
from typing import Union, Optional, Set

# Default fields to exclude when validating existing artifacts
DEFAULT_HASH_FIELDS = {'novyx:integrityHash', 'Integrity_Hash'}


def calculate_hash(
    data: Union[str, Path, dict],
    *,
    exclude_fields: Optional[Set[str]] = None,
    sort_keys: bool = False
) -> str:
    """
    Calculate SHA-256 hash for integrity verification.

    Args:
        data: Content to hash. Can be:
            - str: Raw string content (hashed directly)
            - Path: File path (read and processed line-by-line)
            - dict: Dictionary (serialized to JSON)
        exclude_fields: Set of field names whose lines should be excluded.
            Defaults to DEFAULT_HASH_FIELDS for Path/dict inputs.
            Use empty set to include all fields.
        sort_keys: Whether to sort keys when serializing dict.
            Defaults to False to preserve existing validation behavior.
            Ignored for str and Path inputs.

    Returns:
        SHA-256 hex digest string.
    """
    if isinstance(data, str):
        # Raw string: hash directly
        content = data

    elif isinstance(data, Path):
        # File path: line-based filtering (preserves exact file formatting)
        exclude = exclude_fields if exclude_fields is not None else DEFAULT_HASH_FIELDS
        with open(data, 'r', encoding='utf-8') as f:
            lines = f.readlines()
        content = ''.join(
            line for line in lines
            if not any(f'"{field}"' in line for field in exclude)
        )

    elif isinstance(data, dict):
        # Dict: serialize to JSON, then filter lines
        exclude = exclude_fields if exclude_fields is not None else DEFAULT_HASH_FIELDS
        json_str = json.dumps(data, indent=2, sort_keys=sort_keys)
        if exclude:
            lines = json_str.split('\n')
            content = '\n'.join(
                line for line in lines
                if not any(f'"{field}"' in line for field in exclude)
            )
        else:
            content = json_str

    else:
        raise TypeError(f"Unsupported data type: {type(data)}")

    return hashlib.sha256(content.encode('utf-8')).hexdigest()


def calculate_hash_for_new_artifact(data: dict) -> str:
    """
    Calculate hash for a NEW artifact (before hash field is added).

    Uses sort_keys=True for deterministic output regardless of
    dict insertion order. Excludes no fields since hash field
    has not been added yet.
    """
    return calculate_hash(data, exclude_fields=set(), sort_keys=True)
