"""
Flightradar24 API Client (Explorer tier)
Документация: https://fr24api.flightradar24.com/docs
"""

import os
import json
import time
import logging
from pathlib import Path
from datetime import datetime, timedelta, timezone
from typing import Optional, Dict, Any, List

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

logger = logging.getLogger(__name__)


class FR24Client:
    """
    Клиент для работы с Flightradar24 API (Explorer tier)
    Поддерживает кэширование запросов для экономии кредитов
    """

    BASE_URL = "https://fr24api.flightradar24.com/api"

    # Московская область: bbox (min_lat, min_lon, max_lat, max_lon)
    MOSCOW_REGION_BOUNDS = {
        "lat_min": 54.0,
        "lat_max": 57.0,
        "lon_min": 35.5,
        "lon_max": 40.5,
    }

    def __init__(self, api_key: Optional[str] = None, cache_dir: str = "data/cache"):
        self.api_key = api_key or os.getenv("FLIGHTRADAR24_API_KEY")
        if not self.api_key:
            raise ValueError(
                "API ключ не найден. Установите FLIGHTRADAR24_API_KEY в .env"
            )

        self.cache_dir = Path(cache_dir)
        self.cache_dir.mkdir(parents=True, exist_ok=True)

        # Настройка сессии с retry
        self.session = requests.Session()
        retry = Retry(total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503])
        adapter = HTTPAdapter(max_retries=retry)
        self.session.mount("https://", adapter)

        self.session.headers.update({
            "Authorization": f"Bearer {self.api_key}",
            "Accept": "application/json",
            "Accept-Version": "v1",
        })

        logger.info(f"FR24Client инициализирован, кэш: {self.cache_dir}")

    def _cache_key(self, endpoint: str, params: dict) -> str:
        """Генерация ключа кэша"""
        import hashlib
        param_str = json.dumps(params, sort_keys=True)
        h = hashlib.md5(f"{endpoint}{param_str}".encode()).hexdigest()[:12]
        return h

    def _get_cache(self, key: str, ttl_hours: int = 24) -> Optional[dict]:
        """Получение данных из кэша"""
        cache_file = self.cache_dir / f"{key}.json"
        if cache_file.exists():
            age = time.time() - cache_file.stat().st_mtime
            if age < ttl_hours * 3600:
                with open(cache_file) as f:
                    logger.debug(f"Кэш-хит: {key}")
                    return json.load(f)
        return None

    def _set_cache(self, key: str, data: dict):
        """Сохранение данных в кэш"""
        cache_file = self.cache_dir / f"{key}.json"
        with open(cache_file, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=2)

    def _request(
        self, endpoint: str, params: dict = None, cache_ttl_hours: int = 6
    ) -> dict:
        """Выполнение запроса с кэшированием"""
        params = params or {}
        cache_key = self._cache_key(endpoint, params)

        cached = self._get_cache(cache_key, cache_ttl_hours)
        if cached is not None:
            return cached

        url = f"{self.BASE_URL}{endpoint}"
        logger.info(f"API запрос: {endpoint} {params}")

        resp = self.session.get(url, params=params, timeout=30)
        resp.raise_for_status()
        data = resp.json()

        self._set_cache(cache_key, data)
        return data

    # ──────────────────────────────────────────────
    # Основные методы
    # ──────────────────────────────────────────────

    def get_live_flights(
        self,
        bounds: Optional[str] = None,
        limit: int = 100,
    ) -> dict:
        """
        Live позиции самолётов в зоне Московской области
        bounds: "lat_min,lon_min,lat_max,lon_max"
        """
        b = self.MOSCOW_REGION_BOUNDS
        bounds = bounds or f"{b['lat_min']},{b['lon_min']},{b['lat_max']},{b['lon_max']}"

        return self._request(
            "/live/flight-positions/light",
            params={"bounds": bounds, "limit": limit},
            cache_ttl_hours=0,  # live данные не кэшируем
        )

    def get_flight_tracks(self, flight_id: str) -> list:
        """
        Трек полёта по ID рейса.
        Возвращает список точек трека (нормализованных).
        Структура ответа API: [{fr24_id, tracks:[{timestamp,lat,lon,alt,...}]}]
        """
        raw = self._request(
            "/flight-tracks",
            params={"flight_id": flight_id},
            cache_ttl_hours=24,
        )
        # Ответ — список объектов [{fr24_id, tracks:[...]}]
        if isinstance(raw, list) and raw:
            return raw[0].get("tracks", [])
        # Fallback если обёрнуто в data
        if isinstance(raw, dict):
            items = raw.get("data", raw.get("tracks", []))
            if items and isinstance(items[0], dict) and "tracks" in items[0]:
                return items[0]["tracks"]
            return items
        return []

    def get_historic_flights(
        self,
        timestamp: int,
        bounds: Optional[str] = None,
        limit: int = 200,
    ) -> dict:
        """
        Исторические позиции самолётов в указанный момент времени
        timestamp: Unix timestamp
        """
        b = self.MOSCOW_REGION_BOUNDS
        bounds = bounds or f"{b['lat_min']},{b['lon_min']},{b['lat_max']},{b['lon_max']}"

        return self._request(
            "/historic/flight-positions/light",
            params={"timestamp": timestamp, "bounds": bounds, "limit": limit},
            cache_ttl_hours=168,  # исторические данные: 7 дней
        )

    def get_flights_summary(
        self,
        date_from: str,
        date_to: str,
        airport: Optional[str] = None,
    ) -> dict:
        """
        Сводка рейсов за период
        date_from/date_to: "YYYY-MM-DD"
        """
        params = {"date_from": date_from, "date_to": date_to}
        if airport:
            params["airport"] = airport
        return self._request("/flights/summary", params=params, cache_ttl_hours=168)

    def get_usage(self) -> dict:
        """Отчёт об использовании кредитов"""
        return self._request("/usage", cache_ttl_hours=1)

    # ──────────────────────────────────────────────
    # Удобные методы для прототипа
    # ──────────────────────────────────────────────

    def fetch_daily_snapshots(
        self,
        date: str,
        interval_hours: int = 3,
    ) -> List[dict]:
        """
        Сбор снимков позиций самолётов за день с шагом interval_hours часов.
        Возвращает список нормализованных позиций.
        """
        dt = datetime.strptime(date, "%Y-%m-%d").replace(tzinfo=timezone.utc)
        positions = []

        for hour in range(0, 24, interval_hours):
            ts = int((dt + timedelta(hours=hour)).timestamp())
            logger.info(f"Загрузка снимка: {date} {hour:02d}:00 UTC")
            try:
                data = self.get_historic_flights(ts)
                flights = self._normalize_flights(data)
                for f in flights:
                    f["snapshot_time"] = f"{date}T{hour:02d}:00Z"
                positions.extend(flights)
                time.sleep(0.5)  # пауза между запросами
            except Exception as e:
                logger.warning(f"Ошибка при загрузке снимка {hour}h: {e}")

        return positions

    def _normalize_flights(self, raw: dict) -> List[dict]:
        """Нормализация ответа API в единый формат"""
        flights = []
        items = raw.get("data", raw.get("flights", []))
        if not items:
            return []

        for item in items:
            # Поля могут отличаться в зависимости от endpoint и версии API
            flight = {
                "id": item.get("id") or item.get("fr24_id", ""),
                "callsign": item.get("callsign") or item.get("cs", ""),
                "flight_number": item.get("flight") or item.get("fn", ""),
                "lat": float(item.get("lat", 0) or item.get("latitude", 0)),
                "lon": float(item.get("lon", 0) or item.get("longitude", 0)),
                "altitude": int(item.get("alt", 0) or item.get("altitude", 0) or 0),
                "speed": int(item.get("gspeed", 0) or item.get("speed", 0) or 0),
                "heading": int(item.get("track", 0) or item.get("heading", 0) or 0),
                "aircraft_type": item.get("type") or item.get("ac_type", ""),
                "registration": item.get("reg", ""),
                "origin": item.get("orig_icao") or item.get("from", ""),
                "destination": item.get("dest_icao") or item.get("to", ""),
                "airline": item.get("airline") or item.get("op", ""),
                "timestamp": item.get("timestamp", ""),
            }
            # Пропускаем записи без координат
            if flight["lat"] != 0 or flight["lon"] != 0:
                flights.append(flight)

        return flights
