import { MediaType } from '../../types'
import type { Detection, Media } from '../../types'
import { getClassColor } from '../../class-colors'

const THUMB_MAX = 240
const CROP_PAD = 0.15
const FULL_FRAME_MAX = 1280

async function getSourceCanvas(
  media: Media,
  videoEl: HTMLVideoElement | null,
): Promise<{ canvas: HTMLCanvasElement; w: number; h: number } | null> {
  const canvas = document.createElement('canvas')

  if (media.mediaType === MediaType.Video && videoEl && videoEl.videoWidth) {
    const w = videoEl.videoWidth
    const h = videoEl.videoHeight
    canvas.width = w
    canvas.height = h
    canvas.getContext('2d')?.drawImage(videoEl, 0, 0, w, h)
    return { canvas, w, h }
  }

  if (media.mediaType === MediaType.Image) {
    const img = new Image()
    img.crossOrigin = 'anonymous'
    img.src = media.path.startsWith('blob:')
      ? media.path
      : `/api/annotations/media/${media.id}/file`
    await new Promise<void>(resolve => {
      img.onload = () => resolve()
      img.onerror = () => resolve()
    })
    if (!img.naturalWidth) return null
    const w = img.naturalWidth
    const h = img.naturalHeight
    canvas.width = w
    canvas.height = h
    canvas.getContext('2d')?.drawImage(img, 0, 0, w, h)
    return { canvas, w, h }
  }

  return null
}

export interface ThumbnailCapture {
  fullFrame: string
  detectionThumbnails: string[]
}

export async function captureThumbnails(
  media: Media,
  videoEl: HTMLVideoElement | null,
  detections: Detection[],
): Promise<ThumbnailCapture> {
  const src = await getSourceCanvas(media, videoEl)
  if (!src) return { fullFrame: '', detectionThumbnails: detections.map(() => '') }

  const fullScale = Math.min(1, FULL_FRAME_MAX / src.w)
  const full = document.createElement('canvas')
  full.width = Math.max(1, Math.round(src.w * fullScale))
  full.height = Math.max(1, Math.round(src.h * fullScale))
  full.getContext('2d')?.drawImage(src.canvas, 0, 0, full.width, full.height)
  const fullFrame = full.toDataURL('image/jpeg', 0.85)

  const detectionThumbnails = detections.map(d => cropDetection(src, d))

  return { fullFrame, detectionThumbnails }
}

function cropDetection(
  src: { canvas: HTMLCanvasElement; w: number; h: number },
  d: Detection,
): string {
  const cxPx = d.centerX * src.w
  const cyPx = d.centerY * src.h
  const bw = d.width * src.w
  const bh = d.height * src.h
  const side = Math.max(bw, bh) * (1 + CROP_PAD * 2)

  const sx = cxPx - side / 2
  const sy = cyPx - side / 2

  const ix0 = Math.max(0, Math.floor(sx))
  const iy0 = Math.max(0, Math.floor(sy))
  const ix1 = Math.min(src.w, Math.ceil(sx + side))
  const iy1 = Math.min(src.h, Math.ceil(sy + side))
  const iw = Math.max(1, ix1 - ix0)
  const ih = Math.max(1, iy1 - iy0)

  const out = document.createElement('canvas')
  out.width = THUMB_MAX
  out.height = THUMB_MAX
  const ctx = out.getContext('2d')
  if (ctx) {
    ctx.fillStyle = '#1e1e1e'
    ctx.fillRect(0, 0, THUMB_MAX, THUMB_MAX)

    const scale = THUMB_MAX / side
    ctx.drawImage(
      src.canvas,
      ix0, iy0, iw, ih,
      (ix0 - sx) * scale, (iy0 - sy) * scale, iw * scale, ih * scale,
    )

    const bx = cxPx - bw / 2
    const by = cyPx - bh / 2
    ctx.strokeStyle = getClassColor(d.classNum)
    ctx.lineWidth = Math.max(2, THUMB_MAX / 100)
    ctx.strokeRect(
      (bx - sx) * scale,
      (by - sy) * scale,
      bw * scale,
      bh * scale,
    )
  }
  return out.toDataURL('image/jpeg', 0.8)
}

export async function recaptureThumbnails(
  fullFrameDataUrl: string,
  detections: Detection[],
): Promise<string[]> {
  if (!fullFrameDataUrl) return detections.map(() => '')
  const img = new Image()
  img.src = fullFrameDataUrl
  await new Promise<void>(resolve => {
    img.onload = () => resolve()
    img.onerror = () => resolve()
  })
  if (!img.naturalWidth) return detections.map(() => '')
  const canvas = document.createElement('canvas')
  canvas.width = img.naturalWidth
  canvas.height = img.naturalHeight
  canvas.getContext('2d')?.drawImage(img, 0, 0)
  const src = { canvas, w: canvas.width, h: canvas.height }
  return detections.map(d => cropDetection(src, d))
}
