#!/usr/bin/env python3
"""Install the managed Hermes response and Telegram name-provenance patches."""

from __future__ import annotations

import argparse
import ast
import hashlib
import importlib.util
import json
import os
import tempfile
from pathlib import Path

PATCH_VERSION = "teleo-response-transform-hook-v1"
RUNTIME_PATCH_VERSION = "teleo-hermes-runtime-patches-v2"
PATCH_MARKER = "        # TELEO_RESPONSE_TRANSFORM_HOOK_V1\n"
START_ANCHOR = "        # Save trajectory if enabled\n"
END_ANCHOR = "        # Extract reasoning from the last assistant message (if any)\n"

REPLACEMENT = '''        # TELEO_RESPONSE_TRANSFORM_HOOK_V1
        # Run response validators before trajectory/session persistence. A plugin
        # may return {"assistant_response": "..."}; the last non-empty value wins.
        # This keeps delivered text and durable conversation memory identical.
        if final_response and not interrupted:
            try:
                from hermes_cli.plugins import invoke_hook as _invoke_hook
                _post_results = _invoke_hook(
                    "post_llm_call",
                    session_id=self.session_id,
                    user_message=original_user_message,
                    assistant_response=final_response,
                    conversation_history=list(messages),
                    model=self.model,
                    platform=getattr(self, "platform", None) or "",
                )
                _original_final_response = final_response
                for _post_result in _post_results:
                    if not isinstance(_post_result, dict):
                        continue
                    _candidate = _post_result.get("assistant_response")
                    if isinstance(_candidate, str) and _candidate.strip():
                        final_response = _candidate.strip()
                if final_response != _original_final_response:
                    _updated_assistant_message = False
                    for _message in reversed(messages):
                        if _message.get("role") == "assistant" and not _message.get("tool_calls"):
                            _message["content"] = final_response
                            _updated_assistant_message = True
                            break
                    if not _updated_assistant_message:
                        messages.append({"role": "assistant", "content": final_response})
            except Exception as exc:
                logger.warning("post_llm_call hook failed: %s", exc)

        # Save trajectory if enabled
        self._save_trajectory(messages, user_message, completed)

        # Clean up VM and browser for this task after conversation completes
        self._cleanup_task_resources(effective_task_id)

        # Persist session to both JSON log and SQLite
        self._persist_session(messages, conversation_history)


'''


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 = (
            '_candidate = _post_result.get("assistant_response")',
            "self._save_trajectory(messages, user_message, completed)",
            "self._persist_session(messages, conversation_history)",
        )
        missing = [item for item in required if item not in source]
        if missing:
            raise ValueError(f"installed patch marker is incomplete: {missing}")
        return source, False

    if source.count(START_ANCHOR) != 1 or source.count(END_ANCHOR) != 1:
        raise ValueError("Hermes response lifecycle anchors changed; refusing an unreviewed patch")
    start = source.index(START_ANCHOR)
    end = source.index(END_ANCHOR, start)
    original_block = source[start:end]
    required_original = (
        '"post_llm_call"',
        "self._save_trajectory(messages, user_message, completed)",
        "self._cleanup_task_resources(effective_task_id)",
        "self._persist_session(messages, conversation_history)",
    )
    missing = [item for item in required_original if item not in original_block]
    if missing:
        raise ValueError(f"Hermes response lifecycle changed; missing expected statements: {missing}")

    patched = source[:start] + REPLACEMENT + source[end:]
    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:
        status = "patch_required" if changed else "installed"
        return {
            "patch_version": PATCH_VERSION,
            "status": status,
            "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 _load_telegram_name_policy_patcher():
    path = Path(__file__).with_name("apply_telegram_sender_name_policy.py")
    spec = importlib.util.spec_from_file_location("teleo_telegram_sender_name_policy", path)
    if not spec or not spec.loader:
        raise RuntimeError(f"could not load Telegram name-policy patcher: {path}")
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


def install_runtime(run_agent_path: Path, *, check: bool) -> tuple[dict[str, object], int]:
    response_result, response_code = install(run_agent_path, check=check)
    name_policy = _load_telegram_name_policy_patcher()
    telegram_path = run_agent_path.parent / "gateway" / "platforms" / "telegram.py"
    name_result, name_code = name_policy.install(telegram_path, check=check)

    if check:
        changed = bool(response_code or name_code)
        status = "patch_required" if changed else "installed"
        code = 1 if changed else 0
    else:
        changed = any(
            result.get("status") == "installed_now"
            for result in (response_result, name_result)
        )
        status = "installed_now" if changed else "already_installed"
        code = 0

    return {
        "patch_version": RUNTIME_PATCH_VERSION,
        "status": status,
        "agent_root": str(run_agent_path.parent),
        "response_transform": response_result,
        "telegram_sender_name_policy": name_result,
    }, code


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_runtime(args.target, check=args.check)
    print(json.dumps(result, sort_keys=True))
    return code


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