'use server'

import { revalidatePath } from 'next/cache'
import { z } from 'zod'
import { createClient } from '@/lib/supabase/server'
import { parseCanvasData } from '@/lib/notes/canvas'
import { parseDocument } from '@/lib/notes/document'
import { buildStorageKey, validateAttachment, sanitizeFilename } from '@/lib/notes/attachments'

const ATTACHMENT_BUCKET = 'project-notes'

// Lenient UUID shape (any 8-4-4-4-12 hex). NOT strict RFC-4122 v4: seeded demo
// rows use non-v4 ids (e.g. the G61-KDK demo project '…1111111111aa'), and the
// real validity is enforced by the FK + RLS at insert time anyway.
const uuidSchema = z
  .string()
  .regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, 'invalid uuid')

export type ActionResult<T = unknown> =
  | { ok: true; data?: T }
  | { ok: false; error: string }

const noteInputSchema = z.object({
  projectId: uuidSchema,
  title: z.string().trim().max(200).optional().default(''),
  bodyText: z.string().max(20000).optional().default(''),
  // Normalized below via parseDocument (full A4 document); accept anything here.
  canvasData: z.unknown(),
})

export type NoteInput = z.input<typeof noteInputSchema>

/** Create a project note. RLS enforces the project belongs to the caller. */
export async function createNote(raw: NoteInput): Promise<ActionResult<{ id: string }>> {
  const parsed = noteInputSchema.safeParse(raw)
  if (!parsed.success) return { ok: false, error: 'invalid input' }
  const { projectId, title, bodyText, canvasData } = parsed.data

  const supabase = await createClient()
  const { data: claims } = await supabase.auth.getClaims()
  if (!claims?.claims) return { ok: false, error: 'unauthorized' }
  const userId = claims.claims.sub

  const { data, error } = await supabase
    .from('project_notes')
    .insert({
      project_id: projectId,
      user_id: userId,
      title: title || null,
      body_text: bodyText || null,
      canvas_data: parseDocument(canvasData),
    })
    .select('id')
    .single()

  if (error || !data) return { ok: false, error: error?.message ?? 'insert failed' }

  revalidatePath('/notes')
  return { ok: true, data: { id: data.id } }
}

/** Update an existing project note. RLS scopes the row to the caller's project. */
export async function updateNote(id: string, raw: NoteInput): Promise<ActionResult> {
  if (!uuidSchema.safeParse(id).success) return { ok: false, error: 'invalid id' }
  const parsed = noteInputSchema.safeParse(raw)
  if (!parsed.success) return { ok: false, error: 'invalid input' }
  const { projectId, title, bodyText, canvasData } = parsed.data

  const supabase = await createClient()
  const { data: claims } = await supabase.auth.getClaims()
  if (!claims?.claims) return { ok: false, error: 'unauthorized' }

  const { error } = await supabase
    .from('project_notes')
    .update({
      project_id: projectId,
      title: title || null,
      body_text: bodyText || null,
      canvas_data: parseDocument(canvasData),
      updated_at: new Date().toISOString(),
    })
    .eq('id', id)

  if (error) return { ok: false, error: error.message }

  revalidatePath('/notes')
  revalidatePath(`/notes/${id}`)
  return { ok: true }
}

/** Upload an image attachment to a note. RLS scopes storage + row to the owner. */
export async function uploadAttachment(
  noteId: string,
  formData: FormData,
): Promise<ActionResult<{ id: string }>> {
  if (!uuidSchema.safeParse(noteId).success) return { ok: false, error: 'invalid id' }
  const file = formData.get('file')
  if (!(file instanceof File)) return { ok: false, error: 'no file' }

  const valid = validateAttachment({ size: file.size, type: file.type })
  if (!valid.ok) return { ok: false, error: valid.error }

  const supabase = await createClient()
  const { data: claims } = await supabase.auth.getClaims()
  if (!claims?.claims) return { ok: false, error: 'unauthorized' }

  const key = buildStorageKey(noteId, crypto.randomUUID(), file.name)
  const { error: upErr } = await supabase.storage
    .from(ATTACHMENT_BUCKET)
    .upload(key, file, { contentType: file.type, upsert: false })
  if (upErr) return { ok: false, error: upErr.message }

  const { data, error } = await supabase
    .from('project_note_attachments')
    .insert({ note_id: noteId, storage_key: key, mime_type: file.type })
    .select('id')
    .single()
  if (error || !data) {
    // Roll back the orphaned object so storage doesn't leak on a failed insert.
    await supabase.storage.from(ATTACHMENT_BUCKET).remove([key])
    return { ok: false, error: error?.message ?? 'insert failed' }
  }

  revalidatePath(`/notes/${noteId}`)
  return { ok: true, data: { id: data.id } }
}

/** Persist the SVG annotation overlay (canvas_data shape) for an attachment. */
export async function saveAttachmentAnnotations(
  attachmentId: string,
  annotations: unknown,
): Promise<ActionResult> {
  if (!uuidSchema.safeParse(attachmentId).success) return { ok: false, error: 'invalid id' }
  const supabase = await createClient()
  const { data: claims } = await supabase.auth.getClaims()
  if (!claims?.claims) return { ok: false, error: 'unauthorized' }

  const { error } = await supabase
    .from('project_note_attachments')
    .update({ annotations: parseCanvasData(annotations) })
    .eq('id', attachmentId)
  if (error) return { ok: false, error: error.message }
  return { ok: true }
}

/** Delete an attachment row and its backing storage object. */
export async function deleteAttachment(attachmentId: string): Promise<ActionResult> {
  if (!uuidSchema.safeParse(attachmentId).success) return { ok: false, error: 'invalid id' }
  const supabase = await createClient()
  const { data: claims } = await supabase.auth.getClaims()
  if (!claims?.claims) return { ok: false, error: 'unauthorized' }

  const { data: row } = await supabase
    .from('project_note_attachments')
    .select('storage_key, note_id')
    .eq('id', attachmentId)
    .maybeSingle()
  if (!row) return { ok: false, error: 'not found' }

  await supabase.storage.from(ATTACHMENT_BUCKET).remove([row.storage_key])
  const { error } = await supabase.from('project_note_attachments').delete().eq('id', attachmentId)
  if (error) return { ok: false, error: error.message }

  revalidatePath(`/notes/${row.note_id}`)
  return { ok: true }
}

const DOCUMENTS_BUCKET = 'documents'

/**
 * Render the note's PDF into the project's Datenablage (`documents`). One PDF
 * per note (deterministic storage key), so re-saving overwrites + updates the
 * existing row rather than piling up duplicates.
 */
export async function saveNotePdf(
  noteId: string,
  projectId: string,
  pdfBase64: string,
  title: string,
): Promise<ActionResult> {
  if (!uuidSchema.safeParse(noteId).success || !uuidSchema.safeParse(projectId).success) {
    return { ok: false, error: 'invalid id' }
  }
  if (typeof pdfBase64 !== 'string' || pdfBase64.length === 0) {
    return { ok: false, error: 'no pdf' }
  }

  const supabase = await createClient()
  const { data: claims } = await supabase.auth.getClaims()
  if (!claims?.claims) return { ok: false, error: 'unauthorized' }
  const userId = claims.claims.sub

  const bytes = Buffer.from(pdfBase64, 'base64')
  const docTitle = (title || '').trim() || 'Notiz'
  const filename = `${sanitizeFilename(docTitle)}.pdf`
  const storageKey = `${DOCUMENTS_BUCKET}/${projectId}/note-${noteId}.pdf`

  const { error: upErr } = await supabase.storage
    .from(DOCUMENTS_BUCKET)
    .upload(storageKey, bytes, { contentType: 'application/pdf', upsert: true })
  if (upErr) return { ok: false, error: upErr.message }

  const { data: existing } = await supabase
    .from('documents')
    .select('id')
    .eq('storage_key', storageKey)
    .maybeSingle()

  if (existing) {
    const { error } = await supabase
      .from('documents')
      .update({
        title: docTitle,
        original_filename: filename,
        file_size: bytes.length,
        mime_type: 'application/pdf',
        updated_at: new Date().toISOString(),
      })
      .eq('id', existing.id)
    if (error) return { ok: false, error: error.message }
  } else {
    const { error } = await supabase.from('documents').insert({
      repository_type: 'project',
      project_id: projectId,
      storage_key: storageKey,
      original_filename: filename,
      title: docTitle,
      document_type: 'note',
      mime_type: 'application/pdf',
      file_size: bytes.length,
      uploaded_by: userId,
      access_scope: 'project',
    })
    if (error) return { ok: false, error: error.message }
  }

  revalidatePath('/repository')
  revalidatePath(`/notes/${noteId}`)
  return { ok: true }
}
