"""Tests for deterministic Telegram participant-name provenance in Hermes."""

from __future__ import annotations

import importlib.util
from pathlib import Path
from types import SimpleNamespace

import pytest

ROOT = Path(__file__).resolve().parents[1]
PATCHER = ROOT / "hermes-agent" / "patches" / "apply_telegram_sender_name_policy.py"


def load_patcher():
    spec = importlib.util.spec_from_file_location("hermes_telegram_sender_name_policy", PATCHER)
    assert spec and spec.loader
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


def upstream_fixture() -> str:
    return '''class Adapter:
    def build_source(self, **values):
        return values

    def build_event_source(self, user):
        return self.build_source(
            user_id=str(user.id) if user else None,
            user_name=user.full_name if user else None,
        )
'''


def patched_adapter_class():
    module = load_patcher()
    patched, changed = module.patch_source(upstream_fixture())
    assert changed is True
    namespace: dict[str, object] = {}
    exec(patched, namespace)
    return namespace["Adapter"], module, patched


def test_current_visible_handle_overrides_stale_profile_name() -> None:
    adapter_class, _module, _patched = patched_adapter_class()
    user = SimpleNamespace(id=9070919, username="m3taversal", full_name="STALE-PERSONAL-NAME")

    source = adapter_class().build_event_source(user)

    assert "m3taversal" not in PATCHER.read_text(encoding="utf-8")
    assert source == {"user_id": "9070919", "user_name": "m3taversal"}
    assert "STALE-PERSONAL-NAME" not in source.values()


def test_each_participant_keeps_own_handle_and_stable_user_id() -> None:
    adapter_class, _module, _patched = patched_adapter_class()
    target = SimpleNamespace(id=9070919, username="m3taversal", full_name="STALE-PERSONAL-NAME")
    other = SimpleNamespace(id=8181818, username="other_operator", full_name="Other Operator")

    first_target = adapter_class().build_event_source(target)
    other_source = adapter_class().build_event_source(other)
    restarted_target = adapter_class().build_event_source(target)

    assert first_target == restarted_target == {"user_id": "9070919", "user_name": "m3taversal"}
    assert other_source == {"user_id": "8181818", "user_name": "other_operator"}
    assert first_target["user_id"] != other_source["user_id"]
    assert first_target["user_name"] != other_source["user_name"]


def test_full_name_is_only_the_no_username_fallback() -> None:
    adapter_class, _module, _patched = patched_adapter_class()
    user = SimpleNamespace(id=7171717, username=None, full_name="Visible Display Name")

    assert adapter_class().build_event_source(user) == {
        "user_id": "7171717",
        "user_name": "Visible Display Name",
    }


def test_patch_is_idempotent_and_rejects_unknown_upstream_shape() -> None:
    _adapter_class, module, patched = patched_adapter_class()

    assert module.patch_source(patched) == (patched, False)
    with pytest.raises(ValueError, match="sender construction changed"):
        module.patch_source("class Adapter:\n    pass\n")


def test_install_check_reports_patch_required_then_installed(tmp_path: Path) -> None:
    module = load_patcher()
    target = tmp_path / "telegram.py"
    target.write_text(upstream_fixture(), encoding="utf-8")

    before, before_code = module.install(target, check=True)
    assert before_code == 1
    assert before["status"] == "patch_required"

    applied, applied_code = module.install(target, check=False)
    assert applied_code == 0
    assert applied["status"] == "installed_now"

    after, after_code = module.install(target, check=True)
    assert after_code == 0
    assert after["status"] == "installed"
