'use server'

import { db } from '@/lib/db'
import { requireSession } from '@/lib/auth'
import { saveMapImage, deleteUpload } from '@/lib/images'
import { createLocation } from '@/lib/locations'
import { slugify } from '@/lib/utils'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'

export async function createMap(formData: FormData) {
  await requireSession()
  const name = (formData.get('name') as string)?.trim()
  const locationId = (formData.get('locationId') as string) || ''
  const image = formData.get('image') as File | null

  if (!name) return { ok: false as const, error: 'Name is required' }
  if (!locationId) return { ok: false as const, error: 'Location is required' }
  if (!image || image.size === 0) return { ok: false as const, error: 'Image is required' }

  const buf = Buffer.from(await image.arrayBuffer())
  const saved = await saveMapImage(buf)

  try {
    const created = await db.map.create({
      data: {
        name,
        imagePath: saved.path,
        width: saved.width,
        height: saved.height,
        locationId,
      },
    })
    revalidatePath('/locations')
    revalidatePath('/locations/maps')
    return { ok: true as const, id: created.id }
  } catch (err) {
    await deleteUpload(saved.path)
    return {
      ok: false as const,
      error: err instanceof Error ? err.message : 'Could not create map',
    }
  }
}

export async function renameMap(mapId: string, name: string) {
  await requireSession()
  const trimmed = name.trim()
  if (!trimmed) return { ok: false as const, error: 'Name is required' }
  await db.map.update({ where: { id: mapId }, data: { name: trimmed } })
  revalidatePath(`/locations/maps/${mapId}`)
  revalidatePath('/locations/maps')
  return { ok: true as const }
}

export async function replaceMapImage(formData: FormData) {
  await requireSession()
  const mapId = formData.get('mapId') as string
  const image = formData.get('image') as File | null
  if (!mapId) return { ok: false as const, error: 'Missing mapId' }
  if (!image || image.size === 0) {
    return { ok: false as const, error: 'Image is required' }
  }

  const existing = await db.map.findUnique({
    where: { id: mapId },
    select: { imagePath: true },
  })
  if (!existing) return { ok: false as const, error: 'Map not found' }

  const buf = Buffer.from(await image.arrayBuffer())
  const saved = await saveMapImage(buf)

  await db.map.update({
    where: { id: mapId },
    data: {
      imagePath: saved.path,
      width: saved.width,
      height: saved.height,
    },
  })

  // Old file is no longer referenced anywhere — pins survive because they're
  // normalized coordinates against the (new) image.
  await deleteUpload(existing.imagePath)

  revalidatePath(`/locations/maps/${mapId}`)
  revalidatePath('/locations/maps')
  return { ok: true as const }
}

export async function deleteMap(mapId: string) {
  await requireSession()
  const m = await db.map.findUnique({ where: { id: mapId } })
  if (!m) return
  await db.map.delete({ where: { id: mapId } })
  await deleteUpload(m.imagePath)
  revalidatePath('/locations')
  revalidatePath('/locations/maps')
  redirect('/locations/maps')
}

// Add a pin or move an existing one (single pin per location per map).
export async function upsertPin(
  mapId: string,
  locationId: string,
  x: number,
  y: number,
) {
  await requireSession()
  const clampedX = Math.max(0, Math.min(1, x))
  const clampedY = Math.max(0, Math.min(1, y))

  const existing = await db.locationPin.findUnique({
    where: { mapId_locationId: { mapId, locationId } },
  })

  if (existing) {
    await db.locationPin.update({
      where: { id: existing.id },
      data: { x: clampedX, y: clampedY },
    })
  } else {
    await db.locationPin.create({
      data: { mapId, locationId, x: clampedX, y: clampedY },
    })
  }
  revalidatePath(`/locations/maps/${mapId}`)
}

export async function deletePin(pinId: string) {
  await requireSession()
  const pin = await db.locationPin.findUnique({ where: { id: pinId } })
  if (!pin) return
  await db.locationPin.delete({ where: { id: pinId } })
  revalidatePath(`/locations/maps/${pin.mapId}`)
}

// Pin a location on the map by name. The new pin always belongs to a *child*
// of the map's owner, so a map can only ever surface places that live inside
// the location it depicts — this is what keeps the map's pins and the owner's
// sublocation list as one set instead of two structures that drift apart. If a
// child with that name already exists it is reused (no duplicate is minted);
// otherwise a fresh child is created. Either way the pin is upserted so
// re-pinning an existing child just repositions it.
export async function createLocationAndPin(
  mapId: string,
  name: string,
  x: number,
  y: number,
) {
  await requireSession()
  const trimmed = name.trim()
  if (!trimmed) return { ok: false as const, error: 'Name is required' }

  const map = await db.map.findUnique({
    where: { id: mapId },
    select: { locationId: true },
  })
  if (!map) return { ok: false as const, error: 'Map not found' }

  // Reuse an existing sublocation with the same name rather than creating a
  // parallel duplicate (the bug that split "Walk In Pantry" from the empty
  // "Walkin Pantry" under House).
  let location: { id: string } | null = await db.location.findFirst({
    where: { parentId: map.locationId, slug: slugify(trimmed) },
    select: { id: true },
  })
  if (!location) {
    try {
      location = await createLocation({
        name: trimmed,
        parentId: map.locationId,
        notes: null,
      })
    } catch (err) {
      return {
        ok: false as const,
        error: err instanceof Error ? err.message : 'Could not create location',
      }
    }
  }

  const clampedX = Math.max(0, Math.min(1, x))
  const clampedY = Math.max(0, Math.min(1, y))
  await db.locationPin.upsert({
    where: { mapId_locationId: { mapId, locationId: location.id } },
    update: { x: clampedX, y: clampedY },
    create: { mapId, locationId: location.id, x: clampedX, y: clampedY },
  })

  revalidatePath(`/locations/maps/${mapId}`)
  revalidatePath('/locations')
  return { ok: true as const, locationId: location.id }
}
