/* tdd-guard:skip — client form composition, verified via gstack-smoke + Vercel-Preview (KAR-344/662) */
'use client'

import * as React from 'react'
import { useRouter } from 'next/navigation'
import { Save, ImagePlus, Plus, Trash2, ChevronLeft, ChevronRight, FileDown } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { NoteCanvas } from '@/components/notes/note-canvas'
import {
  NoteAttachmentAnnotator,
  type AttachmentItem,
} from '@/components/notes/note-attachment-annotator'
import { createNote, updateNote, uploadAttachment, saveNotePdf } from '@/app/notes/actions'
import { buildNotePdf, blobToBase64, downloadBlob } from '@/lib/notes/pdf'
import { type NoteCanvasData, emptyCanvas } from '@/lib/notes/canvas'
import {
  type NoteDocument,
  type NoteOrientation,
  type NoteBackground,
  ORIENTATIONS,
  BACKGROUNDS,
  emptyDocument,
  parseDocument,
  pageDimensions,
  mmToPx,
} from '@/lib/notes/document'

export interface NoteProjectOption {
  id: string
  label: string
}

export interface ExistingNote {
  id: string
  project_id: string
  title: string | null
  body_text: string | null
  canvas_data: unknown
}

interface NoteEditorProps {
  projects: NoteProjectOption[]
  note?: ExistingNote
  attachments?: AttachmentItem[]
}

const SELECT_CLASS =
  'flex h-11 w-full rounded-sm border border-border bg-card px-3 text-sm text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none disabled:opacity-50'

export function NoteEditor({ projects, note, attachments = [] }: NoteEditorProps) {
  const router = useRouter()
  const fileInputRef = React.useRef<HTMLInputElement | null>(null)
  const [uploading, setUploading] = React.useState(false)
  const [projectId, setProjectId] = React.useState(note?.project_id ?? '')
  const [title, setTitle] = React.useState(note?.title ?? '')
  const [body, setBody] = React.useState(note?.body_text ?? '')
  const [doc, setDoc] = React.useState<NoteDocument>(
    note ? parseDocument(note.canvas_data) : emptyDocument(),
  )
  const [pageIndex, setPageIndex] = React.useState(0)
  const [pending, setPending] = React.useState(false)
  const [error, setError] = React.useState<string | null>(null)
  const [saved, setSaved] = React.useState(false)
  const [pdfStatus, setPdfStatus] = React.useState<'idle' | 'rendering' | 'done' | 'failed'>('idle')
  const [pdfError, setPdfError] = React.useState<string | null>(null)

  const dims = pageDimensions(doc.orientation)
  const gridPx = mmToPx(doc.gridMm)
  const safeIndex = Math.min(pageIndex, doc.pages.length - 1)

  function updateCurrentPage(next: NoteCanvasData) {
    setSaved(false)
    setDoc((d) => ({ ...d, pages: d.pages.map((p, i) => (i === safeIndex ? next : p)) }))
  }

  function addPage() {
    setDoc((d) => ({ ...d, pages: [...d.pages, emptyCanvas()] }))
    setPageIndex(doc.pages.length)
    setSaved(false)
  }

  function deletePage() {
    if (doc.pages.length <= 1) return
    setDoc((d) => ({ ...d, pages: d.pages.filter((_, i) => i !== safeIndex) }))
    setPageIndex(Math.max(0, safeIndex - 1))
    setSaved(false)
  }

  async function handleSave() {
    setError(null)
    setSaved(false)
    setPdfStatus('idle')
    setPdfError(null)
    if (!projectId) {
      setError('Bitte ein Projekt wählen.')
      return
    }
    setPending(true)
    const payload = { projectId, title, bodyText: body, canvasData: doc }
    const res = note ? await updateNote(note.id, payload) : await createNote(payload)

    if (!res.ok) {
      setPending(false)
      setError(res.error)
      return
    }

    const savedId = note
      ? note.id
      : res.data && typeof res.data === 'object' && 'id' in res.data
        ? String((res.data as { id: string }).id)
        : null

    // Auto-render the PDF into the project Datenablage. The note is already
    // persisted; PDF failure is surfaced (with the real error) but never blocks
    // the save. PDF is pure jspdf vector, so it can't hang the spinner.
    let pdfOk = false
    if (savedId) {
      setPdfStatus('rendering')
      try {
        const blob = await buildNotePdf(doc)
        const base64 = await blobToBase64(blob)
        const pdfRes = await saveNotePdf(savedId, projectId, base64, title)
        pdfOk = pdfRes.ok
        setPdfStatus(pdfRes.ok ? 'done' : 'failed')
        if (!pdfRes.ok) setPdfError(pdfRes.error)
      } catch (e) {
        setPdfStatus('failed')
        setPdfError(e instanceof Error ? e.message : String(e))
      }
    }

    setPending(false)
    // New note: navigate to the editable note only once the PDF succeeded, so a
    // PDF failure stays visible here for diagnosis instead of being lost on nav.
    if (note || !pdfOk) {
      setSaved(true)
      return
    }
    router.push(savedId ? `/notes/${savedId}` : '/notes')
  }

  async function handleExport() {
    setError(null)
    try {
      const blob = await buildNotePdf(doc)
      downloadBlob(blob, `${(title || 'Notiz').trim()}.pdf`)
    } catch {
      setError('PDF-Export fehlgeschlagen.')
    }
  }

  async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0]
    e.target.value = ''
    if (!file || !note) return
    setError(null)
    setUploading(true)
    const fd = new FormData()
    fd.append('file', file)
    const res = await uploadAttachment(note.id, fd)
    setUploading(false)
    if (!res.ok) {
      setError(res.error)
      return
    }
    // The new attachment's signed URL is produced server-side → re-validate.
    router.refresh()
  }

  return (
    <div className="max-w-screen-lg mx-auto px-4 py-6 space-y-5">
      <div className="grid gap-4 sm:grid-cols-2">
        <div className="space-y-1.5">
          <Label htmlFor="note-project">Projekt *</Label>
          <select
            id="note-project"
            value={projectId}
            onChange={(e) => setProjectId(e.target.value)}
            disabled={Boolean(note)}
            className={SELECT_CLASS}
          >
            <option value="">— Projekt wählen —</option>
            {projects.map((p) => (
              <option key={p.id} value={p.id}>
                {p.label}
              </option>
            ))}
          </select>
        </div>
        <div className="space-y-1.5">
          <Label htmlFor="note-title">Titel</Label>
          <Input
            id="note-title"
            value={title}
            onChange={(e) => setTitle(e.target.value)}
            placeholder="z.B. Wertstrom Linie 3"
            className="h-11"
          />
        </div>
      </div>

      <div className="space-y-1.5">
        <Label htmlFor="note-body">Text</Label>
        <textarea
          id="note-body"
          value={body}
          onChange={(e) => setBody(e.target.value)}
          rows={3}
          placeholder="Notiz, Kontext, Beobachtungen…"
          className="flex w-full rounded-sm border border-border bg-card px-3 py-2 text-sm text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
        />
      </div>

      {/* Document controls */}
      <div className="flex flex-wrap items-end gap-3 rounded-md border border-border bg-muted/20 p-3">
        <div className="space-y-1">
          <Label htmlFor="doc-orientation" className="text-xs">
            Format
          </Label>
          <select
            id="doc-orientation"
            value={doc.orientation}
            disabled={pending}
            onChange={(e) =>
              setDoc((d) => ({ ...d, orientation: e.target.value as NoteOrientation }))
            }
            className={`${SELECT_CLASS} w-40`}
          >
            {ORIENTATIONS.map((o) => (
              <option key={o.value} value={o.value}>
                {o.label}
              </option>
            ))}
          </select>
        </div>
        <div className="space-y-1">
          <Label htmlFor="doc-background" className="text-xs">
            Hintergrund
          </Label>
          <select
            id="doc-background"
            value={doc.background}
            disabled={pending}
            onChange={(e) =>
              setDoc((d) => ({ ...d, background: e.target.value as NoteBackground }))
            }
            className={`${SELECT_CLASS} w-36`}
          >
            {BACKGROUNDS.map((b) => (
              <option key={b.value} value={b.value}>
                {b.label}
              </option>
            ))}
          </select>
        </div>
        {doc.background !== 'blank' && (
          <div className="space-y-1">
            <Label htmlFor="doc-grid" className="text-xs">
              Raster (mm)
            </Label>
            <Input
              id="doc-grid"
              type="number"
              min={2}
              max={50}
              value={doc.gridMm}
              disabled={pending}
              onChange={(e) =>
                setDoc((d) => ({
                  ...d,
                  gridMm: Math.min(50, Math.max(2, Number(e.target.value) || d.gridMm)),
                }))
              }
              className="h-11 w-24"
            />
          </div>
        )}
        <div className="ml-auto flex items-center gap-2">
          <Button
            type="button"
            variant="ghost-muted"
            size="sm"
            onClick={() => setPageIndex(Math.max(0, safeIndex - 1))}
            disabled={pending || safeIndex === 0}
            aria-label="Vorherige Seite"
          >
            <ChevronLeft size={16} />
          </Button>
          <span className="text-sm text-muted-foreground tabular-nums">
            Seite {safeIndex + 1} / {doc.pages.length}
          </span>
          <Button
            type="button"
            variant="ghost-muted"
            size="sm"
            onClick={() => setPageIndex(Math.min(doc.pages.length - 1, safeIndex + 1))}
            disabled={pending || safeIndex >= doc.pages.length - 1}
            aria-label="Nächste Seite"
          >
            <ChevronRight size={16} />
          </Button>
          <Button type="button" variant="outline" size="sm" onClick={addPage} disabled={pending}>
            <Plus size={16} />
            Seite
          </Button>
          <Button
            type="button"
            variant="outline"
            size="sm"
            onClick={deletePage}
            disabled={pending || doc.pages.length <= 1}
            aria-label="Seite löschen"
          >
            <Trash2 size={16} />
          </Button>
        </div>
      </div>

      <NoteCanvas
        key={`${doc.orientation}-${safeIndex}`}
        value={doc.pages[safeIndex] ?? emptyCanvas()}
        onChange={updateCurrentPage}
        disabled={pending}
        viewBox={dims}
        background={doc.background}
        gridPx={gridPx}
      />

      {note && (
        <div className="space-y-3">
          <div className="flex items-center justify-between">
            <Label>Fotos &amp; Annotationen</Label>
            <input
              ref={fileInputRef}
              type="file"
              accept="image/*"
              className="hidden"
              onChange={handleUpload}
            />
            <Button
              type="button"
              variant="outline"
              size="lg"
              onClick={() => fileInputRef.current?.click()}
              disabled={uploading}
            >
              <ImagePlus size={16} />
              {uploading ? 'Lädt hoch…' : 'Foto hinzufügen'}
            </Button>
          </div>
          {attachments.length === 0 ? (
            <p className="text-xs text-muted-foreground">
              Noch keine Fotos. Lade ein Bild hoch und annotiere es mit dem Stift.
            </p>
          ) : (
            <div className="space-y-3">
              {attachments.map((a) => (
                <NoteAttachmentAnnotator key={a.id} attachment={a} />
              ))}
            </div>
          )}
        </div>
      )}

      {error && (
        <p role="alert" className="text-sm text-destructive">
          {error}
        </p>
      )}
      {saved && !error && (
        <div className="space-y-1 text-sm">
          <p role="status" className="text-primary">
            Gespeichert.
            {pdfStatus === 'done' && ' PDF liegt in der Datenablage.'}
          </p>
          {pdfStatus === 'failed' && (
            <p role="alert" className="text-destructive">
              PDF/Datenablage fehlgeschlagen{pdfError ? `: ${pdfError}` : '.'}
            </p>
          )}
        </div>
      )}

      <div className="flex items-center justify-end gap-2">
        <Button type="button" variant="ghost" size="lg" onClick={() => router.push('/notes')} disabled={pending}>
          Abbrechen
        </Button>
        <Button type="button" variant="outline" size="lg" onClick={handleExport} disabled={pending}>
          <FileDown size={16} />
          Als PDF exportieren
        </Button>
        <Button type="button" size="lg" onClick={handleSave} disabled={pending}>
          <Save size={16} />
          {pending
            ? pdfStatus === 'rendering'
              ? 'PDF wird erzeugt…'
              : 'Speichert…'
            : 'Speichern'}
        </Button>
      </div>
    </div>
  )
}
