"""Публикация markdown-дайджеста на telegra.ph — mobile-friendly страница с
кликабельными ссылками и Instant View прямо в Telegram.

Заточен под наш генерируемый markdown: заголовки, списки, болд, ссылки
(голые и [текст](url)), hr. Таблицы Telegraph не поддерживает — строки
рендерим абзацами. Только стандартная библиотека (urllib) — Mac и Pi без venv.

Токен доступа — в env TELEGRAPH_TOKEN (создаётся один раз: `python3 telegraph.py --create-account`).
Ссылки на telegra.ph непубличны в смысле индексации, но доступны по URL —
годится для обзора публичных постов, НЕ для приватных планов.

Запуск: python3 telegraph.py вход.md [--title "Заголовок"]   → печатает URL
        python3 telegraph.py --create-account                 → печатает токен
"""
from __future__ import annotations

import argparse
import json
import os
import re
import sys
import urllib.parse
import urllib.request

API = "https://api.telegra.ph"
# Telegraph разрешает только эти теги; h1/h2 нет — максимум h3/h4.
_HEADING = {1: "h3", 2: "h3", 3: "h4", 4: "h4"}


def _api(method: str, params: dict) -> dict:
    data = urllib.parse.urlencode(params).encode()
    req = urllib.request.Request(f"{API}/{method}", data=data)
    with urllib.request.urlopen(req, timeout=30) as resp:
        out = json.loads(resp.read().decode("utf-8"))
    if not out.get("ok"):
        raise RuntimeError(f"telegraph {method}: {out.get('error')}")
    return out["result"]


def create_account() -> str:
    res = _api("createAccount", {
        "short_name": "natasha-watch",
        "author_name": "Трендвотчинг",
    })
    return res["access_token"]


def _inline(text: str) -> list:
    """Строка markdown → список Telegraph-нод (строки и {tag:a/b})."""
    text = re.sub(r"\[\[([^\]]+)\]\]", r"\1", text)  # вики-ссылки → просто текст
    pattern = re.compile(
        r"\[(?P<lt>[^\]]+)\]\((?P<lu>https?://[^)\s]+)\)"   # [текст](url)
        r"|(?P<url>https?://[^\s<>«»\"']+)"                  # голый url
        r"|\*\*(?P<b>[^*]+)\*\*"                             # **болд**
    )
    nodes: list = []
    pos = 0
    for m in pattern.finditer(text):
        if m.start() > pos:
            nodes.append(text[pos:m.start()])
        if m.group("lt") is not None:
            nodes.append({"tag": "a", "attrs": {"href": m.group("lu")},
                          "children": [m.group("lt")]})
        elif m.group("url") is not None:
            nodes.append({"tag": "a", "attrs": {"href": m.group("url")},
                          "children": [m.group("url")]})
        else:
            nodes.append({"tag": "b", "children": [m.group("b")]})
        pos = m.end()
    if pos < len(text):
        nodes.append(text[pos:])
    return nodes or [text]


def _strip_frontmatter(md: str) -> str:
    """Убрать ведущий YAML-блок (---\\n...\\n---), если он есть."""
    if md.startswith("---"):
        m = re.match(r"^---\s*\n.*?\n---\s*\n", md, re.DOTALL)
        if m:
            return md[m.end():]
    return md


def md_to_nodes(md: str) -> tuple[list, str]:
    """markdown → (Telegraph content nodes, заголовок страницы из первого # )."""
    md = _strip_frontmatter(md)
    title = "Дайджест"
    nodes: list = []
    cur_list: dict | None = None

    def flush_list() -> None:
        nonlocal cur_list
        if cur_list:
            nodes.append(cur_list)
            cur_list = None

    first_h1_taken = False
    for raw in md.splitlines():
        line = raw.rstrip()
        if not line.strip():
            flush_list()
            continue
        if re.fullmatch(r"-{3,}", line.strip()):
            flush_list()
            nodes.append({"tag": "hr"})
            continue
        m = re.match(r"^(#{1,4})\s+(.*)", line)
        if m:
            flush_list()
            level = len(m.group(1))
            content = m.group(2).strip()
            if level == 1 and not first_h1_taken:
                title = content        # первый # — заголовок страницы, в тело не дублируем
                first_h1_taken = True
                continue
            nodes.append({"tag": _HEADING[level], "children": _inline(content)})
            continue
        m = re.match(r"^\s*[-*]\s+(.*)", line)
        if m:
            if cur_list is None:
                cur_list = {"tag": "ul", "children": []}
            cur_list["children"].append({"tag": "li", "children": _inline(m.group(1))})
            continue
        # таблицы Telegraph не умеет — строку-разделитель пропускаем, ячейки склеиваем
        if line.lstrip().startswith("|"):
            cells = [c.strip() for c in line.strip().strip("|").split("|")]
            if all(re.fullmatch(r":?-{2,}:?", c) for c in cells):
                continue
            flush_list()
            nodes.append({"tag": "p", "children": _inline(" · ".join(cells))})
            continue
        flush_list()
        nodes.append({"tag": "p", "children": _inline(line)})

    flush_list()
    return nodes, title


def publish(md: str, token: str, title_override: str | None = None) -> str:
    nodes, title = md_to_nodes(md)
    res = _api("createPage", {
        "access_token": token,
        "title": (title_override or title)[:256],
        "author_name": "Трендвотчинг",
        "content": json.dumps(nodes, ensure_ascii=False),
        "return_content": "false",
    })
    return res["url"]


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("path", nargs="?", help="markdown-файл")
    ap.add_argument("--title", default=None)
    ap.add_argument("--create-account", action="store_true")
    args = ap.parse_args()

    if args.create_account:
        print(create_account())
        return
    if not args.path:
        sys.exit("нужен путь к markdown-файлу или --create-account")
    token = os.environ.get("TELEGRAPH_TOKEN")
    if not token:
        sys.exit("нет TELEGRAPH_TOKEN в окружении")
    md = open(args.path, encoding="utf-8").read()
    print(publish(md, token, args.title))


if __name__ == "__main__":
    main()
