import 'server-only'

import { createAdminClient } from '@/lib/supabase/admin'
import { VEHICLE_PHOTO_SIDES, type VehiclePhotoSide } from '@/lib/domain/pilot'
import { deleteS3Object, getSignedS3Url, isWorkspaceS3Key, S3_PREFIX, uploadFileToS3 } from '@/lib/storage/s3'

/**
 * Pilot Dispatch Dashboard → Units → the four side photos (2026-09-19).
 * Drag & drop per side; dropping on a filled slot overwrites; remove;
 * every change logged with who did it. Tolerates migration 0023 not being
 * applied (photos read as absent, the page says why).
 */

const LEGACY_BUCKET = 'pilot-unit-photos'
const TTL = 60 * 30
const MAX_BYTES = 15 * 1024 * 1024
const ALLOWED = new Set(['image/jpeg', 'image/png', 'image/webp', 'image/heic', 'image/heif'])
const missing = (e: { message?: string } | null | undefined) => !!e && /does not exist|schema cache|not found/i.test(e.message ?? '')

export interface UnitPhoto {
  id: string
  side: VehiclePhotoSide
  url: string | null
  fileName: string
  sizeBytes: number | null
  uploadedBy: string
  uploadedAt: string
}
export interface UnitPhotoLogEntry {
  id: string
  side: string
  action: 'uploaded' | 'replaced' | 'removed'
  actor: string
  fileName: string | null
  previousFileName: string | null
  at: string
}
export interface UnitPhotos {
  /** False until migration 0023 is applied. */
  available: boolean
  /** Keyed `${unitId}:${side}`. */
  photos: Record<string, UnitPhoto>
  /** Newest first, keyed by unit id. */
  log: Record<string, UnitPhotoLogEntry[]>
}

export function isPhotoSide(v: unknown): v is VehiclePhotoSide {
  return typeof v === 'string' && (VEHICLE_PHOTO_SIDES as readonly string[]).includes(v)
}

export async function loadUnitPhotos(ownerUserId: string): Promise<UnitPhotos> {
  const admin = createAdminClient()
  const [rows, log] = await Promise.all([
    admin.from('pilot_unit_photos').select('*').eq('owner_user_id', ownerUserId).eq('status', 'active'),
    admin.from('pilot_unit_photo_log').select('*').eq('owner_user_id', ownerUserId).order('created_at', { ascending: false }).limit(300),
  ])
  if (missing(rows.error) || missing(log.error)) return { available: false, photos: {}, log: {} }
  const photos: Record<string, UnitPhoto> = {}
  for (const r of rows.data ?? []) {
    const url = isWorkspaceS3Key(r.storage_path)
      ? await getSignedS3Url(r.storage_path, TTL).catch(() => null)
      : (await admin.storage.from(LEGACY_BUCKET).createSignedUrl(r.storage_path, TTL)).data?.signedUrl ?? null
    photos[`${r.unit_id}:${r.side}`] = { id: r.id, side: r.side, url, fileName: r.file_name, sizeBytes: r.size_bytes, uploadedBy: r.uploaded_by_label, uploadedAt: r.uploaded_at }
  }
  const byUnit: Record<string, UnitPhotoLogEntry[]> = {}
  for (const e of log.data ?? []) {
    ;(byUnit[e.unit_id] ??= []).push({ id: e.id, side: e.side, action: e.action, actor: e.actor_label, fileName: e.file_name, previousFileName: e.previous_file_name, at: e.created_at })
  }
  return { available: true, photos, log: byUnit }
}

/** Upload a side photo; a photo already in that slot is overwritten (kept as `replaced` in the table, file deleted) and the replacement is logged. */
export async function putUnitPhoto(params: { ownerUserId: string; unitId: string; side: VehiclePhotoSide; file: File; actor: { id: string; label: string } }) {
  const { file } = params
  if (file.size === 0 || file.size > MAX_BYTES) return { ok: false as const, error: 'Choose an image up to 15MB.' }
  if (!ALLOWED.has(file.type)) return { ok: false as const, error: 'JPG, PNG, WEBP or HEIC images only.' }
  const admin = createAdminClient()
  const { data: current, error: curErr } = await admin.from('pilot_unit_photos').select('id, storage_path, file_name')
    .eq('owner_user_id', params.ownerUserId).eq('unit_id', params.unitId).eq('side', params.side).eq('status', 'active').maybeSingle()
  if (missing(curErr)) return { ok: false as const, error: 'Unit photos need database migration 0023.' }

  let stored
  try {
    stored = await uploadFileToS3(file, file.name, S3_PREFIX.pilotUnitPhoto)
  } catch (error) {
    console.error('S3 pilot photo upload failed:', error instanceof Error ? error.message : error)
    return { ok: false as const, error: 'Upload failed. Try again.' }
  }

  const now = new Date().toISOString()
  // Overwrite: retire the current photo first (the unique index allows one active per slot), then record the new one.
  if (current) await admin.from('pilot_unit_photos').update({ status: 'replaced', removed_at: now }).eq('id', current.id)
  const { data: inserted, error: insErr } = await admin.from('pilot_unit_photos').insert({
    owner_user_id: params.ownerUserId, unit_id: params.unitId, side: params.side, storage_provider: 's3', storage_path: stored.key, external_url: stored.url, file_name: file.name, mime_type: stored.contentType,
    size_bytes: file.size, uploaded_by: params.actor.id, uploaded_by_label: params.actor.label, uploaded_at: now, status: 'active',
  }).select('id').single()
  if (insErr || !inserted) {
    await deleteS3Object(stored.key).catch(() => undefined)
    if (current) await admin.from('pilot_unit_photos').update({ status: 'active', removed_at: null }).eq('id', current.id)
    return { ok: false as const, error: insErr?.message ?? 'Could not record the photo.' }
  }
  const newId = inserted.id as string
  if (current) {
    await admin.from('pilot_unit_photos').update({ replaced_by: newId }).eq('id', current.id)
    if (isWorkspaceS3Key(current.storage_path)) {
      await deleteS3Object(current.storage_path).catch(() => undefined)
    } else {
      await admin.storage.from(LEGACY_BUCKET).remove([current.storage_path])
    }
  }
  const { data: logRow } = await admin.from('pilot_unit_photo_log').insert({
    owner_user_id: params.ownerUserId, unit_id: params.unitId, side: params.side, action: current ? 'replaced' : 'uploaded',
    actor_user_id: params.actor.id, actor_label: params.actor.label, file_name: file.name, previous_file_name: current?.file_name ?? null, photo_id: newId,
  }).select('*').single()
  const signedUrl = await getSignedS3Url(stored.key, TTL).catch(() => null)
  const photo: UnitPhoto = { id: newId, side: params.side, url: signedUrl, fileName: file.name, sizeBytes: file.size, uploadedBy: params.actor.label, uploadedAt: now }
  const logEntry: UnitPhotoLogEntry = { id: logRow?.id ?? crypto.randomUUID(), side: params.side, action: current ? 'replaced' : 'uploaded', actor: params.actor.label, fileName: file.name, previousFileName: current?.file_name ?? null, at: logRow?.created_at ?? now }
  return { ok: true as const, action: logEntry.action, photo, logEntry }
}

export async function removeUnitPhoto(params: { ownerUserId: string; unitId: string; side: VehiclePhotoSide; actor: { id: string; label: string } }) {
  const admin = createAdminClient()
  const { data: current, error } = await admin.from('pilot_unit_photos').select('id, storage_path, file_name')
    .eq('owner_user_id', params.ownerUserId).eq('unit_id', params.unitId).eq('side', params.side).eq('status', 'active').maybeSingle()
  if (missing(error)) return { ok: false as const, error: 'Unit photos need database migration 0023.' }
  if (!current) return { ok: false as const, error: 'There is no photo in that slot.' }
  const now = new Date().toISOString()
  await admin.from('pilot_unit_photos').update({ status: 'removed', removed_at: now, removed_by_label: params.actor.label }).eq('id', current.id)
  if (isWorkspaceS3Key(current.storage_path)) {
    await deleteS3Object(current.storage_path).catch(() => undefined)
  } else {
    await admin.storage.from(LEGACY_BUCKET).remove([current.storage_path])
  }
  const { data: logRow } = await admin.from('pilot_unit_photo_log').insert({
    owner_user_id: params.ownerUserId, unit_id: params.unitId, side: params.side, action: 'removed',
    actor_user_id: params.actor.id, actor_label: params.actor.label, file_name: null, previous_file_name: current.file_name, photo_id: current.id,
  }).select('*').single()
  const logEntry: UnitPhotoLogEntry = { id: logRow?.id ?? crypto.randomUUID(), side: params.side, action: 'removed', actor: params.actor.label, fileName: null, previousFileName: current.file_name, at: logRow?.created_at ?? now }
  return { ok: true as const, logEntry }
}
