'use server'

import { db } from '@/lib/db'
import { requireSession } from '@/lib/auth'
import { saveItemImage, saveVoiceNote, deleteUpload } from '@/lib/images'
import { resolveLocationPath } from '@/lib/locations'
import { rebuildSearchText } from '@/lib/search'
import { enqueueIdentification } from '@/lib/identify'
import { maybeAutoStartSession } from '@/lib/ai-review-session'
import { revalidatePath } from 'next/cache'

interface CaptureResult {
  ok: true
  itemId: string
  locationId: string
  locationPath: string
}

interface CaptureError {
  ok: false
  error: string
}

export async function captureItem(formData: FormData): Promise<CaptureResult | CaptureError> {
  const session = await requireSession()

  const locationInput = (formData.get('location') as string)?.trim()
  // Multiple angles: client appends one or more `photo` entries. First one is
  // treated as primary. `getAll` preserves order.
  const photos = formData
    .getAll('photo')
    .filter((p): p is File => p instanceof File && p.size > 0)
  const caption = (formData.get('caption') as string)?.trim() || null
  const voice = formData.get('voice') as File | null
  const voiceMime = (formData.get('voiceMime') as string) || 'audio/webm'

  if (!locationInput) return { ok: false, error: 'Location is required' }
  if (photos.length === 0) return { ok: false, error: 'Photo is required' }

  const resolved = await resolveLocationPath(locationInput)
  if (!resolved) return { ok: false, error: 'Could not parse location' }

  const savedImages: Awaited<ReturnType<typeof saveItemImage>>[] = []
  try {
    for (const p of photos) {
      const buf = Buffer.from(await p.arrayBuffer())
      savedImages.push(await saveItemImage(buf))
    }
  } catch (err) {
    for (const s of savedImages) {
      await deleteUpload(s.path)
      await deleteUpload(s.thumbPath)
    }
    return {
      ok: false,
      error: err instanceof Error ? err.message : 'Failed to encode photos',
    }
  }

  let savedVoice: Awaited<ReturnType<typeof saveVoiceNote>> | null = null
  if (voice && voice.size > 0) {
    const voiceBuf = Buffer.from(await voice.arrayBuffer())
    savedVoice = await saveVoiceNote(voiceBuf, voiceMime)
  }

  try {
    const { itemId, primaryPhotoId } = await db.$transaction(async tx => {
      const created = await tx.item.create({
        data: {
          name: 'Untitled item',
          locationId: resolved.id,
          addedById: session.userId,
          caption,
          voiceNotePath: savedVoice?.path ?? null,
          voiceNoteMime: savedVoice?.mime ?? null,
        },
      })
      let primaryId = ''
      for (let i = 0; i < savedImages.length; i++) {
        const s = savedImages[i]
        const row = await tx.photo.create({
          data: {
            itemId: created.id,
            path: s.path,
            thumbPath: s.thumbPath,
            width: s.width,
            height: s.height,
            bytes: s.bytes,
            takenAt: s.takenAt,
            isPrimary: i === 0,
          },
        })
        if (i === 0) primaryId = row.id
      }
      await rebuildSearchText(tx, created.id)
      return { itemId: created.id, primaryPhotoId: primaryId }
    })

    // One identification job per item (on the primary photo). The Phase 2
    // identify pass loads all photos for the item, so additional angles are
    // visible without spawning extra jobs / clutter on /identify.
    await enqueueIdentification(primaryPhotoId)

    // Auto-kick an identify pass once enough captures have piled up, so the
    // field capture loop doesn't depend on remembering to tap "Start AI pass".
    // Fire-and-forget: the ssh launch must not slow down "Save & next".
    void maybeAutoStartSession()

    revalidatePath('/')
    revalidatePath('/identify')
    revalidatePath('/locations')

    return {
      ok: true,
      itemId,
      locationId: resolved.id,
      locationPath: resolved.path,
    }
  } catch (err) {
    for (const s of savedImages) {
      await deleteUpload(s.path)
      await deleteUpload(s.thumbPath)
    }
    if (savedVoice) await deleteUpload(savedVoice.path)
    return {
      ok: false,
      error: err instanceof Error ? err.message : 'Failed to save',
    }
  }
}

export async function resolveLocationOnly(input: string) {
  await requireSession()
  return resolveLocationPath(input)
}
