#!/usr/bin/env python3
"""Bind Hermes Telegram participant names to the current update's visible handle."""

from __future__ import annotations

import argparse
import ast
import hashlib
import json
import os
import tempfile
from pathlib import Path

PATCH_VERSION = "teleo-telegram-visible-handle-name-policy-v1"
PATCH_MARKER = "            # TELEO_TELEGRAM_VISIBLE_HANDLE_NAME_POLICY_V1\n"
SOURCE_ANCHOR = "            user_name=user.full_name if user else None,\n"

REPLACEMENT = '''            # TELEO_TELEGRAM_VISIBLE_HANDLE_NAME_POLICY_V1
            # The current Telegram update is authoritative for the visible
            # handle. The stable Telegram user_id continues to own session
            # isolation; profile full_name is only a fallback when no public
            # username exists on this update.
            user_name=(
                (user.username or "").strip().lstrip("@")
                if user and (user.username or "").strip()
                else user.full_name if user else None
            ),
'''


def sha256_text(value: str) -> str:
    return hashlib.sha256(value.encode("utf-8")).hexdigest()


def patch_source(source: str) -> tuple[str, bool]:
    if PATCH_MARKER in source:
        required = (
            '(user.username or "").strip().lstrip("@")',
            "else user.full_name if user else None",
            "user_id=str(user.id) if user else None",
        )
        missing = [item for item in required if item not in source]
        if missing:
            raise ValueError(f"installed Telegram name-policy marker is incomplete: {missing}")
        if SOURCE_ANCHOR in source:
            raise ValueError("installed Telegram name-policy marker still contains the stale full-name anchor")
        return source, False

    if source.count(SOURCE_ANCHOR) != 1:
        raise ValueError("Hermes Telegram sender construction changed; refusing an unreviewed name-policy patch")

    patched = source.replace(SOURCE_ANCHOR, REPLACEMENT, 1)
    ast.parse(patched)
    return patched, True


def install(path: Path, *, check: bool) -> tuple[dict[str, str | bool], int]:
    source = path.read_text(encoding="utf-8")
    patched, changed = patch_source(source)
    before_sha = sha256_text(source)
    after_sha = sha256_text(patched)
    if check:
        return {
            "patch_version": PATCH_VERSION,
            "status": "patch_required" if changed else "installed",
            "target": str(path),
            "sha256": before_sha,
        }, 1 if changed else 0

    if changed:
        mode = path.stat().st_mode & 0o777
        with tempfile.NamedTemporaryFile(
            "w", encoding="utf-8", dir=path.parent, prefix=f".{path.name}.", delete=False
        ) as handle:
            handle.write(patched)
            temp_path = Path(handle.name)
        try:
            temp_path.chmod(mode)
            os.replace(temp_path, path)
        finally:
            temp_path.unlink(missing_ok=True)

    ast.parse(path.read_text(encoding="utf-8"))
    return {
        "patch_version": PATCH_VERSION,
        "status": "installed_now" if changed else "already_installed",
        "target": str(path),
        "before_sha256": before_sha,
        "after_sha256": after_sha,
    }, 0


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("target", type=Path)
    parser.add_argument("--check", action="store_true")
    args = parser.parse_args()
    result, code = install(args.target, check=args.check)
    print(json.dumps(result, sort_keys=True))
    return code


if __name__ == "__main__":
    raise SystemExit(main())
