import { TguApiError } from '../model/tguTypes'
import { toUtcIso } from '../utils/localTime'
import { apiFetch } from './apiFetch'

const BASE = '/api/pcp-ballistics'

export type FlightLinePoint = {
  time: string
  revolution: number
  lat: number
  long: number
  latLeft: number
  longLeft: number
  latInnerLeft: number
  longInnerLeft: number
  latInnerRight: number
  longInnerRight: number
  latRight: number
  longRight: number
  revSign: 'ASC' | 'DESC'
}

/**
 * Трасса полёта КА (flight-line) на интервале для отрисовки на карте/таймлайне из
 * pcp-ballistics-service (GET /api/satellites/{norad}/flight-line). Шаг трассы 60 с.
 * @param noradId NORAD-идентификатор КА.
 * @param fromMs Начало интервала, мс от эпохи.
 * @param toMs Конец интервала, мс от эпохи.
 *
 * @returns Точки трассы полёта на интервале.
 */
export async function fetchFlightLine(
  noradId: string,
  fromMs: number,
  toMs: number,
): Promise<FlightLinePoint[]> {
  const params = new URLSearchParams({
    time_start: toLocalDateTime(fromMs),
    time_stop: toLocalDateTime(toMs),
  })
  const url = `${BASE}/api/satellites/${encodeURIComponent(noradId)}/flight-line?${params.toString()}`

  let response: Response
  try {
    response = await apiFetch(url, { headers: { Accept: 'application/json' } })
  } catch {
    throw new TguApiError('Не удалось подключиться к сервису баллистики.', 'network')
  }

  const text = await response.text()
  if (!response.ok) {
    throw new TguApiError(
      extractErrorText(text) || 'Ошибка сервиса баллистики.',
      'http',
      response.status,
    )
  }
  if (!text.trim()) {
    return []
  }

  return JSON.parse(text) as FlightLinePoint[]
}

export type PointVisibilityParam = {
  /** NORAD-идентификатор КА. */
  noradId: number
  /** Идентификатор объекта (цели) из запроса. */
  objectId: string
  /** Момент видимости (UTC ISO). */
  time: string
  /** Номер витка. */
  revolution: number
  /** Крен (gamma), град. */
  gamma: number
  /** Угол Солнца, град. */
  sunAngle: number
  /** Наклонная дальность. */
  range: number
  /** Направление витка. */
  revSign: 'ASC' | 'DESC'
}

export type PointVisibilityFilters = {
  /** Минимальный угол Солнца, град. */
  sunAngleMin?: number
  /** Минимальный крен, град. */
  rollMin?: number
  /** Максимальный крен, град. */
  rollMax?: number
}

export type GeoPointTarget = {
  id?: string
  lat: number
  lon: number
  height?: number
}

/**
 * Параметры видимости точки на заданном интервале времени из pcp-ballistics-service
 * (POST /api/obj-view/mpl-point). Возвращает по одному моменту видимости на виток.
 * @param noradId NORAD-идентификатор КА.
 * @param target Цель (координаты точки) для расчёта видимости.
 * @param fromMs Начало интервала, мс от эпохи.
 * @param toMs Конец интервала, мс от эпохи.
 * @param filters Фильтры по углу Солнца и крену (необязательны).
 *
 * @returns По одному моменту видимости на виток на интервале.
 */
export async function fetchPointVisibility(
  noradId: number,
  target: GeoPointTarget,
  fromMs: number,
  toMs: number,
  filters: PointVisibilityFilters = {},
): Promise<PointVisibilityParam[]> {
  const body = {
    satellites: [noradId],
    timeStart: toLocalDateTime(fromMs),
    timeStop: toLocalDateTime(toMs),
    obj: {
      id: target.id ?? '',
      position: {
        lat: target.lat,
        long: target.lon,
        height: target.height ?? 0,
      },
    },
    sunAngleMin: filters.sunAngleMin ?? null,
    rollMin: filters.rollMin ?? null,
    rollMax: filters.rollMax ?? null,
  }

  let response: Response
  try {
    response = await apiFetch(`${BASE}/api/obj-view/mpl-point`, {
      method: 'POST',
      headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    })
  } catch {
    throw new TguApiError('Не удалось подключиться к сервису баллистики.', 'network')
  }

  const text = await response.text()
  if (!response.ok) {
    throw new TguApiError(
      extractErrorText(text) || 'Ошибка сервиса баллистики.',
      'http',
      response.status,
    )
  }

  if (!text.trim()) {
    return []
  }

  return JSON.parse(text) as PointVisibilityParam[]
}

/** Целеуказание (момент/угол места/азимут/дальность) границы зоны радиовидимости. */
export type RvaTargetPosition = {
  /** Момент (UTC ISO). */
  time: string
  /** Угол места, град. */
  elevation: number
  /** Азимут, град. */
  azimuth: number
  /** Наклонная дальность. */
  range: number
}

/** Зона радиовидимости (ЗРВ) станции для КА на витке. */
export type RvaZone = {
  /** NORAD-идентификатор КА. */
  noradId: number
  /** Номер станции приёма (совпадает с number из /api/stations). */
  stationId: number
  /** Номер витка. */
  revolution: number
  /** Вход в зону. */
  onStart: RvaTargetPosition
  /** Максимум (кульминация). */
  onMaximum: RvaTargetPosition
  /** Выход из зоны. */
  onStop: RvaTargetPosition
  /** Длительность зоны, секунды. */
  duration: number
}

/**
 * Зоны радиовидимости (ЗРВ) для КА на заданном интервале из pcp-ballistics-service
 * (GET /api/satellites/{norad}/rva). Баллистика считает зоны по реальным станциям приёма
 * (каталог берёт у сервиса станций); каждая зона помечена номером станции (stationId).
 * @param noradId NORAD-идентификатор КА.
 * @param fromMs Начало интервала, мс от эпохи.
 * @param toMs Конец интервала, мс от эпохи.
 *
 * @returns Зоны радиовидимости КА на интервале (помечены номером станции).
 */
export async function fetchSatelliteRva(
  noradId: number,
  fromMs: number,
  toMs: number,
): Promise<RvaZone[]> {
  const params = new URLSearchParams({
    time_start: toLocalDateTime(fromMs),
    time_stop: toLocalDateTime(toMs),
  })
  const url = `${BASE}/api/satellites/${noradId}/rva?${params.toString()}`

  let response: Response
  try {
    response = await apiFetch(url, { headers: { Accept: 'application/json' } })
  } catch {
    throw new TguApiError('Не удалось подключиться к сервису баллистики.', 'network')
  }

  const text = await response.text()
  if (!response.ok) {
    throw new TguApiError(
      extractErrorText(text) || 'Ошибка сервиса баллистики.',
      'http',
      response.status,
    )
  }
  if (!text.trim()) {
    return []
  }

  return JSON.parse(text) as RvaZone[]
}

/**
 * UTC ISO date-time for API DTO/query params.
 * @param ms Время в миллисекундах от эпохи.
 *
 * @returns Строка UTC date-time.
 */
function toLocalDateTime(ms: number): string {
  return toUtcIso(ms)
}

function extractErrorText(body: string): string {
  const trimmed = body.trim()
  if (!trimmed) return ''
  try {
    const parsed = JSON.parse(trimmed) as Record<string, unknown>
    for (const field of ['message', 'error', 'reason', 'detail']) {
      const value = parsed[field]
      if (typeof value === 'string' && value.trim()) {
        return value
      }
    }
  } catch {
    return trimmed
  }
  return trimmed
}
