#!/usr/bin/env python3
"""
Telegraph Doujinshi Uploader
Автоматически создаёт статью в Telegraph из папки с изображениями.

Зависимости:
    pip install requests pillow

Режимы хостинга изображений (в порядке приоритета при указании):
  1. --nginx-base-url  Твой собственный сервер (самый надёжный)
  2. --imgbb-key       ImgBB (бесплатный, https://api.imgbb.com/)
  3. (по умолчанию)   telegra.ph/upload (нестабильно)

Использование:
  python telega.py --folder fmg1 --title "FMG 1" --nginx-base-url http://doujinshi.tlinmo.ru:8083
  python telega.py --folder fmg1 --title "FMG 1" --imgbb-key ВАШ_КЛЮЧ
  python telega.py --folder fmg1 --title "FMG 1"
"""

import re, sys, json, time, base64, argparse, io
from pathlib import Path
from urllib.parse import urljoin, quote
import requests
from PIL import Image

# ──────────────────────────────────────────────
TELEGRAPH_API    = "https://api.telegra.ph"
TELEGRAPH_UPLOAD = "https://telegra.ph/upload"
IMGBB_UPLOAD     = "https://api.imgbb.com/1/upload"

SUPPORTED_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff", ".tif"}
MAX_SIDE     = 3000
JPEG_QUALITY = 88

BROWSER_HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/124.0.0.0 Safari/537.36"
    ),
    "Accept":           "application/json, text/javascript, */*; q=0.01",
    "Accept-Language":  "ru-RU,ru;q=0.9,en-US;q=0.8",
    "Origin":           "https://telegra.ph",
    "Referer":          "https://telegra.ph/",
    "X-Requested-With": "XMLHttpRequest",
}


# ──────────────────────────────────────────────
# ИЗОБРАЖЕНИЯ
# ──────────────────────────────────────────────

def natural_sort_key(path):
    parts = re.split(r'(\d+)', path.name)
    return [int(p) if p.isdigit() else p.lower() for p in parts]


def prepare_image(path):
    """Открывает файл, конвертирует в JPEG RGB, возвращает байты."""
    img = Image.open(path)
    if img.mode in ("RGBA", "PA"):
        bg = Image.new("RGB", img.size, (255, 255, 255))
        bg.paste(img, mask=img.split()[-1])
        img = bg
    elif img.mode == "P":
        img = img.convert("RGBA")
        bg = Image.new("RGB", img.size, (255, 255, 255))
        bg.paste(img, mask=img.split()[-1])
        img = bg
    elif img.mode != "RGB":
        img = img.convert("RGB")
    if MAX_SIDE and max(img.size) > MAX_SIDE:
        img.thumbnail((MAX_SIDE, MAX_SIDE), Image.LANCZOS)
    buf = io.BytesIO()
    img.save(buf, format="JPEG", quality=JPEG_QUALITY, optimize=True)
    return buf.getvalue()


def collect_images(folder):
    files = [f for f in folder.iterdir() if f.is_file() and f.suffix.lower() in SUPPORTED_EXTS]
    if not files:
        raise FileNotFoundError(f"В папке {folder} не найдено изображений.")
    files.sort(key=natural_sort_key)
    return files


# ──────────────────────────────────────────────
# ХОСТИНГИ ИЗОБРАЖЕНИЙ
# ──────────────────────────────────────────────

def url_via_nginx(path, folder_name, nginx_base_url):
    """
    Возвращает готовый URL файла на nginx-сервере.
    Никакой загрузки не происходит — файлы уже лежат на сервере.
    URL строится как: <base>/<folder_name>/<filename>
    Пример: http://doujinshi.tlinmo.ru:8083/fmg1/1.webp
    """
    base = nginx_base_url.rstrip("/")
    # Кодируем имя файла на случай пробелов или кириллицы
    filename_encoded = quote(path.name, safe="")
    url = f"{base}/{quote(folder_name, safe='')}/{filename_encoded}"
    # Проверяем доступность (HEAD-запрос, быстро)
    try:
        r = requests.head(url, timeout=8)
        if r.status_code >= 400:
            raise RuntimeError(f"Сервер вернул {r.status_code} для {url}")
    except requests.exceptions.ConnectionError as e:
        raise RuntimeError(f"Не удалось подключиться к {url}: {e}")
    return url


def upload_via_telegraph(image_bytes, filename):
    session = requests.Session()
    try:
        session.get("https://telegra.ph/", headers={"User-Agent": BROWSER_HEADERS["User-Agent"]}, timeout=10)
    except Exception:
        pass
    resp = session.post(
        TELEGRAPH_UPLOAD,
        headers=BROWSER_HEADERS,
        files={"file": (filename, image_bytes, "image/jpeg")},
        timeout=60,
    )
    if resp.status_code != 200:
        raise RuntimeError(f"HTTP {resp.status_code}: {resp.text[:120]}")
    data = resp.json()
    if isinstance(data, list) and data and "src" in data[0]:
        return "https://telegra.ph" + data[0]["src"]
    raise RuntimeError(f"Unexpected response: {data}")


def upload_via_imgbb(image_bytes, api_key):
    b64 = base64.b64encode(image_bytes).decode()
    resp = requests.post(
        IMGBB_UPLOAD,
        params={"key": api_key},
        data={"image": b64},
        timeout=60,
    )
    if resp.status_code != 200:
        raise RuntimeError(f"ImgBB HTTP {resp.status_code}: {resp.text[:200]}")
    data = resp.json()
    if data.get("success"):
        return data["data"]["url"]
    raise RuntimeError(f"ImgBB error: {data}")


def get_image_url(path, folder_name, nginx_base_url, imgbb_key):
    """
    Получает URL изображения согласно выбранному режиму.
    Приоритет: nginx > imgbb > telegraph.
    """
    if nginx_base_url:
        # Nginx: файлы уже на сервере, просто строим URL
        return url_via_nginx(path, folder_name, nginx_base_url)

    # Для ImgBB и Telegraph нужно читать и конвертировать файл
    image_bytes = prepare_image(path)
    filename = f"img_{path.stem}.jpg"

    if imgbb_key:
        return upload_via_imgbb(image_bytes, imgbb_key)

    # Fallback: Telegraph upload
    try:
        return upload_via_telegraph(image_bytes, filename)
    except Exception as e:
        raise RuntimeError(
            f"Telegraph upload failed: {e}\n"
            "  Используй --nginx-base-url или --imgbb-key."
        )


# ──────────────────────────────────────────────
# TELEGRAPH АККАУНТ И СТРАНИЦА
# ──────────────────────────────────────────────

def load_or_create_account(token_file, author_name):
    if token_file.exists():
        saved = json.loads(token_file.read_text(encoding="utf-8"))
        print(f"[*] Используется сохранённый аккаунт: {saved.get('short_name')}")
        return saved["access_token"]
    resp = requests.post(
        f"{TELEGRAPH_API}/createAccount",
        json={
            "short_name": (author_name[:32] if author_name else "DoujinBot"),
            "author_name": author_name or "",
        },
        timeout=20,
    )
    resp.raise_for_status()
    result = resp.json()
    if not result.get("ok"):
        raise RuntimeError(f"createAccount failed: {result}")
    account = result["result"]
    token_file.write_text(json.dumps(account, ensure_ascii=False, indent=2), encoding="utf-8")
    print(f"[+] Создан новый аккаунт Telegraph, токен -> {token_file}")
    return account["access_token"]


def create_page(access_token, title, author_name, content):
    resp = requests.post(
        f"{TELEGRAPH_API}/createPage",
        json={
            "access_token": access_token,
            "title": title,
            "author_name": author_name,
            "content": content,
            "return_content": False,
        },
        timeout=30,
    )
    resp.raise_for_status()
    result = resp.json()
    if not result.get("ok"):
        raise RuntimeError(f"createPage failed: {result}")
    return result["result"]["url"]


def build_content(image_urls):
    return [
        {"tag": "figure", "children": [{"tag": "img", "attrs": {"src": u}}]}
        for u in image_urls
    ]


# ──────────────────────────────────────────────
# ОСНОВНОЙ ЦИКЛ
# ──────────────────────────────────────────────

def process_all_images(files, folder_name, nginx_base_url, imgbb_key, delay):
    """
    Для nginx: просто строит URL без задержек (нет сетевых запросов на загрузку).
    Для imgbb/telegraph: загружает с паузами и повторами.
    """
    urls = []
    total = len(files)
    is_nginx = bool(nginx_base_url)
    consecutive_errors = 0

    for i, path in enumerate(files, 1):
        print(f"  [{i:>2}/{total}] {path.name} ... ", end="", flush=True)
        success = False
        max_attempts = 1 if is_nginx else 3

        for attempt in range(1, max_attempts + 1):
            try:
                url = get_image_url(path, folder_name, nginx_base_url, imgbb_key)
                urls.append(url)
                if is_nginx:
                    print(f"OK  {url}")
                else:
                    print("OK")
                consecutive_errors = 0
                success = True
                break
            except Exception as e:
                if attempt < max_attempts:
                    wait = attempt * 3
                    print(f"\n       Попытка {attempt} упала: {e}")
                    print(f"       Жду {wait} сек ... ", end="", flush=True)
                    time.sleep(wait)
                else:
                    print(f"\n       ПРОПУЩЕН: {e}")
                    consecutive_errors += 1

        if not success and consecutive_errors >= 5:
            print("\n[!] 5 файлов подряд упали -- прерываю.")
            break

        # Пауза между загрузками нужна только для внешних хостингов
        if not is_nginx and i < total:
            time.sleep(delay)

    return urls


# ──────────────────────────────────────────────
# ТОЧКА ВХОДА
# ──────────────────────────────────────────────

def main():
    parser = argparse.ArgumentParser(
        description="Создаёт статью в Telegraph из папки с изображениями.",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Режимы (используй один из трёх):

  1) Свой nginx-сервер (рекомендуется, мгновенно):
       python telega.py -f fmg1 -t "FMG 1" --nginx-base-url http://doujinshi.tlinmo.ru:8083
     Файлы уже лежат на сервере — скрипт только строит URL и проверяет доступность.
     Структура URL: <base>/<имя_папки>/<имя_файла>

  2) ImgBB (бесплатно, надёжно):
       python telega.py -f fmg1 -t "FMG 1" --imgbb-key ВАШ_КЛЮЧ
     Ключ: https://api.imgbb.com/

  3) Telegraph upload (по умолчанию, нестабильно):
       python telega.py -f fmg1 -t "FMG 1"
        """,
    )
    parser.add_argument("--folder",          "-f", required=True,  help="Папка с изображениями")
    parser.add_argument("--title",           "-t", required=True,  help="Заголовок статьи")
    parser.add_argument("--author",          "-a", default="",     help="Имя автора")
    parser.add_argument("--nginx-base-url",        default=None,
                        help="Базовый URL nginx-сервера, напр. http://doujinshi.tlinmo.ru:8083")
    parser.add_argument("--imgbb-key",             default=None,   help="API ключ ImgBB")
    parser.add_argument("--token-file",            default=None,   help="JSON-файл с токеном Telegraph")
    parser.add_argument("--delay",           "-d", type=float, default=0.7,
                        help="Пауза между загрузками в сек (только для imgbb/telegraph, по умолчанию 0.7)")
    args = parser.parse_args()

    folder = Path(args.folder).expanduser().resolve()
    if not folder.is_dir():
        print(f"[!] Папка не найдена: {folder}")
        sys.exit(1)

    # Имя папки для построения nginx URL (последний компонент пути)
    folder_name = folder.name

    token_file = (
        Path(args.token_file) if args.token_file
        else Path(__file__).parent / "telegraph_token.json"
    )

    # Определяем режим для отображения
    if args.nginx_base_url:
        mode = f"Свой сервер  ({args.nginx_base_url}/{folder_name}/...)"
    elif args.imgbb_key:
        mode = "ImgBB"
    else:
        mode = "Telegraph upload (нестабильно)"

    print(f"\n{'='*58}")
    print(f"  Telegraph Doujinshi Uploader")
    print(f"{'='*58}")
    print(f"  Папка     : {folder}")
    print(f"  Заголовок : {args.title}")
    print(f"  Автор     : {args.author or '(не указан)'}")
    print(f"  Хостинг   : {mode}")
    print(f"{'='*58}\n")

    print("[1/4] Подготовка аккаунта Telegraph ...")
    access_token = load_or_create_account(token_file, args.author)

    print("\n[2/4] Сканирование папки ...")
    files = collect_images(folder)
    print(f"  Найдено: {len(files)} изображений")

    print("\n[3/4] Сборка URL изображений ...")
    image_urls = process_all_images(
        files, folder_name,
        args.nginx_base_url, args.imgbb_key, args.delay,
    )
    if not image_urls:
        print("\n[!] Не удалось получить URL ни одного изображения.")
        sys.exit(1)
    print(f"\n  Готово: {len(image_urls)} / {len(files)}")

    print("\n[4/4] Создание статьи в Telegraph ...")
    content = build_content(image_urls)
    page_url = create_page(access_token, args.title, args.author, content)

    print(f"\n{'='*58}")
    print(f"  Статья опубликована!")
    print(f"  {page_url}")
    print(f"{'='*58}\n")


if __name__ == "__main__":
    main()