import time
import math
import requests
from requests.exceptions import Timeout, ConnectionError, RequestException


class RadarioClient:
    def __init__(self, headers: dict, timeout: int = 30, max_retries: int = 3,
                 sleep_between_pages: float = 0.3, sleep_between_orders: float = 0.2):
        self.headers = headers
        self.timeout = timeout
        self.max_retries = max_retries
        self.sleep_between_pages = sleep_between_pages
        self.sleep_between_orders = sleep_between_orders

    def _get(self, url: str, params: dict | None = None):
        for attempt in range(self.max_retries):
            try:
                resp = requests.get(url, headers=self.headers, params=params, timeout=self.timeout)
                resp.raise_for_status()
                return resp
            except (Timeout, ConnectionError):
                if attempt == self.max_retries - 1:
                    raise
                wait = (2 ** attempt) + (time.time() % 1)
                time.sleep(wait)
            except RequestException:
                raise
        raise RuntimeError(f"Max retries exceeded for {url}")

    def get_event(self, event_id: int) -> dict:
        url = f"https://api.radario.ru/events/{event_id}"
        return self._get(url).json()

    def get_ticket_types(self, event_id: int, limit: int = 1000) -> dict:
        url = f"https://api.radario.ru/events/{event_id}/ticket_types"
        return self._get(url, params={"limit": limit}).json()

    def get_tickets_page(self, event_id: int, limit: int, offset: int) -> dict:
        url = f"https://api.radario.ru/events/{event_id}/tickets"
        return self._get(url, params={"limit": limit, "offset": offset}).json()

    def get_all_tickets(self, event_id: int, limit: int = 100) -> list[dict]:
        first = self.get_tickets_page(event_id, limit=limit, offset=0)
        if not first.get("success") or first.get("error"):
            raise RuntimeError(f"API error: {first.get('error', 'Unknown error')}")

        data = first["data"]
        total = data.get("totalCount", 0)
        items = list(data.get("items", []))

        total_pages = math.ceil(total / limit) if total else 1
        print(f"Fetched page 1/{total_pages} (offset 0) - {len(data.get('items', []))} items")

        for page in range(1, total_pages):
            offset = page * limit
            time.sleep(self.sleep_between_pages)
            chunk = self.get_tickets_page(event_id, limit=limit, offset=offset)

            if not chunk.get("success") or chunk.get("error"):
                print(f"Warning: Page at offset {offset} failed: {chunk.get('error')}")
                continue

            chunk_items = chunk["data"].get("items", [])
            items.extend(chunk_items)
            print(f"Fetched page {page+1}/{total_pages} (offset {offset}) - {len(chunk_items)} items")

        return items

    def get_order(self, order_id: int | str) -> dict:
        url = f"https://api.radario.ru/host/orders/{order_id}"
        return self._get(url).json()

    def fetch_order_details(self, order_ids: list, format_money, safe_str) -> tuple[dict, dict]:
        """
        Returns:
          order_info_map[order_id] = {phone, full_amount}
          ticket_type_map[ticket_id] = ticket_type_id
        """
        order_info_map: dict = {}
        ticket_type_map: dict = {}

        print(f"\nFetching details for {len(order_ids)} unique orders (timeout={self.timeout}s, retries={self.max_retries})...")

        for i, order_id in enumerate(order_ids, 1):
            try:
                data = self.get_order(order_id)
                if data.get("success") and data.get("data"):
                    order_data = data["data"].get("order", {})
                    order_info_map[order_id] = {
                        "phone": safe_str(order_data.get("orderUserPhone")),
                        "full_amount": format_money(order_data.get("amount")),
                    }
                    for ticket in data["data"].get("tickets", []):
                        tkt_id = ticket.get("id")
                        tkt_type_id = ticket.get("type", {}).get("id")
                        if tkt_id is not None and tkt_type_id is not None:
                            ticket_type_map[str(tkt_id)] = str(tkt_type_id)
                else:
                    order_info_map[order_id] = {"phone": "", "full_amount": "0.00"}

                if i % 10 == 0 or i == len(order_ids):
                    print(f"  Fetched {i}/{len(order_ids)} orders (collected {len(ticket_type_map)} ticket type IDs)...")

                time.sleep(self.sleep_between_orders)

            except Exception as e:
                print(f"  Failed order {order_id} after {self.max_retries} retries: {str(e)[:120]}")
                order_info_map[order_id] = {"phone": "", "full_amount": "0.00"}

        return order_info_map, ticket_type_map

