# -*- coding: utf-8 -*-
r"""Extrai HP, EXP e respawn de cada monstro da wiki russa oficial do Karos.

    python tools/extrair_wiki_mobs.py  ->  server/Data/Monsters/MobWiki.ini

DE ONDE VEM. gamexp.com era o publicador russo do Karos e mantinha uma wiki com a base
oficial -- 720 monstros, cada um com pagina propria. O site esta fora do ar (devolve 503),
portanto le-se do arquivo da web (web.archive.org).

O QUE CADA PAGINA TRAZ, em linhas de tabela com rotulo:

    Уровень                 nivel
    Агрессия                agressivo (да/нет)
    Получаемый опыт         EXP que da
    Очки здоровья           HP
    Очки маны               MP
    Повторное возрождение   respawn, em segundos

ISTO NAO E MEDICAO NOSSA. E dado de terceiros, e entra no projecto com esse rotulo: a coluna
`fonte` diz `wiki`. Onde houver medicao no fio, a medicao ganha -- ja aconteceu antes de uma
fonte externa (os hooks do RaGEZONE) trazer numeros que nao batiam com o nosso binario.

A CONFERENCIA e o que decide se se pode confiar: temos 20 especies com HP medido e 14 com
EXP medida. Se os numeros da wiki baterem nesses, o resto ganha credito por associacao.

O NOME E EM RUSSO e o nosso catalogo esta em portugues, portanto o casamento nao e directo.
Este extractor so recolhe; casar os nomes e o passo seguinte, e faz-se pelo par (nivel, HP),
que e quase unico.
"""
import io
import os
import re
import sys
import time
import urllib.parse
import urllib.request

# O INSTANTANEO CONCRETO primeiro, e o curinga como reserva.
#
# `web/2026/` e um redireccionamento que o arquivo resolve para o instantaneo mais proximo,
# e para algumas paginas devolve 404 onde o carimbo exacto devolve 200. Ao contrario, ha
# paginas de monstro guardadas noutras datas, que o carimbo fixo nao apanha. Tentam-se os
# dois, por esta ordem.
CARIMBO = "http://web.archive.org/web/20260307153528/https://www.gamexp.com/ru/wiki/karos/"
CURINGA = "http://web.archive.org/web/2026/https://www.gamexp.com/ru/wiki/karos/"
LISTA = "Монстры:ОфициальнаяБаза"
ROTULOS = {
    "Получаемый опыт": "exp",
    "Очки здоровья": "hp",
    "Очки маны": "mp",
    "Повторное возрождение": "respawn",
    "Агрессия": "agressivo",
    "Уровень": "nivel",
}


def buscar(caminho, tentativas=2):
    """Tenta o carimbo fixo e depois o curinga. `caminho` e o que vem depois de /karos/."""
    ultimo = ""
    for base in (CARIMBO, CURINGA):
        for i in range(tentativas):
            try:
                req = urllib.request.Request(base + caminho,
                                             headers={"User-Agent": "Mozilla/5.0"})
                with urllib.request.urlopen(req, timeout=60) as r:
                    return r.read().decode("utf-8", "replace")
            except Exception as e:                                  # noqa: BLE001
                ultimo = str(e)[:60]
                if "404" in ultimo:
                    break        # nao ha nesta data; passa ao curinga
                time.sleep(2 + i * 3)
    return ""


def celulas(html):
    for row in re.findall(r"<tr[^>]*>(.*?)</tr>", html, re.S):
        c = [re.sub(r"\s+", " ", re.sub(r"<[^>]+>", "", x)).strip()
             for x in re.findall(r"<t[dh][^>]*>(.*?)</t[dh]>", row, re.S)]
        if c:
            yield c


def main() -> int:
    nomes = []
    de = 0
    while True:
        # A PRIMEIRA PAGINA E SEM QUERY. O arquivo nao guardou `?from=0&limit=50` (da 404);
        # guardou a pagina limpa e depois as de from=50 em diante.
        q = "" if de == 0 else "?from=%d&limit=50" % de
        h = buscar(urllib.parse.quote(LISTA) + q)
        if not h:
            break
        achou = 0
        for l in re.findall(r'href="([^"]+)"', h):
            d = urllib.parse.unquote(l)
            m = re.search(r"/wiki/karos/([^\"?]+):ОфициальнаяБаза$", d)
            if m and m.group(1) not in nomes:
                nomes.append(m.group(1))
                achou += 1
        t = re.sub(r"<[^>]+>", " ", h)
        mm = re.search(r"с (\d+) по (\d+) из (\d+)", t)
        print("pagina de %d: +%d nomes (total %d)%s"
              % (de, achou, len(nomes), ("  [%s]" % mm.group(0)) if mm else ""))
        if not achou:
            break
        de += 50
        if mm and int(mm.group(2)) >= int(mm.group(3)):
            break
        if de > 1200:
            break

    print("monstros a visitar: %d" % len(nomes))
    linhas = []
    for i, n in enumerate(nomes):
        h = buscar(urllib.parse.quote(n + ":ОфициальнаяБаза"))
        if not h:
            continue
        d = {}
        for c in celulas(h):
            rot = c[0].rstrip(":").strip()
            if rot in ROTULOS and len(c) > 1 and c[1]:
                d[ROTULOS[rot]] = c[1]
        nome = n.replace("_", " ")
        lv = re.search(r"\(ур\. (\d+)\)", nome)
        d.setdefault("nivel", lv.group(1) if lv else "")
        if d.get("hp") or d.get("exp"):
            linhas.append((re.sub(r"\s*\(ур\. \d+\)", "", nome), d))
        if (i + 1) % 25 == 0:
            print("  %d/%d  (%d com dados)" % (i + 1, len(nomes), len(linhas)))

    destino = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                           "..", "server", "Data", "Monsters", "MobWiki.ini")
    nl, tb = chr(10), chr(9)
    with io.open(destino, "w", encoding="utf-8", newline="") as f:
        f.write("; MobWiki -- HP, EXP e respawn da wiki russa oficial (gamexp.com)." + nl)
        f.write("; Gerado por tools/extrair_wiki_mobs.py. NAO e medicao nossa: ver o" + nl)
        f.write("; cabecalho dessa ferramenta. Onde houver medicao no fio, ela ganha." + nl)
        f.write(tb.join(["//nome_ru", "nivel", "hp", "exp", "mp",
                         "respawn", "agressivo"]) + nl)
        for nome, d in linhas:
            f.write(tb.join([nome, d.get("nivel", ""),
                             re.sub(r"[^0-9]", "", d.get("hp", "")),
                             re.sub(r"[^0-9]", "", d.get("exp", "")),
                             re.sub(r"[^0-9]", "", d.get("mp", "")),
                             re.sub(r"[^0-9]", "", d.get("respawn", "")),
                             d.get("agressivo", "")]) + nl)
    print("escritos %d monstros em %s" % (len(linhas), os.path.normpath(destino)))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
