"""
Стратегия Б — загрузка треков по списку рейсов из табло.
Алгоритм:
  1. Берём список рейсов из tablo_need_load.json (callsign известен)
  2. Ищем fr24_id через /live/flight-positions/full (1 кредит/рейс)
     — но для исторических рейсов лучше /historic/flight-positions/full
     с timestamp вылета
  3. Загружаем трек /flight-tracks, фильтруем по bbox МО
  4. Сохраняем в flights_SVO_2026-03-21_strategy_b.json
"""
import requests, json, time, os, sys
from datetime import datetime, timezone
from dotenv import load_dotenv
from noise_model import calc_zone_radii_for_point

load_dotenv(dotenv_path='.env')
KEY  = os.getenv('FLIGHTRADAR24_API_KEY')
BASE = 'https://fr24api.flightradar24.com/api'
H    = {'Authorization': f'Bearer {KEY}', 'Accept': 'application/json', 'Accept-Version': 'v1'}

BBOX = {'lat_min': 54.0, 'lat_max': 57.0, 'lon_min': 35.5, 'lon_max': 40.5}
def in_mo(lat, lon):
    return BBOX['lat_min'] <= lat <= BBOX['lat_max'] and BBOX['lon_min'] <= lon <= BBOX['lon_max']

CACHE_DIR = 'data/cache_SVO_b'
os.makedirs(CACHE_DIR, exist_ok=True)

# ── Загружаем список рейсов ──────────────────────────────────────
with open('data/tablo_need_load.json') as f:
    need = json.load(f)

flights_todo = need['flights']
print(f'📋 Рейсов к загрузке: {len(flights_todo)}')
print(f'   Кэш: {CACHE_DIR}/')

def get_fr24_id(callsign, dep_iso):
    """Ищем fr24_id через исторический снимок в момент вылета"""
    cache_file = f'{CACHE_DIR}/id_{callsign}.json'
    if os.path.exists(cache_file):
        with open(cache_file) as f:
            return json.load(f).get('fr24_id')

    # Берём timestamp из времени вылета
    try:
        dep_dt = datetime.fromisoformat(dep_iso.replace('Z','+00:00'))
        ts = int(dep_dt.timestamp())
    except:
        return None

    r = requests.get(f'{BASE}/historic/flight-positions/full', headers=H,
        params={'bounds': '57.0,54.0,35.5,40.5', 'timestamp': ts, 'limit': 500},
        timeout=20)
    if r.status_code == 429:
        time.sleep(35)
        r = requests.get(f'{BASE}/historic/flight-positions/full', headers=H,
            params={'bounds': '57.0,54.0,35.5,40.5', 'timestamp': ts, 'limit': 500},
            timeout=20)
    if not r.ok: return None

    data = r.json().get('data', [])
    cs_norm = callsign.replace(' ','').upper()
    for item in data:
        item_cs = (item.get('callsign') or '').upper()
        item_fn = (item.get('flight') or '').replace(' ','').upper()
        if item_cs == cs_norm or item_fn == cs_norm:
            fid = item.get('fr24_id')
            with open(cache_file, 'w') as f:
                json.dump({'fr24_id': fid, 'callsign': callsign}, f)
            return fid
    # Не нашли — сохраняем null чтобы не повторять
    with open(cache_file, 'w') as f:
        json.dump({'fr24_id': None}, f)
    return None

def get_track(fr24_id):
    """Загружаем трек, фильтруем по МО"""
    cache_file = f'data/cache_SVO/track_{fr24_id}.json'
    # Проверяем все кэши
    for d in ['data/cache_SVO', 'data/cache_SVO_offset90m', CACHE_DIR]:
        cf = f'{d}/track_{fr24_id}.json'
        if os.path.exists(cf):
            with open(cf) as f:
                return json.load(f)

    r = requests.get(f'{BASE}/flight-tracks', headers=H,
        params={'flight_id': fr24_id}, timeout=20)
    if r.status_code == 429:
        print(f'  ⏳ 429, пауза 35с...')
        time.sleep(35)
        r = requests.get(f'{BASE}/flight-tracks', headers=H,
            params={'flight_id': fr24_id}, timeout=20)
    if not r.ok: return []

    raw = r.json()
    tracks = raw[0].get('tracks', []) if isinstance(raw, list) and raw else []
    with open(f'{CACHE_DIR}/track_{fr24_id}.json', 'w') as f:
        json.dump(tracks, f)
    return tracks

# ── Основной цикл ────────────────────────────────────────────────
flights_final = []
found_ids     = 0
no_id         = 0
no_mo_points  = 0
errors        = 0

# Группируем рейсы по времени вылета — один snapshot на несколько рейсов
# Это экономит кредиты: один запрос на снимок даёт много fr24_id
snapshot_cache = {}  # timestamp → {callsign: fr24_id}

def get_snapshot(ts):
    if ts in snapshot_cache:
        return snapshot_cache[ts]
    r = requests.get(f'{BASE}/historic/flight-positions/full', headers=H,
        params={'bounds': '57.0,54.0,35.5,40.5', 'timestamp': ts, 'limit': 500},
        timeout=20)
    if r.status_code == 429:
        time.sleep(35)
        r = requests.get(f'{BASE}/historic/flight-positions/full', headers=H,
            params={'bounds': '57.0,54.0,35.5,40.5', 'timestamp': ts, 'limit': 500},
            timeout=20)
    if not r.ok:
        snapshot_cache[ts] = {}
        return {}
    result = {}
    for item in r.json().get('data', []):
        cs = (item.get('callsign') or '').replace(' ','').upper()
        fn = (item.get('flight') or '').replace(' ','').upper()
        fid = item.get('fr24_id')
        if cs:  result[cs] = fid
        if fn:  result[fn] = fid
    snapshot_cache[ts] = result
    return result

# Группируем рейсы по ближайшему часу вылета
from itertools import groupby
from collections import defaultdict

hour_groups = defaultdict(list)
no_dep = []
for fl in flights_todo:
    dep = fl.get('departure', '')
    if not dep:
        no_dep.append(fl)
        continue
    try:
        dt = datetime.fromisoformat(dep.replace('Z','+00:00'))
        # Округляем к ближайшим 30 мин (±15 мин от вылета)
        ts = int(dt.timestamp())
        ts_rounded = (ts // 1800) * 1800
        hour_groups[ts_rounded].append(fl)
    except:
        no_dep.append(fl)

print(f'Уникальных временных слотов: {len(hour_groups)} (по 30 мин)')
print(f'Рейсов без времени вылета: {len(no_dep)}')
print(f'Всего слотов × ~500 рейсов/снимок — загружаем эффективно\n')

total = len(flights_todo)
processed = 0

for ts_slot, slot_flights in sorted(hour_groups.items()):
    # Один запрос снимка на весь слот
    dt_str = datetime.fromtimestamp(ts_slot, tz=timezone.utc).strftime('%H:%M')
    snapshot = get_snapshot(ts_slot)
    time.sleep(1.2)

    for fl in slot_flights:
        processed += 1
        cs = (fl.get('callsign') or '').replace(' ','').upper()
        fn = (fl.get('flight_number') or '').replace(' ','').upper()

        # Ищем fr24_id из снимка
        fid = snapshot.get(cs) or snapshot.get(fn)

        # Если не нашли — пробуем соседние слоты (±30 мин)
        if not fid:
            for delta in [-1800, 1800, -3600, 3600]:
                fid = snapshot_cache.get(ts_slot + delta, {}).get(cs) or \
                      snapshot_cache.get(ts_slot + delta, {}).get(fn)
                if fid: break

        if not fid:
            # Последняя попытка — проверяем кэш файлов
            cf = f'{CACHE_DIR}/id_{cs}.json'
            if os.path.exists(cf):
                with open(cf) as f2:
                    fid = json.load(f2).get('fr24_id')

        if not fid:
            no_id += 1
            print(f'  [{processed}/{total}] {fl["flight_number"]:10} ❓ fr24_id не найден')
            continue

        found_ids += 1

        # Загружаем трек
        tracks = get_track(fid)
        time.sleep(1.0)

        mo_pts = sorted([p for p in tracks if in_mo(p.get('lat',0), p.get('lon',0))],
                        key=lambda p: p.get('timestamp',''))
        if len(mo_pts) < 2:
            no_mo_points += 1
            continue

        points = []
        for pt in mo_pts:
            alt_ft = pt.get('alt',0) or 0
            alt_m  = round(alt_ft / 3.28084)
            points.append({
                'lat': pt['lat'], 'lon': pt['lon'],
                'altitude': alt_ft, 'altitude_m': alt_m,
                'speed': pt.get('gspeed',0), 'heading': pt.get('track',0),
                'timestamp': pt.get('timestamp',''),
                'zone_radii': calc_zone_radii_for_point(alt_m),
            })

        # Определяем тип (вылет из SVO или прилёт в SVO)
        flight_type = fl.get('direction', 'departure')

        flights_final.append({
            'id': fid,
            'callsign': fl.get('callsign', cs),
            'flight_number': fl.get('flight_number', ''),
            'aircraft_type': fl.get('aircraft', '')[:5] if fl.get('aircraft') else '',
            'registration': '',
            'airline': fl.get('airline', ''),
            'airline_iata': fl.get('airline_iata', ''),
            'origin': 'SVO' if flight_type == 'departure' else '',
            'destination': '' if flight_type == 'departure' else 'SVO',
            'orig_icao': 'UUEE' if flight_type == 'departure' else '',
            'dest_icao': '' if flight_type == 'departure' else 'UUEE',
            'airport': 'SVO',
            'type': flight_type,
            'dep_scheduled': fl.get('departure', ''),
            'points': points,
        })

        if processed % 20 == 0 or processed == total:
            pts_total = sum(len(f['points']) for f in flights_final)
            print(f'  [{processed}/{total}] треков: {len(flights_final)}, точек: {pts_total}, '
                  f'нет id: {no_id}, нет МО: {no_mo_points}')

# Сохраняем
print(f'\n✅ Итого треков с данными над МО: {len(flights_final)}')
print(f'   fr24_id не найден: {no_id}')
print(f'   нет точек над МО:  {no_mo_points}')

out = {
    'airport': 'SVO', 'date': '2026-03-21',
    'strategy': 'B', 'source': 'yandex_rasp + fr24_production',
    'flights_count': len(flights_final),
    'flights': flights_final
}
with open('data/flights_SVO_2026-03-21_strategy_b.json', 'w', encoding='utf-8') as f:
    json.dump(out, f, ensure_ascii=False)

total_pts = sum(len(f['points']) for f in flights_final)
print(f'   Точек над МО: {total_pts}')
print('💾 data/flights_SVO_2026-03-21_strategy_b.json')
