/**
 * 오일 유출 감지 — GPU 추론 서버 API 연동
 *
 * 시립대(starsafire) ResNet101+DANet 모델 기반
 * 프레임 캡처 → base64 JPEG → POST /api/aerial/oil-detect → 세그멘테이션 결과
 *
 * 5개 클래스: background(0), black(1), brown(2), rainbow(3), silver(4)
 */

import { api } from '@common/services/api';

// ── Types ──────────────────────────────────────────────────────────────────

export interface OilDetectionConfig {
  captureIntervalMs: number; // API 호출 주기 (ms), default 5000
  coverageAreaM2: number; // 카메라 커버리지 면적 (m²), default 10000
  captureWidth: number; // 캡처 해상도 (너비), default 512
}

/** 유류 클래스 정의 */
export interface OilClass {
  classId: number;
  className: string;
  color: [number, number, number]; // RGB
  thicknessMm: number;
}

/** 개별 유류 영역 (API 응답에서 변환) */
export interface OilRegion {
  classId: number;
  className: string;
  pixelCount: number;
  percentage: number;
  areaM2: number;
  thicknessMm: number;
}

/** 감지 결과 (오버레이에서 사용) */
export interface OilDetectionResult {
  regions: OilRegion[];
  totalPercentage: number;
  totalAreaM2: number;
  mask: Uint8Array; // 클래스 인덱스 (0-4)
  maskWidth: number;
  maskHeight: number;
  timestamp: number;
}

// ── Constants ──────────────────────────────────────────────────────────────

export const DEFAULT_OIL_DETECTION_CONFIG: OilDetectionConfig = {
  captureIntervalMs: 5000,
  coverageAreaM2: 10000,
  captureWidth: 512,
};

/** 유류 클래스 팔레트 (시립대 starsafire 기준) */
export const OIL_CLASSES: OilClass[] = [
  { classId: 1, className: 'black', color: [0, 0, 204], thicknessMm: 1.0 },
  { classId: 2, className: 'brown', color: [180, 180, 180], thicknessMm: 0.1 },
  { classId: 3, className: 'rainbow', color: [255, 255, 0], thicknessMm: 0.0003 },
  { classId: 4, className: 'silver', color: [178, 102, 255], thicknessMm: 0.0001 },
];

export const OIL_CLASS_NAMES: Record<number, string> = {
  1: '에멀전(Black)',
  2: '원유(Brown)',
  3: '무지개막(Rainbow)',
  4: '은색막(Silver)',
};

// ── Frame Capture ──────────────────────────────────────────────────────────

/**
 * 비디오 프레임을 캡처하여 base64 JPEG 문자열로 반환한다.
 */
export function captureFrameAsBase64(video: HTMLVideoElement, targetWidth: number): string | null {
  if (video.readyState < 2 || video.videoWidth === 0) return null;

  const aspect = video.videoHeight / video.videoWidth;
  const w = targetWidth;
  const h = Math.round(w * aspect);

  try {
    const canvas = document.createElement('canvas');
    canvas.width = w;
    canvas.height = h;
    const ctx = canvas.getContext('2d');
    if (!ctx) return null;
    ctx.drawImage(video, 0, 0, w, h);
    // data:image/jpeg;base64,... → base64 부분만 추출
    const dataUrl = canvas.toDataURL('image/jpeg', 0.85);
    return dataUrl.split(',')[1] || null;
  } catch {
    return null;
  }
}

// ── API Inference ──────────────────────────────────────────────────────────

interface ApiInferenceRegion {
  classId: number;
  className: string;
  pixelCount: number;
  percentage: number;
  thicknessMm: number;
}

interface ApiInferenceResponse {
  mask: string; // base64 uint8 array
  width: number;
  height: number;
  regions: ApiInferenceRegion[];
}

/**
 * GPU 추론 서버에 프레임을 전송하고 오일 감지 결과를 반환한다.
 */
export async function detectOilSpillAPI(
  video: HTMLVideoElement,
  config: OilDetectionConfig,
): Promise<OilDetectionResult | null> {
  const imageBase64 = captureFrameAsBase64(video, config.captureWidth);
  if (!imageBase64) return null;

  const response = await api.post<ApiInferenceResponse>('/aerial/oil-detect', {
    image: imageBase64,
  });

  const { mask: maskB64, width, height, regions: apiRegions } = response.data;
  const totalPixels = width * height;

  // base64 → Uint8Array
  const binaryStr = atob(maskB64);
  const mask = new Uint8Array(binaryStr.length);
  for (let i = 0; i < binaryStr.length; i++) {
    mask[i] = binaryStr.charCodeAt(i);
  }

  // API 영역 → OilRegion 변환 (면적 계산 포함)
  const regions: OilRegion[] = apiRegions.map((r) => ({
    classId: r.classId,
    className: r.className,
    pixelCount: r.pixelCount,
    percentage: r.percentage,
    areaM2: (r.pixelCount / totalPixels) * config.coverageAreaM2,
    thicknessMm: r.thicknessMm,
  }));

  const totalPercentage = regions.reduce((sum, r) => sum + r.percentage, 0);
  const totalAreaM2 = regions.reduce((sum, r) => sum + r.areaM2, 0);

  return {
    regions,
    totalPercentage,
    totalAreaM2,
    mask,
    maskWidth: width,
    maskHeight: height,
    timestamp: Date.now(),
  };
}
