from __future__ import annotations

import argparse
import json
import time
from typing import Any

import requests


EVENT_META_INFO_URL = "https://d.minieye.tech/ess-api/dashoard/event/meta_info"
CLIP_ID_KEYS = ("clip_id", "clip_uuid", "clip_ukey", "ukey", "id")


def fetch_event_meta(
    event_id: str,
    timeout: float = 60.0,
    max_retries: int = 3,
    retry_backoff_sec: float = 2.0,
) -> dict[str, Any]:
    event_id = str(event_id).strip()
    if not event_id:
        raise ValueError("event_id is required")

    last_error: Exception | None = None
    total_attempts = max(1, int(max_retries))
    for attempt in range(1, total_attempts + 1):
        try:
            response = requests.post(
                url=EVENT_META_INFO_URL,
                json={"event_id": event_id},
                timeout=timeout,
            )
            response.raise_for_status()
            payload = response.json()
            if not isinstance(payload, dict):
                raise ValueError(f"Unexpected event meta response type: {type(payload).__name__}")
            return payload
        except (requests.exceptions.Timeout, requests.exceptions.RequestException, ValueError) as exc:
            last_error = exc
            if attempt >= total_attempts:
                break
            sleep_sec = max(0.0, float(retry_backoff_sec)) * (2 ** (attempt - 1))
            print(
                f"[warn] event_id={event_id} request attempt {attempt}/{total_attempts} failed: "
                f"{type(exc).__name__}: {exc}. retry in {sleep_sec:.1f}s"
            )
            if sleep_sec > 0:
                time.sleep(sleep_sec)

    assert last_error is not None
    raise last_error


def _append_clip_id(clip_ids: list[str], seen: set[str], value: Any) -> None:
    clip_id = str(value).strip()
    if clip_id and clip_id not in seen:
        seen.add(clip_id)
        clip_ids.append(clip_id)


def _extract_clip_ids_from_item(item: Any, clip_ids: list[str], seen: set[str]) -> None:
    if item is None:
        return
    if isinstance(item, str):
        _append_clip_id(clip_ids, seen, item)
        return
    if isinstance(item, (list, tuple, set)):
        for sub_item in item:
            _extract_clip_ids_from_item(sub_item, clip_ids, seen)
        return
    if isinstance(item, dict):
        matched_key = False
        for key in CLIP_ID_KEYS:
            value = item.get(key)
            if value is not None:
                matched_key = True
                _extract_clip_ids_from_item(value, clip_ids, seen)
        if matched_key:
            return
        for value in item.values():
            _extract_clip_ids_from_item(value, clip_ids, seen)
        return

    _append_clip_id(clip_ids, seen, item)


def extract_associated_clip_ids(payload: dict[str, Any]) -> list[str]:
    clip_items = payload.get("associated_clip_list", [])
    if clip_items is None:
        return []
    clip_ids: list[str] = []
    seen: set[str] = set()
    _extract_clip_ids_from_item(clip_items, clip_ids, seen)
    return clip_ids


def get_associated_clip_ids(
    event_id: str,
    timeout: float = 60.0,
    max_retries: int = 3,
    retry_backoff_sec: float = 2.0,
) -> list[str]:
    payload = fetch_event_meta(
        event_id,
        timeout=timeout,
        max_retries=max_retries,
        retry_backoff_sec=retry_backoff_sec,
    )
    return extract_associated_clip_ids(payload)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Resolve PDCL clip ids from one or more event ids.")
    parser.add_argument("event_ids", nargs="+", help="One or more event ids to resolve.")
    parser.add_argument("--timeout", type=float, default=60.0, help="HTTP request timeout in seconds.")
    parser.add_argument("--retries", type=int, default=3, help="Max request attempts per event id.")
    parser.add_argument("--retry-backoff-sec", type=float, default=2.0, help="Base retry backoff in seconds.")
    parser.add_argument("--pretty", action="store_true", help="Pretty-print the JSON result.")
    return parser.parse_args()


def main() -> None:
    args = parse_args()
    result = {
        event_id: get_associated_clip_ids(
            event_id,
            timeout=args.timeout,
            max_retries=args.retries,
            retry_backoff_sec=args.retry_backoff_sec,
        )
        for event_id in args.event_ids
    }
    if args.pretty:
        print(json.dumps(result, indent=2, ensure_ascii=False))
    else:
        print(json.dumps(result, ensure_ascii=False))


if __name__ == "__main__":
    main()
