import { wingPool } from '../db/wingDb.js';
import { getBacktrack } from './predictionService.js';

const PYTHON_API_URL = process.env.PYTHON_API_URL ?? 'http://localhost:5003';
const VESSEL_TRACK_API_URL = process.env.VESSEL_TRACK_API_URL ?? 'https://guide.gc-si.dev/signal-batch';
const VESSEL_TRACK_COOKIE = process.env.VESSEL_TRACK_COOKIE ?? '';

// 유종 코드(DB) → OpenDrift 유종 코드 매핑
const OIL_TYPE_MAP: Record<string, string> = {
  'BUNKER_C':      'GENERIC BUNKER C',
  'DIESEL':        'GENERIC DIESEL',
  'CRUDE_OIL':     'WEST TEXAS INTERMEDIATE (WTI)',
  'HEAVY_FUEL_OIL': 'GENERIC HEAVY FUEL OIL',
  'KEROSENE':      'FUEL OIL NO.1 (KEROSENE)',
  'GASOLINE':      'GENERIC GASOLINE',
};

// AIS 선박유형 코드 → 위험도 점수 매핑
// AIS VESSEL_TP: 80-89=유조선류, 70-79=카고, 30-39=어선
const VESSEL_TYPE_SCORES: Array<[number, number, number]> = [
  [80, 89, 1.0],  // 유조선 계열
  [70, 79, 0.5],  // 화물선 계열
  [30, 39, 0.3],  // 어선
];

const RANK_COLORS = ['#ef4444', '#f97316', '#eab308', '#22c55e', '#3b82f6',
                     '#8b5cf6', '#ec4899', '#14b8a6', '#f59e0b', '#6366f1'];

interface PythonParticle { lat: number; lon: number }
interface PythonTimeStep {
  particles: PythonParticle[];
  center_lat?: number;
  center_lon?: number;
  remaining_volume_m3: number;
  weathered_volume_m3: number;
  pollution_area_km2: number;
  beached_volume_m3: number;
  pollution_coast_length_m: number;
}

// ============================================================
// 선박 항적 API 타입
// ============================================================

interface VesselTrackApiRequest {
  startTime: string;
  endTime: string;
  mode: 'SEQUENTIAL';
  polygons: Array<{
    id: string;
    name: string;
    coordinates: [number, number][];
  }>;
}

interface ChnPrmShipInfo {
  imo: number;
  name: string;
  callsign: string;
  vesselType: string;
  lat: number;
  lon: number;
  sog: number;
  cog: number;
  heading: number;
  length: number;
  width: number;
  draught: number;
  destination: string;
  status: string;
  messageTimestamp: string;
}

interface VesselTrack {
  vesselId: string;
  nationalCode: string;
  geometry: [number, number][];  // [lon, lat][]
  speeds: number[];              // knots
  totalDistance: number;
  avgSpeed: number;
  maxSpeed: number;
  pointCount: number;
  shipName: string;
  shipType: string;              // vessel type code string (e.g. "74")
  shipKindCode: string;
  chnPrmShipInfo: ChnPrmShipInfo | null;
  timestamps: string[];          // Unix timestamp strings
}

interface HitDetail {
  polygonId: string;
  polygonName: string;
  entryTimestamp: number;
  exitTimestamp: number;
  hitPointCount: number;
  visitIndex: number;
}

interface VesselTrackApiResponse {
  tracks: VesselTrack[];
  hitDetails: Record<string, HitDetail[]>;
  summary: {
    totalVessels: number;
    totalPoints: number;
    mode: string;
    polygonIds: string[];
    processingTimeMs: number;
    cachedDates: string[];
    totalCachedVessels: number;
  };
}

// haversine 거리 계산 (NM)
function haversineNm(lat1: number, lon1: number, lat2: number, lon2: number): number {
  const R = 3440.065; // NM
  const dLat = (lat2 - lat1) * Math.PI / 180;
  const dLon = (lon2 - lon1) * Math.PI / 180;
  const a = Math.sin(dLat / 2) ** 2 +
    Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) ** 2;
  return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}

// anlysRange 파싱: '12', '±12시간', '12h' 등 → 숫자
function parseAnalysisHours(anlysRange: string): number {
  const m = anlysRange.match(/(\d+)/);
  return m ? parseInt(m[1], 10) : 12;
}

// 시간 포맷: Date → 'HH:MM' 형식 (KST)
function toTimeLabel(d: Date): string {
  const kst = new Date(d.getTime() + 9 * 3600000);
  return `${String(kst.getUTCHours()).padStart(2, '0')}:${String(kst.getUTCMinutes()).padStart(2, '0')}`;
}


// 파티클 스텝별 탐색 영역 계산
function computeParticleSteps(
  rawResult: PythonTimeStep[],
  spillTime: Date,
  anlysHours: number,
): Array<{ stepIdx: number; atTime: Date; centroid: { lat: number; lon: number }; radiusNm: number }> {
  const totalSteps = rawResult.length;
  const msPerStep = anlysHours * 3600000 / Math.max(totalSteps - 1, 1);

  return rawResult.map((step, idx) => {
    const atTime = new Date(spillTime.getTime() - idx * msPerStep);
    const particles = step.particles.filter(p => p.lat != null && p.lon != null);

    let centroid: { lat: number; lon: number };
    let radiusNm: number;

    if (particles.length === 0) {
      centroid = { lat: step.center_lat ?? 0, lon: step.center_lon ?? 0 };
      radiusNm = 5;
    } else {
      centroid = {
        lat: particles.reduce((s, p) => s + p.lat, 0) / particles.length,
        lon: particles.reduce((s, p) => s + p.lon, 0) / particles.length,
      };
      const maxDist = Math.max(...particles.map(p => haversineNm(centroid.lat, centroid.lon, p.lat, p.lon)));
      radiusNm = Math.max(maxDist * 1.2, 2); // 최소 2 NM
    }

    return { stepIdx: idx, atTime, centroid, radiusNm };
  });
}

// 선박유형 점수
function getVesselTypeScore(vesselTp: number | null): number {
  if (vesselTp == null) return 0.3;
  for (const [min, max, score] of VESSEL_TYPE_SCORES) {
    if (vesselTp >= min && vesselTp <= max) return score;
  }
  return 0.2;
}

// 급감속 감지: 속도 배열에서 50% 이상 감소 여부 → 0~1
function detectSpeedDrop(speeds: number[]): number {
  const valid = speeds.filter(s => s > 0);
  if (valid.length < 2) return 0;
  let maxDrop = 0;
  for (let i = 1; i < valid.length; i++) {
    const drop = (valid[i - 1] - valid[i]) / valid[i - 1];
    if (drop > maxDrop) maxDrop = drop;
  }
  return maxDrop > 0.5 ? Math.min(1, maxDrop) : 0;
}

// AIS 단절 감지: 타임스탬프 배열에서 평균 간격의 3배 이상 gap 존재 여부
function detectAisGapFromTimestamps(timestamps: string[]): boolean {
  if (timestamps.length < 3) return false;
  const sorted = timestamps.map(Number).sort((a, b) => a - b);
  const avgInterval = (sorted[sorted.length - 1] - sorted[0]) / (sorted.length - 1);
  for (let i = 1; i < sorted.length; i++) {
    if (sorted[i] - sorted[i - 1] > avgInterval * 3) return true;
  }
  return false;
}

function vesselTypeToLabel(tp: number | null): string {
  if (tp == null) return '미분류';
  if (tp >= 80 && tp <= 89) return '유조선';
  if (tp >= 70 && tp <= 79) return '화물선';
  if (tp >= 30 && tp <= 39) return '어선';
  if (tp >= 60 && tp <= 69) return '여객선';
  if (tp >= 90 && tp <= 99) return '특수선';
  return `선박(${tp})`;
}

interface RankedVessel {
  rank: number;
  name: string;
  imo: string;
  type: string;
  flag: string;
  flagCountry: string;
  probability: number;
  closestTime: string;
  closestDistance: number;
  speedChange: string;
  aisStatus: string;
  description: string;
  color: string;
  mmsi: string;
  _rawScore: number;
  _track: VesselTrack;
  _minDistIdx: number;
}

// 탐색 폴리곤 빌드 (사고위치 + 탐색반경 → 바운딩 박스)
function buildSearchPolygon(
  lat: number,
  lon: number,
  radiusNm: number,
): { id: string; name: string; coordinates: [number, number][] } {
  const latDelta = radiusNm / 60;
  const lonDelta = radiusNm / (60 * Math.cos(lat * Math.PI / 180));
  return {
    id: 'zone_0',
    name: '역추적 탐색구역',
    coordinates: [
      [lon - lonDelta, lat - latDelta],
      [lon + lonDelta, lat - latDelta],
      [lon + lonDelta, lat + latDelta],
      [lon - lonDelta, lat + latDelta],
      [lon - lonDelta, lat - latDelta],
    ],
  };
}

// 선박 항적 API 호출
async function fetchVesselTracks(
  spillLat: number,
  spillLon: number,
  srchRadiusNm: number,
  startTime: Date,
  endTime: Date,
): Promise<VesselTrackApiResponse> {
  const polygon = buildSearchPolygon(spillLat, spillLon, srchRadiusNm);
  const toApiDatetime = (d: Date) => d.toISOString().substring(0, 19);

  const body: VesselTrackApiRequest = {
    startTime: toApiDatetime(startTime),
    endTime: toApiDatetime(endTime),
    mode: 'SEQUENTIAL',
    polygons: [polygon],
  };

  console.log('[backtrack] VESSEL_TRACK_API 호출', {
    url: `${VESSEL_TRACK_API_URL}/api/v2/tracks/area-search`,
    startTime: body.startTime,
    endTime: body.endTime,
    srchRadiusNm,
    polygons: body.polygons.length,
    hasCookie: !!VESSEL_TRACK_COOKIE,
  });

  const res = await fetch(`${VESSEL_TRACK_API_URL}/api/v2/tracks/area-search`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      ...(VESSEL_TRACK_COOKIE ? { 'Cookie': VESSEL_TRACK_COOKIE } : {}),
    },
    body: JSON.stringify(body),
  });

  console.log('[backtrack] VESSEL_TRACK_API 응답', { status: res.status, ok: res.ok });
  if (!res.ok) throw new Error(`선박 항적 API 오류: ${res.status}`);
  const data = await res.json() as VesselTrackApiResponse;
  console.log('[backtrack] VESSEL_TRACK_API 데이터', { totalVessels: data.summary?.totalVessels ?? 0, tracks: data.tracks?.length ?? 0 });
  return data;
}

// 스코어링 + 순위 (선박 항적 API 응답 기반)
function scoreAndRankVesselsFromApi(
  tracks: VesselTrack[],
  hitDetails: Record<string, HitDetail[]>,
  spillLat: number,
  spillLon: number,
  srchRadiusNm: number,
  anlysHours: number,
): RankedVessel[] {
  const anlysWindowSec = anlysHours * 3600;

  const scored = tracks.map(track => {
    // 1. spatialScore (40%): 사고 지점과의 최근접 거리
    let minDist = Infinity;
    let minDistIdx = 0;
    track.geometry.forEach(([lon, lat], idx) => {
      const d = haversineNm(lat, lon, spillLat, spillLon);
      if (d < minDist) { minDist = d; minDistIdx = idx; }
    });
    const spatialScore = Math.max(0, 1 - minDist / srchRadiusNm);

    // 2. temporalScore (25%): 탐색구역 체류시간 / 분석 윈도우
    const hits = hitDetails[track.vesselId] ?? [];
    const totalTimeInZoneSec = hits.reduce((sum, h) => sum + (h.exitTimestamp - h.entryTimestamp), 0);
    const temporalScore = Math.min(1, totalTimeInZoneSec / anlysWindowSec);

    // 3. behaviorScore (20%): 급감속 + AIS 단절
    const speedDrop = detectSpeedDrop(track.speeds);
    const aisGap = detectAisGapFromTimestamps(track.timestamps);
    const behaviorScore = Math.min(1, speedDrop * 0.6 + (aisGap ? 0.4 : 0));

    // 4. vesselTypeScore (15%): 선박 유형별 위험도
    const vesselTpRaw = parseInt(track.shipType ?? '', 10);
    const vesselTp = isNaN(vesselTpRaw) ? null : vesselTpRaw;
    const vesselTypeScore = getVesselTypeScore(vesselTp);

    const rawScore = 0.40 * spatialScore + 0.25 * temporalScore + 0.20 * behaviorScore + 0.15 * vesselTypeScore;

    const speedChangeLabel = speedDrop > 0.5 ? '급감속' : speedDrop > 0.2 ? '감속' : '정상';
    const aisStatusLabel = aisGap ? 'AIS단절' : '정상';

    const closestTs = track.timestamps[minDistIdx];
    const closestDate = closestTs ? new Date(Number(closestTs) * 1000) : new Date();

    const descParts: string[] = [];
    if (speedDrop > 0.5) descParts.push(`${toTimeLabel(closestDate)} 급감속 감지`);
    if (aisGap) descParts.push('AIS 신호 단절 구간 존재');
    if (minDist < 1) descParts.push(`최근접 ${minDist.toFixed(2)} NM 이내 통과`);

    const imo = track.chnPrmShipInfo?.imo?.toString() ?? '';
    const vesselName = track.shipName || track.chnPrmShipInfo?.name || `MMSI:${track.vesselId}`;

    return {
      mmsi: track.vesselId,
      imo,
      name: vesselName,
      type: vesselTypeToLabel(vesselTp),
      flag: track.nationalCode ?? '',
      flagCountry: track.nationalCode ?? '',
      closestTime: toTimeLabel(closestDate),
      closestDistance: Math.round(minDist * 100) / 100,
      speedChange: speedChangeLabel,
      aisStatus: aisStatusLabel,
      description: descParts.join(' · '),
      probability: 0,
      rank: 0,
      color: '',
      _rawScore: rawScore,
      _track: track,
      _minDistIdx: minDistIdx,
    };
  });

  scored.sort((a, b) => b._rawScore - a._rawScore);
  const top = scored.slice(0, 10);
  const maxScore = top[0]?._rawScore ?? 1;

  return top.map((v, i) => ({
    ...v,
    rank: i + 1,
    probability: Math.round((v._rawScore / maxScore) * 95 * 10) / 10,
    color: RANK_COLORS[i] ?? '#888888',
  }));
}

interface ReplayShip {
  vesselName: string;
  color: string;
  path: Array<{ lat: number; lon: number }>;
  speedLabels: string[];
}

// 리플레이용 선박 경로 빌드 (상위 5척, API geometry 직접 사용)
function buildReplayShipsFromApi(ranked: RankedVessel[]): ReplayShip[] {
  return ranked.slice(0, 5).map(v => ({
    vesselName: v.name,
    color: v.color,
    path: v._track.geometry.map(([lon, lat]) => ({ lat, lon })),
    speedLabels: v._track.speeds.map(s => `${s.toFixed(1)} kts`),
  }));
}

interface CollisionEvent {
  position: { lat: number; lon: number };
  timeLabel: string;
  progressPercent: number;
}

// 최고 확률 선박의 최근접 지점 이벤트
function findCollisionEventFromApi(
  ranked: RankedVessel[],
  startTime: Date,
  endTime: Date,
): CollisionEvent | null {
  const top = ranked[0];
  if (!top) return null;

  const idx = top._minDistIdx;
  const ts = top._track.timestamps[idx];
  const pointDate = ts ? new Date(Number(ts) * 1000) : new Date();

  const totalMs = endTime.getTime() - startTime.getTime();
  const pointMs = pointDate.getTime() - startTime.getTime();
  const progressPercent = totalMs > 0
    ? Math.max(0, Math.min(100, Math.round((pointMs / totalMs) * 100)))
    : 0;

  const point = top._track.geometry[idx];
  const [lon, lat] = point ?? [0, 0];

  return {
    position: { lat, lon },
    timeLabel: toTimeLabel(pointDate) + ' 최근접',
    progressPercent,
  };
}

// ============================================================
// 메인 분석 함수 (외부에서 호출)
// ============================================================
export async function runBacktrackAnalysis(backtrackSn: number): Promise<void> {
  await wingPool.query(
    `UPDATE wing.BACKTRACK SET EXEC_STTS_CD='RUNNING' WHERE BACKTRACK_SN=$1`,
    [backtrackSn],
  );

  try {
    const bt = await getBacktrack(backtrackSn);
    if (!bt || bt.lat == null || bt.lon == null || !bt.estSpilDtm) {
      throw new Error('역추적 레코드 정보 불충분 (lat/lon/estSpilDtm 필요)');
    }

    const anlysHours = parseAnalysisHours(bt.anlysRange ?? '12');
    const spillTime = new Date(bt.estSpilDtm);
    const analysisStart = new Date(spillTime.getTime() - anlysHours * 3600000);

    // SPIL_DATA에서 유출량 및 유종 조회
    let matVol: number | null = null;
    let matTy: string | undefined;
    try {
      const { rows: spillRows } = await wingPool.query(
        `SELECT SPIL_QTY, OIL_TP_CD FROM wing.SPIL_DATA WHERE ACDNT_SN=$1 ORDER BY SPIL_DATA_SN ASC LIMIT 1`,
        [bt.acdntSn],
      );
      if (spillRows.length > 0) {
        const row = spillRows[0] as Record<string, unknown>;
        matVol = row['spil_qty'] != null ? Number(row['spil_qty']) : null;
        const oilTpCd = row['oil_tp_cd'] as string | null;
        matTy = oilTpCd ? (OIL_TYPE_MAP[oilTpCd] ?? oilTpCd) : undefined;
      }
    } catch (spillErr) {
      console.warn('[backtrack] SPIL_DATA 조회 실패, matVol 없이 진행:', spillErr);
    }

    // Python 역방향 시뮬레이션 실행 (파티클 시각화용)
    console.log('[backtrack] Python 역방향 시뮬레이션 요청', { lat: bt.lat, lon: bt.lon, anlysHours, matVol, matTy, spillTime: spillTime.toISOString() });
    let rawResult: PythonTimeStep[];
    try {
      const pythonRes = await fetch(`${PYTHON_API_URL}/run-model-backward`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          lat: bt.lat,
          lon: bt.lon,
          startTime: spillTime.toISOString(),
          runTime: anlysHours,
          matVol: matVol ?? 1,
          matTy,
          name: `BACKTRACK_${backtrackSn}`,
        }),
        signal: AbortSignal.timeout(30000),
      });
      if (!pythonRes.ok) throw new Error(`Python 서버 오류: ${pythonRes.status}`);
      const pythonData = await pythonRes.json() as { success: boolean; result: PythonTimeStep[] };
      rawResult = pythonData.result ?? [];
      console.log('[backtrack] Python 역방향 시뮬레이션 완료 — steps:', rawResult.length);
    } catch (pyErr) {
      // Python 미연동 시 폴백: 빈 파티클 스텝 생성
      console.warn('[backtrack] Python 미연동 — 폴백 모드 사용:', pyErr);
      rawResult = Array.from({ length: anlysHours + 1 }, () => ({
        particles: [],
        remaining_volume_m3: 0,
        weathered_volume_m3: 0,
        pollution_area_km2: 0,
        beached_volume_m3: 0,
        pollution_coast_length_m: 0,
      }));
    }

    const steps = computeParticleSteps(rawResult, spillTime, anlysHours);
    if (rawResult.every(s => s.particles.length === 0)) {
      steps.forEach((s, i) => {
        s.centroid = { lat: bt.lat!, lon: bt.lon! };
        s.radiusNm = (bt.srchRadiusNm ?? 10) + i * 2;
      });
    }

    // 선박 항적 API 호출
    const srchRadius = bt.srchRadiusNm ?? 10;
    console.log('[backtrack] 선박 항적 API 호출 시작', { srchRadius, analysisStart: analysisStart.toISOString(), spillTime: spillTime.toISOString() });
    const apiResponse = await fetchVesselTracks(
      bt.lat, bt.lon, srchRadius, analysisStart, spillTime,
    );

    const totalVessels = apiResponse.summary.totalVessels;
    console.log('[backtrack] 선박 점수 계산 시작 — totalVessels:', totalVessels);
    const ranked = scoreAndRankVesselsFromApi(
      apiResponse.tracks,
      apiResponse.hitDetails,
      bt.lat, bt.lon, srchRadius, anlysHours,
    );
    console.log('[backtrack] 선박 점수 계산 완료 — ranked:', ranked.length, '/', totalVessels, '| top:', ranked[0]?.name, ranked[0]?.probability);
    const replayShips = buildReplayShipsFromApi(ranked);
    const collisionEvent = findCollisionEventFromApi(ranked, analysisStart, spillTime);

    const timeRange = {
      start: analysisStart.toISOString(),
      end: spillTime.toISOString(),
    };

    // vessels에서 내부 필드 제거
    const vessels = ranked.map(({ _rawScore: _r, _track: _t, _minDistIdx: _m, ...v }) => v);

    // rawResult 샘플링 (최대 24 스텝, 파티클은 [lon, lat] 형식으로 저장)
    const MAX_BACKWARD_STEPS = 24;
    const sampleRate = Math.max(1, Math.ceil(rawResult.length / MAX_BACKWARD_STEPS));
    const backwardParticles = rawResult
      .filter((_, i) => i % sampleRate === 0)
      .slice(0, MAX_BACKWARD_STEPS)
      .map(step => step.particles.map(p => [p.lon, p.lat] as [number, number]));

    const rsltData = { vessels, replayShips, collisionEvent, timeRange, backwardParticles };

    await wingPool.query(
      `UPDATE wing.BACKTRACK
       SET EXEC_STTS_CD='COMPLETED', RSLT_DATA=$1, TOTAL_VESSELS=$2
       WHERE BACKTRACK_SN=$3`,
      [JSON.stringify(rsltData), totalVessels, backtrackSn],
    );

    console.info(`[backtrack] 분석 완료 SN=${backtrackSn}, 후보선박=${ranked.length}/${totalVessels}`);
  } catch (err) {
    console.error('[backtrack] 분석 실패:', err);
    const errMsg = err instanceof Error ? err.message : '알 수 없는 오류';
    await wingPool.query(
      `UPDATE wing.BACKTRACK SET EXEC_STTS_CD='FAILED', RSLT_DATA=$1 WHERE BACKTRACK_SN=$2`,
      [JSON.stringify({ error: errMsg }), backtrackSn],
    );
  }
}
