import json
import os
import re

_HEX64 = re.compile(r"^[0-9a-f]{64}$")
_HEX128 = re.compile(r"^[0-9a-f]{128}$")
_NOSTR_URI = re.compile(r"(nostr:[a-z]+1)([a-z0-9]+)")

# Keys whose values are event IDs (prefix "e")
_EVENT_ID_KEYS = {"id", "e"}
# Keys whose values are pubkeys (prefix "p")
_PUBKEY_KEYS = {"pubkey", "p"}
# Keys whose values are signatures (prefix "s")
_SIG_KEYS = {"sig"}


def _is_hex64(v) -> bool:
    return isinstance(v, str) and bool(_HEX64.match(v))

def _is_hex128(v) -> bool:
    return isinstance(v, str) and bool(_HEX128.match(v))


class AliasStore:
    def __init__(self, path: str):
        self._path = path
        self._data = self._load()

    def _load(self) -> dict:
        if not os.path.exists(self._path):
            return {"named": {}, "auto": {}, "counters": {"e": 0, "p": 0}}
        with open(self._path) as f:
            return json.load(f)

    def _save(self) -> None:
        with open(self._path, "w") as f:
            json.dump(self._data, f, indent=2)

    # ── Lookup ────────────────────────────────────────────────────────────────

    def alias_for(self, hash: str) -> str | None:
        """Zwraca alias dla hasha (named ma priorytet nad auto)."""
        for name, h in self._data["named"].items():
            if h == hash:
                return f"#{name}"
        auto = self._data["auto"].get(hash)
        return f"#{auto}" if auto else None

    def resolve(self, alias: str) -> str | None:
        """Zamienia #alias → pełny hash. Zwraca None jeśli nie znaleziono."""
        key = alias.lstrip("#")
        if key in self._data["named"]:
            return self._data["named"][key]
        # szukaj w auto: wartość to np. "e1", klucz to hash
        for hash, short in self._data["auto"].items():
            if short == key:
                return hash
        return None

    # ── Write ─────────────────────────────────────────────────────────────────

    def set_named(self, name: str, hash: str) -> str:
        """Ustawia nazwany alias. Zwraca potwierdzenie."""
        self._data["named"][name] = hash
        self._save()
        return f"#{name} → {hash}"

    def get_or_create(self, hash: str, prefix: str = "e") -> str:
        """Zwraca istniejący alias lub tworzy nowy auto-alias."""
        existing = self.alias_for(hash)
        if existing:
            return existing
        self._data["counters"][prefix] = self._data["counters"].get(prefix, 0) + 1
        short = f"{prefix}{self._data['counters'][prefix]}"
        self._data["auto"][hash] = short
        self._save()
        return f"#{short}"

    # ── Transform ─────────────────────────────────────────────────────────────

    def replace_in_data(self, data, _parent_key: str | None = None):
        """Rekurencyjnie zamienia hasze na aliasy w danych z relay."""
        if isinstance(data, dict):
            return {k: self.replace_in_data(v, k) for k, v in data.items()}
        if isinstance(data, list):
            # tagi Nostr: [["e", "<hash>", ...], ["p", "<hash>", ...]]
            if _parent_key == "tags":
                return [self._replace_tag(item) for item in data]
            return [self.replace_in_data(item, _parent_key) for item in data]
        if _is_hex64(data):
            prefix = self._prefix_for_key(_parent_key)
            return self.get_or_create(data, prefix)
        if _is_hex128(data):
            return self.get_or_create(data, "s")
        if isinstance(data, str) and _NOSTR_URI.search(data):
            return _NOSTR_URI.sub(lambda m: m.group(1) + self.get_or_create(m.group(2), "n"), data)
        return data

    def _replace_tag(self, tag: list) -> list:
        """Zamienia hasze w tagu Nostr: ["e"/"p", hash, ...]."""
        if not isinstance(tag, list) or len(tag) < 2:
            return tag
        key = tag[0]
        result = list(tag)
        if key in _EVENT_ID_KEYS | _PUBKEY_KEYS and _is_hex64(tag[1]):
            prefix = "e" if key in _EVENT_ID_KEYS else "p"
            result[1] = self.get_or_create(tag[1], prefix)
        return result

    def resolve_in_tags(self, tags: list) -> list:
        """Zamienia #aliasy → pełne hasze w tagach eventu przed publikacją."""
        result = []
        for tag in tags:
            if not isinstance(tag, list) or len(tag) < 2:
                result.append(tag)
                continue
            new_tag = list(tag)
            new_tag[1] = self._resolve_value(new_tag[1])
            result.append(new_tag)
        return result

    def resolve_in_filter(self, filter: dict) -> dict:
        """Zamienia #aliasy → pełne hasze w filtrze przed wysłaniem do relay."""
        result = {}
        for k, v in filter.items():
            if isinstance(v, list):
                result[k] = [self._resolve_value(item) for item in v]
            else:
                result[k] = v
        return result

    # ── Helpers ───────────────────────────────────────────────────────────────

    def _prefix_for_key(self, key: str | None) -> str:
        if key in _PUBKEY_KEYS:
            return "p"
        if key in _SIG_KEYS:
            return "s"
        return "e"

    def _resolve_value(self, v):
        if isinstance(v, str) and v.startswith("#"):
            resolved = self.resolve(v)
            return resolved if resolved else v
        return v

    def list_all(self) -> str:
        lines = []
        if self._data["named"]:
            lines.append("=== Nazwane ===")
            for name, h in self._data["named"].items():
                lines.append(f"  #{name} → {h}")
        if self._data["auto"]:
            lines.append("=== Auto ===")
            for h, short in self._data["auto"].items():
                lines.append(f"  #{short} → {h}")
        return "\n".join(lines) if lines else "Brak aliasów."
