"""НОРМОСКАН: загрузка → ожидание → JSON и PDF. Требуется: pip install httpx.

Ключ: переменная окружения NK_KEY. Примеры:
  python check.py АР.pdf ЭОМ.pdf --idempotency-key object-001-v1 --request-file task.txt
  python check.py --run-id ИДЕНТИФИКАТОР_ПРИНЯТОЙ_ПРОВЕРКИ
"""
from __future__ import annotations

import argparse
from contextlib import ExitStack
import json
import os
from pathlib import Path
import re
import time

import httpx

BASE = "https://xn--80atfddcbol.xn--p1ai/api/v1"


def checked(response: httpx.Response) -> httpx.Response:
    if response.is_success:
        return response
    try:
        detail = response.json().get("detail", response.json())
    except (ValueError, AttributeError):
        detail = response.text[:300]
    raise RuntimeError(f"HTTP {response.status_code}: {detail}")


def collect(client: httpx.Client, run_id: str, output: Path, wait_seconds: int = 14400) -> dict:
    deadline = time.monotonic() + wait_seconds
    delay = 5
    while True:
        try:
            response = client.get(f"/runs/{run_id}")
            if response.status_code == 429 or response.status_code >= 500:
                raise httpx.RequestError("Сервер временно недоступен")
            run = checked(response).json()
            delay = 5
        except httpx.RequestError:
            if time.monotonic() >= deadline:
                raise RuntimeError(f"Ожидание закончено. Проверка {run_id} продолжает жить на сервере; используйте --run-id.")
            time.sleep(delay)
            delay = min(delay * 2, 30)
            continue
        print(f"{run_id}: {run.get('stage_text') or run['status']}")
        if run["status"] == "failed":
            raise RuntimeError(f"{run.get('error')}. Возврат проверки: {run.get('refunded')}")
        if run["status"] == "done":
            break
        if time.monotonic() >= deadline:
            raise RuntimeError(f"Ожидание закончено. Продолжить получение результата: --run-id {run_id}")
        time.sleep(delay)
    report = checked(client.get(f"/runs/{run_id}/findings")).json()
    (output / "findings.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
    markdown = checked(client.get(f"/runs/{run_id}/report")).text
    (output / "report.md").write_text(markdown, encoding="utf-8")
    # Уточнения из questions не прибавляются к числу замечаний.
    editions = [(None, "report.pdf")]
    for section in report.get("available_sections", []):
        code = section["code"]
        name = re.sub(r"[^\w-]", "_", code)
        editions.append((code, f"report-{name}.pdf"))
    for code, filename in editions:
        response = checked(client.get(f"/runs/{run_id}/report.pdf", params={"section": code} if code else None, timeout=120))
        if not response.content.startswith(b"%PDF"):
            raise RuntimeError("Вместо PDF получен другой ответ; JSON и Markdown уже сохранены.")
        (output / filename).write_bytes(response.content)
    return report


def main() -> None:
    parser = argparse.ArgumentParser(description="Отправить комплект в НОРМОСКАН и сохранить результат")
    parser.add_argument("files", nargs="*", type=Path, help="Файлы одного комплекта")
    parser.add_argument("--run-id", help="Получить уже принятую проверку без новой загрузки")
    parser.add_argument("--idempotency-key", help="Постоянная метка отправки; при повторе используйте ту же")
    parser.add_argument("--request-file", type=Path, help="Задание клиента в текстовом файле UTF-8, до 8000 символов")
    parser.add_argument("--address", default="", help="Адрес объекта, до 500 символов")
    parser.add_argument("--output", type=Path, default=Path("normoskan-result"), help="Папка результата")
    parser.add_argument("--wait-seconds", type=int, default=14400, help="Сколько ждать; этот срок не отменяет проверку")
    args = parser.parse_args()
    if args.run_id and args.files:
        parser.error("Передайте либо файлы, либо --run-id")
    if not args.run_id and (not args.files or not args.idempotency_key):
        parser.error("Для загрузки нужны файлы и --idempotency-key")
    key = os.environ.get("NK_KEY", "").strip()
    if not key:
        parser.error("Задайте ключ в переменной окружения NK_KEY")
    request = args.request_file.read_text(encoding="utf-8-sig").strip() if args.request_file else ""
    if len(request) > 8000 or len(args.address) > 500:
        parser.error("Задание — до 8000 символов, адрес — до 500")
    args.output.mkdir(parents=True, exist_ok=True)
    try:
        with httpx.Client(base_url=os.environ.get("NK_API_BASE", BASE).rstrip("/"),
                          headers={"Authorization": f"Bearer {key}"}, timeout=30) as client:
            checked(client.get("/me"))
            if args.run_id:
                run_id = args.run_id
            else:
                limits = checked(client.get("/limits")).json()
                print("Лимиты вашего подключения:", json.dumps(limits, ensure_ascii=False))
                if request and not checked(client.get("/features")).json().get("user_request"):
                    raise RuntimeError("Это подключение не подтвердило поддержку задания клиента")
                with ExitStack() as handles:
                    files = [("files", (path.name, handles.enter_context(path.open("rb")), "application/octet-stream"))
                             for path in args.files]
                    # httpx отправляет файлы потоком. POST не повторяется автоматически.
                    accepted = checked(client.post("/runs", files=files,
                        data={"user_request": request, "object_address": args.address},
                        headers={"Idempotency-Key": args.idempotency_key},
                        timeout=httpx.Timeout(600, connect=20))).json()
                (args.output / "accepted.json").write_text(json.dumps(accepted, ensure_ascii=False, indent=2), encoding="utf-8")
                run_id = accepted["run_id"]
                print("Комплект принят. Сохранён run_id:", run_id)
            collect(client, run_id, args.output, args.wait_seconds)
            print("Результат сохранён:", args.output.resolve())
    except (httpx.RequestError, RuntimeError, OSError) as error:
        raise SystemExit(f"{error}\nЕсли ответ на загрузку потерян, повторите те же файлы и параметры с тем же --idempotency-key.") from None


if __name__ == "__main__":
    main()
