import crypto from 'crypto';
import { wingPool } from '../db/wingDb.js';
import { createMedia } from '../aerial/aerialService.js';

const IMAGE_API_URL = process.env.IMAGE_API_URL ?? 'http://localhost:5001';

// 유류 클래스 → UI 유종명 매핑
const CLASS_ID_TO_OIL_TYPE: Record<string, string> = {
  '검정': '벙커C유',
  '갈색': '벙커C유',
  '무지개': '경유',
  '은색': '등유',
};

// 유종명 → DB 코드 매핑
const OIL_DB_CODE_MAP: Record<string, string> = {
  '벙커C유': 'BUNKER_C',
  '원유': 'CRUDE_OIL',
  '경유': 'DIESEL',
  '등유': 'GASOLINE',
};

interface OilPolygon {
  classId: string;
  area: number;
  volume: number;
  note: string;
  thickness: number;
  wkt: string;
}

interface ImageServerResponse {
  meta: string;
  data: OilPolygon[];
}

export interface ImageAnalyzeResult {
  acdntSn: number;
  lat: number;
  lon: number;
  oilType: string;
  area: number;
  volume: number;
  fileId: string;
  occurredAt: string;
}

/**
 * mx15hdi CSV 컬럼 순서:
 * Filename, Tlat_d, Tlat_m, Tlat_s, Tlon_d, Tlon_m, Tlon_s,
 * Alat_d, Alat_m, Alat_s, Alon_d, Alon_m, Alon_s,
 * Az, El, Alt, Date1, Date2, Date3, Time1, Time2, Time3
 */
function parseMeta(metaStr: string): { lat: number; lon: number; occurredAt: string } {
  const parts = metaStr.split(',');
  const tlat_d = parseFloat(parts[1]);
  const tlat_m = parseFloat(parts[2]);
  const tlat_s = parseFloat(parts[3]);
  const tlon_d = parseFloat(parts[4]);
  const tlon_m = parseFloat(parts[5]);
  const tlon_s = parseFloat(parts[6]);

  const lat = tlat_d + tlat_m / 60 + tlat_s / 3600;
  const lon = tlon_d + tlon_m / 60 + tlon_s / 3600;

  // Date: Date1(DD), Date2(MM), Date3(YYYY) / Time: Time1(HH), Time2(mm), Time3(ss)
  const dd   = (parts[16] ?? '01').padStart(2, '0');
  const mm   = (parts[17] ?? '01').padStart(2, '0');
  const yyyy = parts[18] ?? new Date().getFullYear().toString();
  const time1 = (parts[19] ?? '00').padStart(2, '0');
  const time2 = (parts[20] ?? '00').padStart(2, '0');
  const occurredAt = `${yyyy}-${mm}-${dd}T${time1}:${time2}:00+09:00`;

  return { lat, lon, occurredAt };
}

export async function analyzeImageFile(imageBuffer: Buffer, originalName: string, acdntNmOverride?: string): Promise<ImageAnalyzeResult> {
  const fileId = crypto.randomUUID();

  // camTy는 현재 "mx15hdi"로 하드코딩한다.
  // TODO: 추후 이미지 EXIF에서 카메라 모델명을 읽어 camTy를 자동 판별하는 로직을
  //       이미지 분석 서버(api.py)에 추가할 예정이다. (check_camera_info 함수 활용)
  const camTy = 'mx15hdi';

  // 이미지 분석 서버 호출
  const formData = new FormData();
  const blob = new Blob([imageBuffer]);
  formData.append('camTy', camTy);
  formData.append('fileId', fileId);
  formData.append('image', blob, originalName);

  let serverResponse: ImageServerResponse;
  try {
    const res = await fetch(`${IMAGE_API_URL}/run-script/`, {
      method: 'POST',
      body: formData,
      signal: AbortSignal.timeout(300_000),
    });

    if (!res.ok) {
      const text = await res.text();
      if (res.status === 400 && text.includes('GPS')) {
        throw Object.assign(new Error('GPS_NOT_FOUND'), { code: 'GPS_NOT_FOUND' });
      }
      throw new Error(`이미지 분석 서버 오류: ${res.status} - ${text}`);
    }

    serverResponse = await res.json() as ImageServerResponse;
  } catch (err: unknown) {
    if (err instanceof Error && (err as NodeJS.ErrnoException).code === 'GPS_NOT_FOUND') throw err;
    if (err instanceof Error && err.name === 'TimeoutError') {
      throw Object.assign(new Error('TIMEOUT'), { code: 'TIMEOUT' });
    }
    throw err;
  }

  // 응답 파싱
  const { lat, lon, occurredAt } = parseMeta(serverResponse.meta);
  const firstOil = serverResponse.data[0];
  const oilType = firstOil ? (CLASS_ID_TO_OIL_TYPE[firstOil.classId] ?? '벙커C유') : '벙커C유';
  const area = firstOil?.area ?? 0;
  const volume = firstOil?.volume ?? 0;

  // ACDNT INSERT
  const acdntNm = acdntNmOverride?.trim() || `이미지분석_${new Date().toISOString().slice(0, 16).replace('T', ' ')}`;
  const acdntRes = await wingPool.query(
    `INSERT INTO wing.ACDNT
       (ACDNT_CD, ACDNT_NM, ACDNT_TP_CD, OCCRN_DTM, LAT, LNG, ACDNT_STTS_CD, USE_YN, REG_DTM)
     VALUES (
       'INC-' || EXTRACT(YEAR FROM NOW())::TEXT || '-' ||
       LPAD(
         (SELECT COALESCE(MAX(CAST(SPLIT_PART(ACDNT_CD, '-', 3) AS INTEGER)), 0) + 1
          FROM wing.ACDNT
          WHERE ACDNT_CD LIKE 'INC-' || EXTRACT(YEAR FROM NOW())::TEXT || '-%')::TEXT,
         4, '0'
       ),
       $1, '유류유출', $2, $3, $4, 'ACTIVE', 'Y', NOW()
     )
     RETURNING ACDNT_SN`,
    [acdntNm, occurredAt, lat, lon]
  );
  const acdntSn: number = acdntRes.rows[0].acdnt_sn;

  // SPIL_DATA INSERT (img_rslt_data에 분석 원본 저장)
  await wingPool.query(
    `INSERT INTO wing.SPIL_DATA
       (ACDNT_SN, OIL_TP_CD, SPIL_QTY, SPIL_UNIT_CD, SPIL_TP_CD, FCST_HR, IMG_RSLT_DATA, REG_DTM)
     VALUES ($1, $2, $3, 'KL', 'CONTINUOUS', 6, $4, NOW())`,
    [
      acdntSn,
      OIL_DB_CODE_MAP[oilType] ?? 'BUNKER_C',
      volume,
      JSON.stringify(serverResponse),
    ]
  );

  // AERIAL_MEDIA INSERT (영상사진관리 목록에서 조회 가능하도록 저장)
  const fileSizeMb = (imageBuffer.length / (1024 * 1024)).toFixed(1) + ' MB';
  await createMedia({
    fileNm: `${fileId}_${originalName}`,
    orgnlNm: originalName,
    acdntSn,
    lon,
    lat,
    locDc: `${lon.toFixed(4)} + ${lat.toFixed(4)}`,
    equipTpCd: 'drone',
    equipNm: camTy,
    mediaTpCd: '사진',
    takngDtm: occurredAt,
    fileSz: fileSizeMb,
  });

  return { acdntSn, lat, lon, oilType, area, volume, fileId, occurredAt };
}
