package gc.mda.signal_batch.global.util;

import gc.mda.signal_batch.domain.vessel.dto.CompactVesselTrack;
import gc.mda.signal_batch.domain.vessel.dto.TrackResponse;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;

import java.util.*;
import java.util.stream.Collectors;

/**
 * TrackResponse (WKT 기반) → CompactVesselTrack (배열 기반) 변환 유틸리티
 * WebSocket API와 REST API의 응답 구조 통일을 위해 사용
 */
@Slf4j
public class TrackConverter {

    private static final WKTReader wktReader = new WKTReader();

    /**
     * 선박별로 분리된 TrackResponse 리스트를 CompactVesselTrack 리스트로 변환
     * - 동일 선박의 여러 세그먼트를 병합
     * - LineStringM을 geometry/timestamps/speeds 배열로 파싱
     *
     * @param trackResponses TrackResponse 리스트 (세그먼트별 분리)
     * @param vesselInfoProvider 선박 정보 조회 함수 (mmsi → VesselInfo)
     * @return CompactVesselTrack 리스트 (선박별 병합)
     */
    public static List<CompactVesselTrack> convert(
            List<TrackResponse> trackResponses,
            VesselInfoProvider vesselInfoProvider) {

        if (trackResponses == null || trackResponses.isEmpty()) {
            return Collections.emptyList();
        }

        // 1. 선박별로 그룹핑 (mmsi 기반)
        Map<String, List<TrackResponse>> byVessel = trackResponses.stream()
                .collect(Collectors.groupingBy(
                        TrackResponse::getMmsi,
                        LinkedHashMap::new,
                        Collectors.toList()
                ));

        // 2. 각 선박별로 세그먼트 병합 및 변환
        List<CompactVesselTrack> result = new ArrayList<>();

        for (Map.Entry<String, List<TrackResponse>> entry : byVessel.entrySet()) {
            String mmsi = entry.getKey();
            List<TrackResponse> segments = entry.getValue();

            CompactVesselTrack compactTrack = mergeSegments(mmsi, segments, vesselInfoProvider);
            if (compactTrack != null && compactTrack.getPointCount() > 0) {
                result.add(compactTrack);
            }
        }

        return result;
    }

    /**
     * 단일 선박의 여러 세그먼트를 하나의 CompactVesselTrack으로 병합
     */
    private static CompactVesselTrack mergeSegments(
            String mmsi,
            List<TrackResponse> segments,
            VesselInfoProvider vesselInfoProvider) {

        if (segments == null || segments.isEmpty()) {
            return null;
        }

        // 시간순 정렬
        segments.sort(Comparator.comparing(TrackResponse::getTimeBucket));

        // 누적 데이터
        List<double[]> allGeometry = new ArrayList<>();
        List<String> allTimestamps = new ArrayList<>();
        List<Double> allSpeeds = new ArrayList<>();
        double totalDistance = 0;
        double maxSpeed = 0;

        Coordinate prevCoord = null;
        long prevTimeMillis = 0;

        for (TrackResponse segment : segments) {
            String trackGeom = segment.getTrackGeom();
            if (trackGeom == null || trackGeom.isEmpty() || "LINESTRING EMPTY".equals(trackGeom)) {
                continue;
            }

            try {
                LineString lineString = (LineString) wktReader.read(trackGeom);

                if (lineString.getNumPoints() == 0) {
                    continue;
                }

                for (int i = 0; i < lineString.getNumPoints(); i++) {
                    Coordinate coord = lineString.getCoordinateN(i);
                    allGeometry.add(new double[]{coord.x, coord.y});

                    // M값 (Unix timestamp 초 단위)
                    long unixTimestamp = (long) coord.getM();
                    allTimestamps.add(String.valueOf(unixTimestamp));

                    // 속도 계산
                    double speed = 0.0;
                    long currentTimeMillis = unixTimestamp * 1000;

                    if (prevCoord != null && prevTimeMillis > 0) {
                        double distance = calculateDistanceNm(prevCoord.y, prevCoord.x, coord.y, coord.x);
                        long timeDiff = currentTimeMillis - prevTimeMillis;
                        if (timeDiff > 0) {
                            speed = distance / (timeDiff / 3600000.0); // knots
                        }
                    }
                    allSpeeds.add(speed);

                    prevCoord = coord;
                    prevTimeMillis = currentTimeMillis;
                }

                // 통계 누적
                if (segment.getDistanceNm() != null) {
                    totalDistance += segment.getDistanceNm().doubleValue();
                }
                if (segment.getMaxSpeed() != null) {
                    maxSpeed = Math.max(maxSpeed, segment.getMaxSpeed().doubleValue());
                }

            } catch (ParseException e) {
                log.warn("Failed to parse LineStringM for vessel {}: {}", mmsi, e.getMessage());
            }
        }

        // 선박 정보 조회
        VesselInfo vesselInfo = vesselInfoProvider != null
                ? vesselInfoProvider.getVesselInfo(mmsi)
                : new VesselInfo("-", "-");

        // 국적 코드: MMSI 앞 3자리 (MID)
        String nationalCode = mmsi != null && mmsi.length() >= 3 ? mmsi.substring(0, 3) : "-";

        // 평균 속도 계산
        double avgSpeed = allSpeeds.stream()
                .filter(s -> s > 0)
                .mapToDouble(Double::doubleValue)
                .average()
                .orElse(0.0);

        return CompactVesselTrack.builder()
                .vesselId(mmsi)
                .nationalCode(nationalCode)
                .geometry(allGeometry)
                .timestamps(allTimestamps)
                .speeds(allSpeeds)
                .totalDistance(totalDistance)
                .avgSpeed(Math.round(avgSpeed * 100.0) / 100.0)
                .maxSpeed(Math.round(maxSpeed * 100.0) / 100.0)
                .pointCount(allGeometry.size())
                .shipName(vesselInfo.getShipName())
                .shipType(vesselInfo.getShipType())
                .build();
    }

    /**
     * 두 좌표 사이의 거리 계산 (Haversine 공식, 해리)
     */
    private static double calculateDistanceNm(double lat1, double lon1, double lat2, double lon2) {
        double lat1Rad = Math.toRadians(lat1);
        double lat2Rad = Math.toRadians(lat2);
        double deltaLat = lat2Rad - lat1Rad;
        double deltaLon = Math.toRadians(lon2 - lon1);

        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
                Math.cos(lat1Rad) * Math.cos(lat2Rad) *
                        Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        // 지구 반지름 (nm)
        double R = 3440.065;
        return R * c;
    }

    /**
     * 선박 정보 조회 인터페이스
     */
    @FunctionalInterface
    public interface VesselInfoProvider {
        VesselInfo getVesselInfo(String mmsi);
    }

    /**
     * 선박 정보 DTO
     */
    public static class VesselInfo {
        private final String shipName;
        private final String shipType;

        public VesselInfo(String shipName, String shipType) {
            this.shipName = shipName != null ? shipName : "-";
            this.shipType = shipType != null ? shipType : "-";
        }

        public String getShipName() {
            return shipName;
        }

        public String getShipType() {
            return shipType;
        }
    }
}
