"""LLMHosts model checksums — SHA-256 manifest for supply chain verification.

This file is the single source of truth for known-good model file hashes.
It lives in source control so that a compromised HuggingFace uploader
cannot substitute a malicious file without the hash mismatch being detected.

To update checksums after a new model version, run:
    python -m llmhosts.models.checksums --update

Hashes are verified automatically on every model download. If a hash is
missing (None), a warning is logged and download proceeds unverified.
"""

from __future__ import annotations

# SHA-256 hashes of the canonical GGUF files as verified on 2026-03-10.
# Hash format: lowercase hex string (64 chars).
# None = not yet verified (safe default — logs warning, download still proceeds).
#
# Populate via: python -m llmhosts.models.checksums --update
# Or pin manually after verifying: sha256sum path/to/file.gguf
CHECKSUMS: dict[str, str | None] = {
    # Qwen models — from Qwen org (first-party, lower risk)
    "qwen2.5-0.5b-instruct-q4_k_m.gguf": None,  # TODO: pin after verification
    "qwen2.5-3b-instruct-q4_k_m.gguf": None,
    "qwen2.5-7b-instruct-q4_k_m.gguf": None,
    "qwen2.5-14b-instruct-q4_k_m.gguf": None,
    # Meta Llama models — from bartowski (third-party quantizer, higher risk)
    "Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf": None,  # TODO: pin after verification
    "Llama-3.3-70B-Instruct-Q4_K_M.gguf": None,
    # Mistral — from TheBloke (third-party, higher risk)
    "mistral-7b-instruct-v0.3.Q4_K_M.gguf": None,
    # DeepSeek — from bartowski (third-party)
    "DeepSeek-R1-Distill-Qwen-7B-Q4_K_M.gguf": None,
}


def verify_file(path: str, filename: str) -> bool | None:
    """Verify a downloaded GGUF file against the known-good SHA-256 hash.

    Returns:
        True  — hash present and matches
        False — hash present and DOES NOT match (reject the file)
        None  — hash not yet pinned in manifest (proceed with warning)
    """
    import hashlib
    import logging

    log = logging.getLogger(__name__)
    expected = CHECKSUMS.get(filename)

    if expected is None:
        log.warning(
            "No SHA-256 checksum pinned for %s — download unverified. "
            "Run `python -m llmhosts.models.checksums --update` to pin.",
            filename,
        )
        return None

    log.debug("Verifying SHA-256 for %s...", filename)
    sha256 = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(65536), b""):
            sha256.update(chunk)
    actual = sha256.hexdigest()

    if actual != expected:
        log.error(
            "SHA-256 MISMATCH for %s: expected %s, got %s. File may be corrupted or tampered. Deleting.",
            filename,
            expected,
            actual,
        )
        return False

    log.info("SHA-256 verified: %s", filename)
    return True


if __name__ == "__main__":
    import argparse
    import sys

    parser = argparse.ArgumentParser(description="Update LLMHosts model checksums")
    parser.add_argument("--update", action="store_true", help="Fetch and print hashes for all local models")
    args = parser.parse_args()

    if args.update:
        import hashlib
        from pathlib import Path

        from llmhosts.models.registry import MODELS, models_dir

        d = Path(models_dir())
        for model_name, info in MODELS.items():
            filepath = d / info["file"]
            if not filepath.exists():
                print(f"  MISSING: {info['file']} (download first with: llmhosts pull {model_name})")
                continue
            sha256 = hashlib.sha256()
            with open(filepath, "rb") as f:
                for chunk in iter(lambda: f.read(65536), b""):
                    sha256.update(chunk)
            print(f'    "{info["file"]}": "{sha256.hexdigest()}",')
    else:
        parser.print_help()
        sys.exit(1)
