"""Минимальный probe Objective API: 4 отчёта × минимальная выборка.

Цель — посмотреть реальную форму payload-ов, не выкачивая весь Екатеринбург.
Ограничения объёма:
  - period: только последние 7 дней (для Сводных)
  - complex_name: фильтр по 1 ЖК (по умолчанию «ЖК "Парк Культуры"»; меняется флагом)

Что выводит:
  - HTTP-статус через клиент (raw exception если что)
  - размер JSON в KB
  - тип (dict/list) + len/keys
  - первые 1-2 элемента (для list) или верхние ключи (для dict) — обрезанные до 1KB
  - сохраняет полный payload в data/raw/objective_test/<timestamp>/<slug>.json

Запуск:
  cd backend
  OBJECTIVE_API_KEY=<key> uv run python -X utf8 ../data/sql/69b_objective_minimal.py
  OBJECTIVE_API_KEY=<key> uv run python -X utf8 ../data/sql/69b_objective_minimal.py --complex 'ЖК "Сибирский сад"'
  OBJECTIVE_API_KEY=<key> uv run python -X utf8 ../data/sql/69b_objective_minimal.py --no-complex --days 3

Notes:
  - Если поле ComplexName в Objective отличается ('ЖК "Парк Культуры"' vs 'Парк Культуры'
    vs 'ЖК Парк Культуры') — поменяй --complex или поставь --no-complex.
  - --no-complex берёт ВСЕ ЖК группы, но всё ещё с период=N дней (для Сводных).
    Для Поквартирных без --complex может быть очень большой ответ — будь осторожен.
"""

from __future__ import annotations

import argparse
import json
import os
import sys
import time
from datetime import date, datetime, timedelta, timezone
from pathlib import Path

# repo-root → /backend для import'а app.*
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "backend"))

from app.services.scrapers.objective import (  # noqa: E402
    ObjectiveAPIError,
    ObjectiveAuthError,
    ObjectiveClient,
)


def _shape(data) -> str:
    if isinstance(data, list):
        return f"list[{len(data)}]"
    if isinstance(data, dict):
        return f"dict(keys={list(data.keys())[:15]})"
    return type(data).__name__


def _peek(data, max_chars: int = 1200) -> str:
    """Короткий человеко-читаемый peek первых элементов / ключей."""
    if isinstance(data, list):
        if not data:
            return "  (пустой список)"
        sample = data[0]
        if isinstance(sample, dict):
            keys = list(sample.keys())
            return f"  [0] keys ({len(keys)}): {keys[:30]}"
        return f"  [0]: {str(sample)[:max_chars]}"
    if isinstance(data, dict):
        out = []
        for k, v in list(data.items())[:10]:
            v_repr = (
                f"list[{len(v)}]" if isinstance(v, list)
                else f"dict({len(v)} keys)" if isinstance(v, dict)
                else str(v)[:120]
            )
            out.append(f"  {k!r} -> {v_repr}")
        return "\n".join(out)
    s = str(data)
    return f"  {s[:max_chars]}{'…' if len(s) > max_chars else ''}"


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--complex", default='Парк Культуры',
                    help='ComplexName-фильтр (default: "Парк Культуры", без префикса ЖК и без кавычек — '
                         'эмпирически проверено что Объектив требует именно такой формат)')
    ap.add_argument("--no-complex", action="store_true",
                    help="Не передавать ComplexName (вся группа)")
    ap.add_argument("--group", default=None,
                    help="GroupName (default: settings.objective_default_group = Екатеринбург)")
    ap.add_argument("--days", type=int, default=7,
                    help="Период для Сводных отчётов в днях (default: 7)")
    ap.add_argument("--only", choices=("corp_sum", "lots_sum", "corp_pf", "lots_pf"),
                    help="Запустить только один отчёт")
    ap.add_argument("--inter-delay", type=int, default=15,
                    help="Пауза в секундах между отчётами (default: 15). "
                         "У Objective жёсткий rate-limit: <1 запрос/30с на дешёвых тарифах.")
    ap.add_argument("--rate-ms", type=int, default=3000,
                    help="Минимальный интервал между HTTP-запросами в мс (default: 3000)")
    ap.add_argument("--retries", type=int, default=2,
                    help="Сколько раз ретраить 429/5xx (default: 2). 0 = не ретраить вообще.")
    args = ap.parse_args()

    if not os.environ.get("OBJECTIVE_API_KEY"):
        # Конфиг также читает из backend/.env — попробуем через settings.
        from app.core.config import settings
        if not settings.objective_api_key:
            print("ERROR: OBJECTIVE_API_KEY не задан (env + backend/.env пустые).")
            return 1

    end_date = date.today()
    start_date = end_date - timedelta(days=args.days)
    complex_name = None if args.no_complex else args.complex
    # Всегда пишем в <repo>/data/raw/objective_test/ независимо от cwd скрипта
    # (раньше был относительный путь — попадал в backend/data/raw/ когда юзер
    # запускал из backend/, и эти файлы превышали 512KB pre-commit лимит).
    repo_root = Path(__file__).resolve().parent.parent.parent
    out_dir = repo_root / "data" / "raw" / "objective_test" / datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
    out_dir.mkdir(parents=True, exist_ok=True)

    print(f"=== Objective minimal probe")
    print(f"    group   = {args.group or '(default settings)'}")
    print(f"    complex = {complex_name or '(none — вся группа)'}")
    print(f"    period  = {start_date} .. {end_date}  ({args.days}d)")
    print(f"    output  = {out_dir.absolute()}")
    print()

    try:
        client = ObjectiveClient(rate_ms=args.rate_ms, retries=args.retries)
    except ObjectiveAuthError as e:
        print(f"AUTH ERROR при создании клиента: {e}")
        return 2

    # 4 канонических отчёта × минимальные параметры
    plan = [
        ("corp_sum", "1. Сводные / Корпуса",
         lambda: client.report_corpuses_summary(
             group_name=args.group, complex_name=complex_name,
             start_date=start_date, end_date=end_date,
             use_ddu=True, use_dkp=True,
         )),
        ("lots_sum", "2. Сводные / Лоты",
         lambda: client.report_lots_summary(
             group_name=args.group, complex_name=complex_name,
             start_date=start_date, end_date=end_date,
             use_ddu=True, use_dkp=True,
         )),
        ("corp_pf", "3. Поквартирные / Корпуса",
         lambda: client.report_corpuses_per_flat(
             group_name=args.group, complex_name=complex_name,
             use_ddu=True, use_dkp=True,
         )),
        ("lots_pf", "4. Поквартирные / Лоты",
         lambda: client.report_lots_per_flat(
             group_name=args.group, complex_name=complex_name,
             use_ddu=True, use_dkp=True,
         )),
    ]
    if args.only:
        plan = [p for p in plan if p[0] == args.only]

    summary = []
    for idx, (slug, label, fn) in enumerate(plan):
        if idx > 0 and args.inter_delay > 0:
            print(f"... пауза {args.inter_delay}s перед следующим отчётом (rate-limit Objective)")
            time.sleep(args.inter_delay)
        print(f"--- {label}")
        t0 = time.time()
        try:
            data = fn()
        except ObjectiveAuthError as e:
            print(f"  ❌ AUTH: {e}\n")
            summary.append((slug, "auth_error", 0))
            continue
        except ObjectiveAPIError as e:
            print(f"  ❌ API: {e}\n")
            summary.append((slug, "api_error", 0))
            continue
        except Exception as e:  # noqa: BLE001
            print(f"  ❌ {type(e).__name__}: {e}\n")
            summary.append((slug, type(e).__name__, 0))
            continue
        elapsed = time.time() - t0

        text = json.dumps(data, ensure_ascii=False, indent=2)
        size_kb = len(text) / 1024.0
        path = out_dir / f"{slug}.json"
        path.write_text(text, encoding="utf-8")

        print(f"  ✓ {elapsed:.2f}s  size={size_kb:.1f} KB  shape={_shape(data)}")
        print(_peek(data))
        print(f"  saved → {path.relative_to(Path.cwd()) if path.is_relative_to(Path.cwd()) else path}")
        print()
        summary.append((slug, "ok", size_kb))

    client.close()

    print("=== Сводка")
    for slug, status, size in summary:
        print(f"  {slug:10} {status:12} {size:.1f} KB")
    print(f"\nDONE. Файлы в: {out_dir.absolute()}")
    return 0


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