import { XMLParser } from 'fast-xml-parser';
import { HttpError } from '../middleware/errors.js';
import { routeMetrics } from './geo.js';

const parser = new XMLParser({
  ignoreAttributes: false,
  attributeNamePrefix: '@_',
  trimValues: true
});

function asList(value) {
  if (value == null) return [];
  return Array.isArray(value) ? value : [value];
}

function parseXml(xml) {
  try {
    return parser.parse(xml);
  } catch (error) {
    throw new HttpError(400, 'Die GPX-Datei ist kein gültiges XML.', error.message);
  }
}

function toRoutePoint(point, sequence) {
  return {
    sequence,
    lat: Number.parseFloat(point['@_lat']),
    lon: Number.parseFloat(point['@_lon']),
    elevation: point.ele == null ? null : Number.parseFloat(point.ele),
    recordedAt: point.time || null
  };
}

function trackPoints(tracks) {
  return tracks
    .flatMap(track => asList(track.trkseg))
    .flatMap(segment => asList(segment.trkpt))
    .map(toRoutePoint)
    .filter(point => Number.isFinite(point.lat) && Number.isFinite(point.lon));
}

export function parseGpx(xml) {
  const gpx = parseXml(xml)?.gpx;
  if (!gpx) throw new HttpError(400, 'GPX-Wurzelelement fehlt.');

  const tracks = asList(gpx.trk);
  const points = trackPoints(tracks);
  if (!points.length) {
    throw new HttpError(400, 'Die GPX-Datei enthält keine verwertbaren Trackpunkte.');
  }

  return {
    name: tracks[0]?.name || gpx.metadata?.name || null,
    points,
    metrics: routeMetrics(points)
  };
}
