# #region orchestrator_config [C:3] [TYPE Module] [SEMANTICS hash, config, snapshot, translate]
# @defgroup Translate Module group.
# @BRIEF Config hash and dictionary snapshot hash utilities for translation planning.
# @RELATION DEPENDS_ON -> [TranslationJob]
# @RELATION DEPENDS_ON -> [TranslationJobDictionary]

import hashlib
import json


# #region compute_config_hash [C:2] [TYPE Function] [SEMANTICS config, hash, deterministic]
# @ingroup Translate
# @BRIEF Compute a deterministic hash of job configuration for snapshot comparison.
# @PRE job has valid configuration attributes.
# @POST Returns 16-char hex SHA-256 hash.
def compute_config_hash(job) -> str:
    """Compute a hash of the job's current configuration for snapshot comparison."""
    config_str = json.dumps({
        "source_dialect": job.source_dialect,
        "target_dialect": job.target_dialect,
        "source_datasource_id": job.source_datasource_id,
        "translation_column": job.translation_column,
        "context_columns": job.context_columns,
        "target_languages": sorted(job.target_languages) if job.target_languages else [],
        "provider_id": job.provider_id,
        "batch_size": job.batch_size,
        "upsert_strategy": job.upsert_strategy,
    }, sort_keys=True)
    return hashlib.sha256(config_str.encode()).hexdigest()[:16]
# #endregion compute_config_hash


# #region compute_dict_snapshot_hash [C:2] [TYPE Function] [SEMANTICS dictionary, hash, snapshot]
# @ingroup Translate
# @BRIEF Compute a hash of dictionary state for snapshot comparison.
#         Includes dictionary IDs, entry count, and max entry updated_at
#         to invalidate cache when dictionary entries are modified.
# @PRE db_session is valid.
# @POST Returns 16-char hex SHA-256 hash.
def compute_dict_snapshot_hash(db_session, job_id: str) -> str:
    """Compute a hash of dictionary state for snapshot comparison."""
    from ...models.translate import TranslationJobDictionary, DictionaryEntry
    from sqlalchemy import func

    dict_links = (
        db_session.query(TranslationJobDictionary)
        .filter(TranslationJobDictionary.job_id == job_id)
        .all()
    )
    if not dict_links:
        return hashlib.sha256(b"").hexdigest()[:16]

    parts: list[str] = []
    for dl in sorted(dict_links, key=lambda d: d.dictionary_id or ""):
        # Include entry count + max updated_at to detect content changes
        entry_count = (
            db_session.query(func.count(DictionaryEntry.id))
            .filter(DictionaryEntry.dictionary_id == dl.dictionary_id)
            .scalar()
        )
        max_updated = (
            db_session.query(func.max(DictionaryEntry.updated_at))
            .filter(DictionaryEntry.dictionary_id == dl.dictionary_id)
            .scalar()
        )
        max_updated_str = max_updated.isoformat() if max_updated else "0"
        parts.append(f"{dl.dictionary_id}:c{entry_count}:u{max_updated_str}")

    hash_input = "|".join(parts)
    return hashlib.sha256(hash_input.encode()).hexdigest()[:16]
# #endregion compute_dict_snapshot_hash
# #endregion orchestrator_config
