package gc.mda.kcg.collector.aircraft;

import com.fasterxml.jackson.databind.JsonNode;
import gc.mda.kcg.domain.aircraft.AircraftCategory;
import gc.mda.kcg.domain.aircraft.AircraftDto;

import java.util.ArrayList;
import java.util.List;

/**
 * Airplanes.live API 응답 JSON → AircraftDto 변환.
 * 프론트엔드 airplaneslive.ts:parseAirplanesLive 이식.
 */
public final class AirplanesLiveParser {

    private AirplanesLiveParser() {}

    private static final double FT_TO_M = 0.3048;
    private static final double KNOTS_TO_MS = 0.5144;
    private static final double FPM_TO_MS = 0.00508;

    /**
     * Airplanes.live JSON 응답을 파싱하여 AircraftDto 리스트 반환.
     * 응답 구조: { "ac": [ { hex, flight, lat, lon, alt_baro, gs, ... } ] }
     */
    public static List<AircraftDto> parse(JsonNode root) {
        JsonNode acArray = root.path("ac");
        if (acArray.isMissingNode() || !acArray.isArray()) {
            return List.of();
        }

        List<AircraftDto> result = new ArrayList<>();
        long now = System.currentTimeMillis();

        for (JsonNode a : acArray) {
            if (a.path("lat").isMissingNode() || a.path("lon").isMissingNode()) continue;
            Double lat = getDouble(a, "lat");
            Double lon = getDouble(a, "lon");
            if (lat == null || lon == null) continue;

            String typecode = getString(a, "t", "");
            int dbFlags = getInt(a, "dbFlags", 0);

            AircraftCategory category = AircraftClassifier.classifyByTypecode(typecode);
            if (category == AircraftCategory.CIVILIAN && AircraftClassifier.isMilitaryByDbFlags(dbFlags)) {
                category = AircraftCategory.MILITARY;
            }

            // alt_baro: 숫자(feet) 또는 문자열 "ground"
            double altitude = 0;
            boolean onGround = false;
            JsonNode altNode = a.path("alt_baro");
            if (altNode.isTextual() && "ground".equals(altNode.asText())) {
                onGround = true;
            } else if (altNode.isNumber()) {
                altitude = altNode.asDouble() * FT_TO_M;
            }

            double heading = a.has("track") ? a.path("track").asDouble(0)
                    : a.path("nav_heading").asDouble(0);
            int seen = getInt(a, "seen", 0);

            result.add(AircraftDto.builder()
                    .icao24(getString(a, "hex", ""))
                    .callsign(getString(a, "flight", "").trim())
                    .lat(lat)
                    .lng(lon)
                    .altitude(altitude)
                    .velocity(getDouble(a, "gs", 0) * KNOTS_TO_MS)
                    .heading(heading)
                    .verticalRate(getDouble(a, "baro_rate", 0) * FPM_TO_MS)
                    .onGround(onGround)
                    .category(category)
                    .typecode(typecode.isEmpty() ? null : typecode)
                    .typeDesc(getStringOrNull(a, "desc"))
                    .registration(getStringOrNull(a, "r"))
                    .operator(getStringOrNull(a, "ownOp"))
                    .squawk(getStringOrNull(a, "squawk"))
                    .lastSeen(now - (long) seen * 1000)
                    .build());
        }
        return result;
    }

    private static String getString(JsonNode node, String field, String defaultVal) {
        JsonNode v = node.path(field);
        return v.isMissingNode() || v.isNull() ? defaultVal : v.asText(defaultVal);
    }

    private static String getStringOrNull(JsonNode node, String field) {
        JsonNode v = node.path(field);
        if (v.isMissingNode() || v.isNull() || v.asText().isBlank()) return null;
        return v.asText();
    }

    private static Double getDouble(JsonNode node, String field) {
        JsonNode v = node.path(field);
        return v.isNumber() ? v.asDouble() : null;
    }

    private static double getDouble(JsonNode node, String field, double defaultVal) {
        JsonNode v = node.path(field);
        return v.isNumber() ? v.asDouble() : defaultVal;
    }

    private static int getInt(JsonNode node, String field, int defaultVal) {
        JsonNode v = node.path(field);
        return v.isNumber() ? v.asInt() : defaultVal;
    }
}
