"""
Модель шумового загрязнения от воздушных судов (v1.1)

Физическая основа
─────────────────
Шум распространяется сферически. Уровень шума определяется
реальным 3D-расстоянием R (гипотенуза) от самолёта до наблюдателя.

На карте отображается горизонтальный катет D:

    самолёт ●
            |\
          H |  \ R  ← граница зоны
            |    \
  земля ●───●─────● наблюдатель
              D

  D = √(R² − H²),  если H < R, иначе 0

Пример (H = 3.5 км):
  R=2 км  → нет (2² < 3.5²)
  R=5 км  → D = √(25−12.25) = 3.57 км (круг)
  R=7 км  → D = 6.06 км, кольцо от 3.57 до 6.06 км
  R=11 км → D = 10.43 км, кольцо от 6.06 до 10.43 км

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
КАЛИБРОВОЧНЫЕ ПАРАМЕТРЫ (редактируй здесь)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

NOISE_ZONES — три концентрических зоны вдоль траектории.
Каждая зона описывает «рукав» определённой ширины рядом с треком.

Поля каждой зоны:
  id          - уникальный идентификатор (используется в JS)
  label       - отображаемое название в легенде
  dist_km     - внешняя граница зоны от трека (км)
                Зона 0 рисуется от 0 до dist_km[0],
                Зона 1 — от dist_km[0] до dist_km[1], и т.д.
  color       - цвет заливки (hex)
  opacity     - базовая прозрачность при полной активации (0.0–1.0)
                Итоговая прозрачность умножается на altitude_factor

ALTITUDE_BANDS — как высота влияет на ширину зон.
  max_alt_m   - верхняя граница диапазона высоты (метры)
  width_factor - коэффициент ширины зоны (1.0 = полная, 0.0 = зона исчезает)
  Диапазоны проверяются снизу вверх, берётся первый подходящий.

Пример калибровки:
  Если реальные замеры показывают, что на высоте 500м зона 0–2км
  слишком широкая — уменьши width_factor для диапазона max_alt_m=900.
"""

# ── Зоны шума ────────────────────────────────────────────────────
#
# Физическая модель (теорема Пифагора):
#
#         самолёт ●
#                 |\
#               H |  \ R  ← гипотенуза = реальное расстояние до наблюдателя
#                 |    \
#   земля ●───────●──────● наблюдатель
#       проекция    D    ← катет = ширина зоны на карте
#
#   D = √(R² − H²),  если H < R, иначе 0
#
# Поля зоны:
#   R_inner  — внутренняя граница сферы (км); для первой зоны = 0
#   R_outer  — внешняя  граница сферы (км)
#   color    — цвет заливки (hex)
#   opacity  — прозрачность (фиксированная, 0.0–1.0)
#
# Таблица соответствия:
#   R < 2 км   → критический шум  🔴
#   R 2–5 км   → сильный шум      🟠
#   R 5–7 км   → средний шум      🟡
#   R 7–9 км   → низкий шум        🟢
#   R > 9 км   → зона не рисуется
#
NOISE_ZONES = [
    {
        "id":      "zone_critical",
        "label":   "Критический (R < 2 км)",
        "R_inner": 0.0,    # км — внутренняя граница сферы
        "R_outer": 2.0,    # км — внешняя  граница сферы
        "color":   "#FF3333",
        "opacity": 0.01,
    },
    {
        "id":      "zone_strong",
        "label":   "Сильный (R 2–5 км)",
        "R_inner": 2.0,
        "R_outer": 5.0,
        "color":   "#FF8800",
        "opacity": 0.01,
    },
    {
        "id":      "zone_medium",
        "label":   "Средний (R 5–7 км)",
        "R_inner": 5.0,
        "R_outer": 7.0,
        "color":   "#FFCC00",
        "opacity": 0.01,
    },
    {
        "id":      "zone_low",
        "label":   "Низкий (R 7–9 км)",
        "R_inner": 7.0,
        "R_outer": 9.0,
        "color":   "#88DD00",
        "opacity": 0.01,
    },
]

# ALTITUDE_BANDS больше не используется — ширина зоны теперь
# рассчитывается аналитически через теорему Пифагора в calc_horizontal_radius()
ALTITUDE_BANDS = []  # оставлен для обратной совместимости

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# КОНЕЦ КАЛИБРОВОЧНЫХ ПАРАМЕТРОВ
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Справочные данные: типичные уровни шума у земли (дБ)
# Источник: стандартные авиационные данные
NOISE_AT_GROUND = {
    "default": 85,       # дефолт для неизвестного типа ВС
    "B738": 88,          # Boeing 737-800
    "B77W": 90,          # Boeing 777-300ER
    "A320": 87,          # Airbus A320
    "A321": 88,          # Airbus A321
    "A333": 89,          # Airbus A330-300
    "A359": 86,          # Airbus A350-900
    "B763": 89,          # Boeing 767-300
    "SU95": 86,          # Sukhoi Superjet 100
    "E170": 84,          # Embraer 170
    "AT75": 80,          # ATR 72
}

# Параметры модели
MAX_NOISE_RADIUS_KM = 3.0    # максимальный радиус шумовой зоны (км) на нулевой высоте
MIN_ALTITUDE_FT = 100        # минимальная высота для расчёта (фут)
MAX_ALTITUDE_FT = 40000      # максимальная высота (фут) — шум не слышен выше
NOISE_THRESHOLD_DB = 55      # порог шума (дБ), ниже которого зона не показывается


def altitude_to_noise_db(altitude_ft: float, aircraft_type: str = "default") -> float:
    """
    Расчёт уровня шума на земле в зависимости от высоты (дБ)
    
    Формула: L = L0 - 20*log10(h/h0) - α*h
    где L0 - шум у земли, h - высота, h0 = 300 ft (опорная высота), α = коэф. затухания
    """
    base_noise = NOISE_AT_GROUND.get(aircraft_type, NOISE_AT_GROUND["default"])
    
    if altitude_ft <= MIN_ALTITUDE_FT:
        return base_noise
    
    if altitude_ft >= MAX_ALTITUDE_FT:
        return 0.0
    
    # Геометрическое затухание (обратный квадрат расстояния → 20 log)
    import math
    h0 = 300  # опорная высота в футах
    geometric_attenuation = 20 * math.log10(altitude_ft / h0)
    
    # Атмосферное поглощение (приблизительно 0.002 дБ/фут)
    atmospheric_attenuation = 0.002 * altitude_ft
    
    noise_db = base_noise - geometric_attenuation - atmospheric_attenuation
    return max(0.0, noise_db)


def altitude_to_noise_radius_km(altitude_ft: float) -> float:
    """
    Расчёт радиуса шумовой зоны (км) на основе высоты
    Простая обратно-пропорциональная модель для визуализации
    """
    if altitude_ft <= 0:
        altitude_ft = 100
    
    if altitude_ft >= MAX_ALTITUDE_FT:
        return 0.0
    
    # Радиус уменьшается с высотой (нелинейно)
    radius = MAX_NOISE_RADIUS_KM * (1.0 - (altitude_ft / MAX_ALTITUDE_FT) ** 0.5)
    return max(0.0, radius)


def altitude_to_color(altitude_ft: float) -> str:
    """
    Цветовая кодировка по высоте:
    - Красный (0–3000 ft): высокий шум
    - Оранжевый (3000–10000 ft): средний шум
    - Жёлтый (10000–25000 ft): низкий шум
    - Зелёный (25000+ ft): минимальный шум
    """
    if altitude_ft < 3000:
        return "#FF0000"     # красный - критический шум
    elif altitude_ft < 10000:
        return "#FF6600"     # оранжевый - высокий шум
    elif altitude_ft < 25000:
        return "#FFAA00"     # жёлтый - средний шум
    else:
        return "#00AA44"     # зелёный - низкий шум


def altitude_to_noise_level(altitude_ft: float) -> str:
    """Текстовое описание уровня шума"""
    if altitude_ft < 3000:
        return "Критический"
    elif altitude_ft < 10000:
        return "Высокий"
    elif altitude_ft < 25000:
        return "Средний"
    else:
        return "Низкий"


def calculate_noise_opacity(altitude_ft: float) -> float:
    """Прозрачность шумовой зоны (0.1–0.6)"""
    if altitude_ft >= MAX_ALTITUDE_FT:
        return 0.0
    opacity = 0.6 * (1.0 - altitude_ft / MAX_ALTITUDE_FT)
    return max(0.05, min(0.6, opacity))


def calc_horizontal_radius(R_km: float, altitude_m: float) -> float:
    """
    Горизонтальный радиус зоны на карте (катет) по теореме Пифагора.

    R_km      — радиус сферы шума (км), граница зоны
    altitude_m — высота самолёта над землёй (метры)

    Возвращает D в км, или 0 если самолёт выше границы зоны.
    """
    import math
    H = altitude_m / 1000.0  # переводим в км
    if H >= R_km:
        return 0.0
    return math.sqrt(max(0.0, R_km**2 - H**2))


def calc_zone_radii_for_point(altitude_m: float) -> list:
    """
    Для каждой зоны возвращает (D_inner, D_outer) в км на земле.
    Если D_outer == 0 → зона не видна.
    Если D_inner == 0 → зона рисуется как круг (без дырки).
    """
    result = []
    for zone in NOISE_ZONES:
        d_inner = calc_horizontal_radius(zone["R_inner"], altitude_m) if zone["R_inner"] > 0 else 0.0
        d_outer = calc_horizontal_radius(zone["R_outer"], altitude_m)
        result.append({
            "id":      zone["id"],
            "color":   zone["color"],
            "opacity": zone["opacity"],
            "d_inner": round(d_inner, 4),  # км, внутренняя граница на карте
            "d_outer": round(d_outer, 4),  # км, внешняя  граница на карте
            "visible": d_outer > 0.0,
        })
    return result


def get_noise_config() -> dict:
    """
    Возвращает калибровочные параметры для фронтенда.
    Вызывается через /api/noise-config — JS читает конфиг при старте.
    """
    return {
        "zones": NOISE_ZONES,
        "altitude_bands": ALTITUDE_BANDS,
    }


def get_altitude_width_factor(altitude_m: float) -> float:
    """Возвращает коэффициент ширины зоны для данной высоты (метры)."""
    for band in ALTITUDE_BANDS:
        if altitude_m <= band["max_alt_m"]:
            return band["width_factor"]
    return 0.0


def process_flight_for_map(flight_data: dict) -> dict:
    """
    Обрабатывает данные одного рейса и добавляет шумовые характеристики
    """
    altitude = flight_data.get("altitude", 0) or 0
    aircraft_type = flight_data.get("aircraft_type", "default") or "default"
    
    return {
        **flight_data,
        "noise_db": round(altitude_to_noise_db(altitude, aircraft_type), 1),
        "noise_radius_km": round(altitude_to_noise_radius_km(altitude), 3),
        "noise_color": altitude_to_color(altitude),
        "noise_level": altitude_to_noise_level(altitude),
        "noise_opacity": round(calculate_noise_opacity(altitude), 3),
    }
