#!/usr/bin/env python3
"""Расход ключа шлюза AILab SCRUME (gw.ailabscrume.ru) — компактный JSON в stdout.

Вызывается плагином ailab-limit через shell.exec. Ключ берётся из .env профилей
и в рендерер не попадает. Один запрос: GET /key/info с пользовательским ключом
(master не нужен) — шлюз отдаёт spend, max_budget и budget_duration в рублях.

Имя переменной НЕ угадывается: скрипт читает `key_env` из config.yaml (активного
профиля, затем корневого) — у разных установок ключ лежит под разными именами
(AILAB, POLZA и т.п.), и жёсткий список молча давал «ключ не найден».

Формат ответа:
  {"ok":true,"spend":5.21,"budget":100.0,"duration":"1d","pct":5.2,"alias":"testoff"}
  {"ok":false,"err":"..."}
"""

import json
import os
import re
import sys
import time
import urllib.error
import urllib.request

BASE = "https://gw.ailabscrume.ru"

# Запасной список: если config.yaml не найден или в нём нет key_env.
KEY_NAMES = (
    "HERMES_CUSTOM_AILAB_API_KEY",
    "CUSTOM_PROVIDER_AILAB_KEY",
    "HERMES_CUSTOM_POLZA_AI_API_KEY",
    "CUSTOM_PROVIDER_POLZA_AI_KEY",
    "AILAB_API_KEY",
)

# shell.exec рвёт вызов на 30 с, поэтому 3 попытки должны укладываться в этот
# бюджет с запасом: 3 x 7 с + паузы = 24 с.
TIMEOUT = 7.0
ATTEMPTS = 3
PAUSE = 1.5


def root_dir():
    return os.path.expandvars(r"%LOCALAPPDATA%\hermes")


def active_profile():
    path = os.path.expandvars(r"%APPDATA%\hermes\active-profile.json")
    if not os.path.exists(path):
        return None
    try:
        with open(path, encoding="utf-8") as f:
            data = json.load(f)
        return data.get("profile") or data.get("active")
    except Exception:
        return None


def profile_dirs():
    """Каталоги профилей: активный первым, затем остальные по алфавиту."""
    root = root_dir()
    out = []

    name = active_profile()
    if name:
        out.append(os.path.join(root, "profiles", name))

    out.append(root)

    prof_dir = os.path.join(root, "profiles")
    if os.path.isdir(prof_dir):
        for entry in sorted(os.listdir(prof_dir)):
            out.append(os.path.join(prof_dir, entry))

    seen, uniq = set(), []
    for d in out:
        if d not in seen:
            seen.add(d)
            uniq.append(d)
    return uniq


def config_key_names():
    """Имена переменных из key_env в config.yaml — любые, а не жёсткий список."""
    names = []
    for d in profile_dirs():
        cfg = os.path.join(d, "config.yaml")
        try:
            with open(cfg, encoding="utf-8", errors="ignore") as f:
                text = f.read()
        except OSError:
            continue
        for m in re.finditer(r"^\s*key_env\s*:\s*[\"\']?([A-Za-z0-9_]+)", text, re.M):
            name = m.group(1)
            if name not in names:
                names.append(name)
    return names


def env_candidates():
    """Пути к .env в том же порядке, что и профили."""
    return [os.path.join(d, ".env") for d in profile_dirs()]


def read_key(path, names):
    """Первая подходящая переменная из names в файле .env."""
    try:
        with open(path, encoding="utf-8", errors="ignore") as f:
            lines = f.readlines()
    except OSError:
        return None, None

    wanted = set(names)
    found = {}
    for line in lines:
        s = line.strip()
        if not s or s.startswith("#") or "=" not in s:
            continue
        name, _, value = s.partition("=")
        name = name.strip()
        if name in wanted:
            value = value.strip().strip('"').strip("'")
            if value.startswith("sk-"):
                found[name] = value

    for name in names:
        if name in found:
            return name, found[name]
    return None, None


def fetch(key):
    """GET /key/info с повторами только на сетевых сбоях.

    401/403 — реальный отказ ключа, повторять бессмысленно: отдаём сразу.
    """
    last = None
    for attempt in range(ATTEMPTS):
        try:
            req = urllib.request.Request(
                BASE + "/key/info", headers={"Authorization": "Bearer " + key}
            )
            with urllib.request.urlopen(req, timeout=TIMEOUT) as r:
                data = json.loads(r.read())
            return data.get("info", data)
        except urllib.error.HTTPError as e:
            if e.code in (401, 403):
                raise
            last = e
        except Exception as e:
            last = e
        if attempt + 1 < ATTEMPTS:
            time.sleep(PAUSE)
    raise last


def main():
    names = config_key_names()
    for name in KEY_NAMES:
        if name not in names:
            names.append(name)

    for path in env_candidates():
        name, key = read_key(path, names)
        if not key:
            continue
        try:
            info = fetch(key)
        except urllib.error.HTTPError as e:
            print(json.dumps({"ok": False, "err": "шлюз вернул %s" % e.code}, ensure_ascii=False))
            return
        except Exception as e:
            print(json.dumps({"ok": False, "err": str(e)[:120]}, ensure_ascii=False))
            return

        spend = float(info.get("spend") or 0)
        budget = info.get("max_budget")
        budget = float(budget) if budget is not None else None
        pct = round(spend / budget * 100, 1) if budget else None

        print(json.dumps({
            "ok": True,
            "spend": round(spend, 4),
            "budget": round(budget, 2) if budget else None,
            "duration": info.get("budget_duration") or "",
            "pct": pct,
            "alias": info.get("key_alias") or "",
        }, ensure_ascii=False))
        return

    print(json.dumps({"ok": False, "err": "ключ шлюза не найден в .env"}, ensure_ascii=False))


if __name__ == "__main__":
    sys.exit(main())
