"""
Testy aliasów pubkey/event-id — `AliasRegistry` + podmiana in/out + wrapper
`_zainstaluj_aliasy` na capabilities.

Model nigdy nie widzi surowego 64-hexa: na wyjściu hex→alias, na wejściu
alias→hex. Suma kontrolna chroni przed cichym trafieniem w inny pubkey.
"""

from __future__ import annotations

import os
import sys
import unittest
from types import SimpleNamespace

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "jou"))

from sdk.capability import Capability, CapabilityRegistry  # noqa: E402
from mcp_nostr_explorer.agent_runtime import (  # noqa: E402
    AliasRegistry,
    _podmień_wychodzące,
    _rozwiń_przychodzące,
    _rozwiń_do_wyświetlenia,
    _zainstaluj_aliasy,
    _znak_kontrolny,
)

HEX_A = "aaf478642ea11a02f566ba560a425ce7cfb15b8d6eb3b398f73210d2b11584dd"
HEX_B = "1111111111111111111111111111111111111111111111111111111111111111"


class TestAliasRegistry(unittest.TestCase):

    def test_mint_stabilny(self):
        reg = AliasRegistry()
        a1 = reg.mint(HEX_A)
        a2 = reg.mint(HEX_A)
        self.assertEqual(a1, a2)  # ten sam ciąg → ten sam alias
        self.assertTrue(a1.startswith("@p"))

    def test_rozne_ciagi_rozne_aliasy(self):
        reg = AliasRegistry()
        self.assertNotEqual(reg.mint(HEX_A), reg.mint(HEX_B))

    def test_round_trip(self):
        reg = AliasRegistry()
        alias = reg.mint(HEX_A)
        self.assertEqual(reg.expand(alias), HEX_A)

    def test_expand_nieznany_alias_rzuca(self):
        reg = AliasRegistry()
        # poprawny format + poprawna suma kontrolna, ale nigdy nie nadany
        alias = f"@p7{_znak_kontrolny(7)}"
        with self.assertRaises(ValueError):
            reg.expand(alias)

    def test_expand_zla_suma_rzuca(self):
        reg = AliasRegistry()
        alias = reg.mint(HEX_A)  # np. @p1d
        n = int(alias[2:-1])
        zła_litera = "a" if alias[-1] != "a" else "b"
        with self.assertRaises(ValueError):
            reg.expand(f"@p{n}{zła_litera}")

    def test_suma_kontrolna_to_litera(self):
        for n in range(1, 50):
            self.assertTrue(_znak_kontrolny(n).isalpha())


class TestPodmiana(unittest.TestCase):

    def test_wychodzace_w_zagniezdzonej_strukturze(self):
        reg = AliasRegistry()
        obj = {"rows": [{"pubkey": HEX_A, "n": 3}, {"pubkey": HEX_B}]}
        out = _podmień_wychodzące(obj, reg)
        self.assertEqual(out["rows"][0]["pubkey"], reg.mint(HEX_A))
        self.assertEqual(out["rows"][0]["n"], 3)  # nie-hex nietknięty
        self.assertNotIn(HEX_A, str(out))  # surowy hex zniknął

    def test_wychodzace_bech32(self):
        reg = AliasRegistry()
        npub = "npub14t68sepw5ydq9atxhftq5sjuul8mzkudd6em8x8hxggd9vg4snwsmxv60g"
        out = _podmień_wychodzące({"v": npub}, reg)
        self.assertTrue(out["v"].startswith("@p"))
        self.assertNotIn(npub, str(out))

    def test_przychodzace_w_sql(self):
        reg = AliasRegistry()
        alias = reg.mint(HEX_A)
        args = {"query": f"SELECT * FROM e WHERE pubkey = '{alias}'"}
        out = _rozwiń_przychodzące(args, reg)
        self.assertIn(HEX_A, out["query"])
        self.assertNotIn(alias, out["query"])

    def test_przychodzace_zla_suma_rzuca(self):
        reg = AliasRegistry()
        with self.assertRaises(ValueError):
            _rozwiń_przychodzące({"pubkey": "@p99z"}, reg)

    def test_round_trip_struktura(self):
        reg = AliasRegistry()
        out = _podmień_wychodzące({"pubkey": HEX_A}, reg)
        back = _rozwiń_przychodzące(out, reg)
        self.assertEqual(back["pubkey"], HEX_A)

    def test_display_best_effort_nie_rzuca(self):
        reg = AliasRegistry()
        alias = reg.mint(HEX_A)
        # znany alias → rozwinięty; nieznany → zostaje jak jest (bez wyjątku)
        txt = _rozwiń_do_wyświetlenia(f"user {alias} i obcy @p99z", reg)
        self.assertIn(HEX_A, txt)
        self.assertIn("@p99z", txt)


class TestInstalacjaNaCapabilities(unittest.IsolatedAsyncioTestCase):

    async def test_owija_in_i_out(self):
        widziane_args = {}

        async def handler(args: dict):
            widziane_args.update(args)            # co handler realnie dostał
            return {"echo_pubkey": HEX_B}          # co handler zwrócił (surowy hex)

        reg = CapabilityRegistry()
        reg.add(Capability(
            name="t__tool", description="d", input_schema={}, handler=handler,
            source="test",
        ))
        agent = SimpleNamespace(nazwa="t", registry=reg)
        sess = SimpleNamespace(aliases=AliasRegistry())

        # model dostanie alias HEX_A na wejściu, podaje go w args
        alias_a = sess.aliases.mint(HEX_A)
        _zainstaluj_aliasy(agent, sess)

        wynik = await reg.get("t__tool").handler({"pubkey": alias_a})

        # IN: handler dostał rozwinięty surowy hex, nie alias
        self.assertEqual(widziane_args["pubkey"], HEX_A)
        # OUT: zwrócony surowy hex zamieniony na alias zanim trafi do modelu
        self.assertEqual(wynik["echo_pubkey"], sess.aliases.mint(HEX_B))


if __name__ == "__main__":
    unittest.main()
