import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { router, useForm, usePage } from '@inertiajs/react'
import StudioLayout from '../../Layouts/StudioLayout'
import RichTextEditor from '../../components/forum/RichTextEditor'
import WorldMediaUploadField from '../../components/worlds/editor/WorldMediaUploadField'
import NovaSelect from '../../components/ui/NovaSelect'
import { Checkbox } from '../../components/ui'

// ── Minimal toast system ────────────────────────────────────────────────────
let _toastId = 0

function useToast() {
  const [toasts, setToasts] = useState([])

  const push = useCallback((message, type = 'info') => {
    const id = ++_toastId
    setToasts((prev) => [...prev, { id, message, type }])
    setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), 5000)
  }, [])

  const dismiss = useCallback((id) => setToasts((prev) => prev.filter((t) => t.id !== id)), [])

  return { toasts, push, dismiss }
}

function ToastStack({ toasts, onDismiss }) {
  if (!toasts.length) return null
  return (
    <div className="fixed bottom-6 right-6 z-[9999] flex flex-col gap-2" aria-live="polite">
      {toasts.map((t) => (
        <div
          key={t.id}
          className={[
            'flex items-start gap-3 rounded-2xl border px-4 py-3 text-sm shadow-2xl backdrop-blur-sm',
            t.type === 'success'
              ? 'border-emerald-400/30 bg-emerald-950/90 text-emerald-100'
              : t.type === 'error'
                ? 'border-rose-400/30 bg-rose-950/90 text-rose-100'
                : 'border-white/15 bg-slate-900/90 text-slate-100',
          ].join(' ')}
        >
          <span className="mt-0.5 text-base leading-none">
            {t.type === 'success' ? '✓' : t.type === 'error' ? '✕' : 'ℹ'}
          </span>
          <span className="flex-1 leading-5">{t.message}</span>
          <button
            type="button"
            onClick={() => onDismiss(t.id)}
            className="ml-2 opacity-60 hover:opacity-100"
            aria-label="Dismiss"
          >×</button>
        </div>
      ))}
    </div>
  )
}
// ─────────────────────────────────────────────────────────────────────────────

function SearchResultList({ items, onSelect, emptyLabel = 'No matches yet.' }) {
  if (!Array.isArray(items) || items.length === 0) {
    return <div className="rounded-2xl border border-dashed border-white/10 bg-white/[0.02] p-3 text-xs text-slate-500">{emptyLabel}</div>
  }

  return (
    <div className="grid gap-2">
      {items.map((item) => (
        <button
          key={`${item.entity_type}-${item.id}`}
          type="button"
          onClick={() => onSelect(item)}
          className="flex items-center gap-3 rounded-2xl border border-white/10 bg-black/20 px-3 py-3 text-left transition hover:border-white/20"
        >
          {item.avatar ? <img src={item.avatar} alt={item.title} className="h-10 w-10 rounded-2xl border border-white/10 object-cover" /> : null}
          <div className="min-w-0 flex-1">
            <div className="text-sm font-semibold text-white">{item.title}</div>
            {item.subtitle ? <div className="text-xs uppercase tracking-[0.14em] text-slate-500">{item.subtitle}</div> : null}
            {item.description ? <div className="mt-1 line-clamp-2 text-xs text-slate-400">{item.description}</div> : null}
          </div>
        </button>
      ))}
    </div>
  )
}

function FieldError({ message }) {
  if (!message) return null
  return <p className="text-xs text-rose-300">{message}</p>
}

function normalizeNewTagName(value) {
  return String(value || '').replace(/\s+/g, ' ').trim().slice(0, 80)
}

function SectionCard({ eyebrow, title, description, actions, children, tone = 'default' }) {
  const toneClass = tone === 'feature'
    ? 'bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.16),transparent_38%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.92))] shadow-[0_24px_70px_rgba(2,6,23,0.28)]'
    : 'bg-white/[0.03]'

  return (
    <section className={`rounded-[28px] border border-white/10 p-5 ${toneClass}`}>
      <div className="flex flex-wrap items-start justify-between gap-4">
        <div className="max-w-3xl">
          {eyebrow ? <p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/75">{eyebrow}</p> : null}
          <h2 className="mt-2 text-xl font-semibold tracking-[-0.03em] text-white">{title}</h2>
          {description ? <p className="mt-2 text-sm leading-6 text-slate-400">{description}</p> : null}
        </div>
        {actions ? <div className="flex flex-wrap gap-2">{actions}</div> : null}
      </div>
      <div className="mt-5">{children}</div>
    </section>
  )
}

function TagPicker({ options, selectedIds, newTagNames, tagQuery, onTagQueryChange, onToggle, onCreateTag, onRemoveNewTag, manageUrl }) {
  const selectedTags = useMemo(() => options.filter((tag) => selectedIds.includes(tag.id)), [options, selectedIds])
  const normalizedQuery = useMemo(() => normalizeNewTagName(tagQuery), [tagQuery])
  const matchingExistingTag = useMemo(() => {
    if (!normalizedQuery) return null

    const lowerQuery = normalizedQuery.toLowerCase()

    return options.find((tag) => String(tag.name || '').toLowerCase() === lowerQuery) || null
  }, [options, normalizedQuery])
  const queryMatchesPending = useMemo(() => {
    if (!normalizedQuery) return false

    const lowerQuery = normalizedQuery.toLowerCase()

    return newTagNames.some((tagName) => tagName.toLowerCase() === lowerQuery)
  }, [newTagNames, normalizedQuery])

  const availableTags = useMemo(() => {
    const query = String(tagQuery || '').trim().toLowerCase()

    return options
      .filter((tag) => !selectedIds.includes(tag.id))
      .filter((tag) => (query === '' ? true : String(tag.name || '').toLowerCase().includes(query)))
      .slice(0, 12)
  }, [options, selectedIds, tagQuery])

  return (
    <div className="grid gap-4">
      <div className="rounded-[24px] border border-white/10 bg-black/20 p-4">
        <div className="flex items-center justify-between gap-3">
          <div>
            <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Selected tags</div>
            <div className="mt-1 text-sm text-slate-400">Attach article topics without forcing the editor to scan a wall of checkboxes.</div>
          </div>
          {manageUrl ? <a href={manageUrl} className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.14em] text-white">Manage tags</a> : null}
        </div>

        <div className="mt-4 flex min-h-[3.5rem] flex-wrap gap-2">
          {selectedTags.length > 0 ? selectedTags.map((tag) => (
            <button
              key={tag.id}
              type="button"
              onClick={() => onToggle(tag.id)}
              className="inline-flex items-center gap-2 rounded-full border border-sky-300/20 bg-sky-400/10 px-3 py-2 text-sm text-sky-50 transition hover:bg-sky-400/15"
            >
              <span>{tag.name}</span>
              <span className="text-xs text-sky-100/70">Remove</span>
            </button>
          )) : null}
          {newTagNames.map((tagName) => (
            <button
              key={tagName}
              type="button"
              onClick={() => onRemoveNewTag(tagName)}
              className="inline-flex items-center gap-2 rounded-full border border-emerald-300/20 bg-emerald-400/10 px-3 py-2 text-sm text-emerald-50 transition hover:bg-emerald-400/15"
            >
              <span>{tagName}</span>
              <span className="rounded-full border border-emerald-200/30 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-emerald-100/80">New</span>
            </button>
          ))}
          {selectedTags.length === 0 && newTagNames.length === 0 ? <div className="rounded-2xl border border-dashed border-white/10 bg-white/[0.02] px-4 py-3 text-sm text-slate-500">No tags selected yet.</div> : null}
        </div>
      </div>

      <div className="rounded-[24px] border border-white/10 bg-black/20 p-4">
        <label className="grid gap-2 text-sm text-slate-300">
          <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Find tags</span>
          <input
            value={tagQuery}
            onChange={(event) => onTagQueryChange(event.target.value)}
            onKeyDown={(event) => {
              if (!['Enter', ','].includes(event.key)) {
                return
              }

              event.preventDefault()

              const nextQuery = normalizeNewTagName(event.currentTarget.value)
              if (!nextQuery) {
                return
              }

              if (matchingExistingTag && !selectedIds.includes(matchingExistingTag.id)) {
                onToggle(matchingExistingTag.id)
                onTagQueryChange('')
                return
              }

              if (!queryMatchesPending) {
                onCreateTag(nextQuery)
              }

              onTagQueryChange('')
            }}
            placeholder="Search existing tags or type a new one"
            className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none"
          />
        </label>

        {normalizedQuery && !matchingExistingTag && !queryMatchesPending ? (
          <button
            type="button"
            onClick={() => {
              onCreateTag(normalizedQuery)
              onTagQueryChange('')
            }}
            className="mt-3 inline-flex items-center gap-2 rounded-full border border-emerald-300/20 bg-emerald-400/10 px-3 py-2 text-sm font-semibold text-emerald-50"
          >
            <span>Create tag</span>
            <span className="rounded-full border border-emerald-200/30 px-2 py-0.5 text-xs text-emerald-100">{normalizedQuery}</span>
          </button>
        ) : null}

        <div className="mt-4 flex flex-wrap gap-2">
          {availableTags.length > 0 ? availableTags.map((tag) => (
            <button
              key={tag.id}
              type="button"
              onClick={() => onToggle(tag.id)}
              className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-2 text-sm text-white transition hover:border-white/20 hover:bg-white/[0.08]"
            >
              + {tag.name}
            </button>
          )) : <div className="rounded-2xl border border-dashed border-white/10 bg-white/[0.02] px-4 py-3 text-sm text-slate-500">No additional tags match the current search.</div>}
        </div>

        <div className="mt-3 text-xs leading-5 text-slate-500">
          Press Enter or comma to queue a new tag. Pending tags are written into the news tag list when the article is saved.
        </div>
      </div>
    </div>
  )
}

function RelationCard({ relation, index, onChange, onRemove, onSearch, results, relationTypeOptions }) {
  return (
    <div className="rounded-[24px] border border-white/10 bg-black/20 p-4">
        <div className="grid gap-4 lg:grid-cols-[180px_minmax(0,1fr)_auto] lg:items-end">
        <div className="grid gap-2 text-sm text-slate-300">
          <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Type</span>
          <NovaSelect value={relation.entity_type} onChange={(val) => onChange(index, { ...relation, entity_type: val, entity_id: '', preview: null, query: '' })} options={relationTypeOptions} searchable={false} />
        </div>
        <label className="grid gap-2 text-sm text-slate-300">
          <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Search entity</span>
          <div className="flex gap-2">
            <input value={relation.query || ''} onChange={(event) => onChange(index, { ...relation, query: event.target.value })} placeholder="Search by name, slug, or title" className="min-w-0 flex-1 rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
            <button type="button" onClick={() => onSearch(index)} className="rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm font-semibold text-white">Search</button>
          </div>
        </label>
        <button type="button" onClick={() => onRemove(index)} className="rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm font-semibold text-rose-100">Remove</button>
      </div>

      {relation.preview ? (
        <div className="mt-4 rounded-2xl border border-emerald-300/20 bg-emerald-400/10 p-4 text-sm text-emerald-50">
          <div className="font-semibold">Linked: {relation.preview.title}</div>
          {relation.preview.subtitle ? <div className="mt-1 text-xs uppercase tracking-[0.14em] text-emerald-100/70">{relation.preview.subtitle}</div> : null}
        </div>
      ) : null}

      <div className="mt-4">
        <SearchResultList items={results} onSelect={(item) => onChange(index, { ...relation, entity_id: item.id, preview: item, query: item.title })} emptyLabel="Search to attach a related entity." />
      </div>

      <label className="mt-4 grid gap-2 text-sm text-slate-300">
        <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Context label</span>
        <input value={relation.context_label || ''} onChange={(event) => onChange(index, { ...relation, context_label: event.target.value })} placeholder="Featured release, Meet the creator, Join this challenge…" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
      </label>
    </div>
  )
}

function stripHtml(value) {
  return String(value || '').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim()
}

function selectOptionsFromValues(options, emptyLabel = null) {
  const base = Array.isArray(options)
    ? options.map((option) => ({
      value: option.value ?? option.id,
      label: option.label ?? option.name,
    }))
    : []

  return emptyLabel ? [{ value: '', label: emptyLabel }, ...base] : base
}

function buildSubmitPayload(data) {
  return {
    title: String(data.title || '').trim(),
    slug: String(data.slug || '').trim(),
    excerpt: String(data.excerpt || ''),
    content: String(data.content || ''),
    cover_image: String(data.cover_image || '').trim(),
    type: String(data.type || ''),
    category_id: data.category_id === '' || data.category_id == null ? null : Number(data.category_id),
    author_id: data.author_id === '' || data.author_id == null ? null : Number(data.author_id),
    editorial_status: String(data.editorial_status || ''),
    published_at: data.published_at ? String(data.published_at) : null,
    is_featured: Boolean(data.is_featured),
    is_pinned: Boolean(data.is_pinned),
    tag_ids: Array.isArray(data.tag_ids) ? data.tag_ids.map((id) => Number(id)).filter(Boolean) : [],
    new_tag_names: Array.isArray(data.new_tag_names) ? data.new_tag_names.map((name) => normalizeNewTagName(name)).filter(Boolean) : [],
    meta_title: String(data.meta_title || ''),
    meta_description: String(data.meta_description || ''),
    meta_keywords: String(data.meta_keywords || ''),
    canonical_url: String(data.canonical_url || '').trim(),
    og_title: String(data.og_title || ''),
    og_description: String(data.og_description || ''),
    og_image: String(data.og_image || '').trim(),
    relations: Array.isArray(data.relations)
      ? data.relations.map((relation) => ({
        entity_type: String(relation.entity_type || '').trim(),
        entity_id: relation.entity_id === '' || relation.entity_id == null ? '' : Number(relation.entity_id),
        context_label: String(relation.context_label || '').trim(),
      }))
      : [],
  }
}

function buildInitialFormData(article, defaultAuthor, typeOptions) {
  return {
    title: article.title || '',
    slug: article.slug || '',
    excerpt: article.excerpt || '',
    content: article.content || '',
    cover_image: article.cover_image || '',
    type: article.type || (typeOptions?.[0]?.value || 'announcement'),
    category_id: article.category_id || '',
    author_id: article.author_id || defaultAuthor?.id || '',
    editorial_status: article.editorial_status || 'draft',
    published_at: article.published_at ? String(article.published_at).slice(0, 16) : '',
    is_featured: Boolean(article.is_featured),
    is_pinned: Boolean(article.is_pinned),
    tag_ids: Array.isArray(article.tag_ids) ? article.tag_ids : [],
    new_tag_names: [],
    meta_title: article.meta_title || '',
    meta_description: article.meta_description || '',
    meta_keywords: article.meta_keywords || '',
    canonical_url: article.canonical_url || '',
    og_title: article.og_title || '',
    og_description: article.og_description || '',
    og_image: article.og_image || '',
    relations: Array.isArray(article.relations) ? article.relations.map((relation) => ({
      entity_type: relation.entity_type || 'group',
      entity_id: relation.entity_id || '',
      context_label: relation.context_label || '',
      preview: relation.preview || null,
      query: relation.preview?.title || '',
    })) : [],
  }
}

export default function StudioNewsEditor() {
  const { props } = usePage()
  const { toasts, push: pushToast, dismiss: dismissToast } = useToast()
  const article = props.article || {}
  const initialFormData = useMemo(() => buildInitialFormData(article, props.defaultAuthor, props.typeOptions), [article, props.defaultAuthor, props.typeOptions])
  const articleSyncKey = useMemo(() => JSON.stringify(initialFormData), [initialFormData])
  const [authorResults, setAuthorResults] = useState([])
  const [authorQuery, setAuthorQuery] = useState(article.author?.title || article.author?.subtitle?.replace(/^@/, '') || '')
  const [selectedAuthor, setSelectedAuthor] = useState(article.author || props.defaultAuthor || null)
  const [relationResults, setRelationResults] = useState({})
  const [tagQuery, setTagQuery] = useState('')
  const [coverPreviewUrl, setCoverPreviewUrl] = useState(article.cover_url || (String(article.cover_image || '').startsWith('http') ? article.cover_image : ''))
  const [stagedCoverPath, setStagedCoverPath] = useState('')
  const lastSyncedArticleKeyRef = useRef(articleSyncKey)

  const form = useForm(initialFormData)

  useEffect(() => {
    if (lastSyncedArticleKeyRef.current === articleSyncKey) {
      return
    }

    lastSyncedArticleKeyRef.current = articleSyncKey
    form.setData(initialFormData)
    form.clearErrors()
    setSelectedAuthor(article.author || props.defaultAuthor || null)
    setAuthorQuery(article.author?.title || article.author?.subtitle?.replace(/^@/, '') || '')
    setRelationResults({})
    setTagQuery('')
    setCoverPreviewUrl(article.cover_url || (String(article.cover_image || '').startsWith('http') ? article.cover_image : ''))
    setStagedCoverPath('')
  }, [article, articleSyncKey, form, initialFormData, props.defaultAuthor])

  const excerptLength = String(form.data.excerpt || '').trim().length
  const bodyWordCount = useMemo(() => {
    const plain = stripHtml(form.data.content)
    return plain === '' ? 0 : plain.split(/\s+/).length
  }, [form.data.content])
  const typeOptions = useMemo(() => selectOptionsFromValues(props.typeOptions || []), [props.typeOptions])
  const statusOptions = useMemo(() => selectOptionsFromValues(props.statusOptions || []), [props.statusOptions])
  const categoryOptions = useMemo(() => selectOptionsFromValues(props.categoryOptions || [], 'No category'), [props.categoryOptions])

  const searchEntities = async (type, query) => {
    const url = new URL(props.entitySearchUrl, window.location.origin)
    url.searchParams.set('type', type)
    url.searchParams.set('q', query)

    const response = await fetch(url.toString(), {
      headers: {
        Accept: 'application/json',
      },
      credentials: 'same-origin',
    })

    if (!response.ok) {
      return []
    }

    const payload = await response.json()
    return Array.isArray(payload.items) ? payload.items : []
  }

  const runAuthorSearch = async () => {
    const items = await searchEntities('user', authorQuery)
    setAuthorResults(items)
  }

  const addRelation = () => {
    form.setData('relations', [
      ...form.data.relations,
      {
        entity_type: props.relationTypeOptions?.[0]?.value || 'group',
        entity_id: '',
        context_label: '',
        preview: null,
        query: '',
      },
    ])
  }

  const updateRelation = (index, nextRelation) => {
    form.setData('relations', form.data.relations.map((relation, relationIndex) => (relationIndex === index ? nextRelation : relation)))
  }

  const removeRelation = (index) => {
    form.setData('relations', form.data.relations.filter((_, relationIndex) => relationIndex !== index))
    setRelationResults((current) => {
      const next = { ...current }
      delete next[index]
      return next
    })
  }

  const runRelationSearch = async (index) => {
    const relation = form.data.relations[index]
    if (!relation) return
    const items = await searchEntities(relation.entity_type, relation.query || '')
    setRelationResults((current) => ({ ...current, [index]: items }))
  }

  const toggleTag = (tagId) => {
    const numericId = Number(tagId)
    const next = form.data.tag_ids.includes(numericId)
      ? form.data.tag_ids.filter((currentId) => currentId !== numericId)
      : [...form.data.tag_ids, numericId]

    form.setData('tag_ids', next)

    if (!form.data.tag_ids.includes(numericId)) {
      const matchedTag = (Array.isArray(props.tagOptions) ? props.tagOptions : []).find((tag) => tag.id === numericId)
      if (matchedTag) {
        const lowerName = String(matchedTag.name || '').toLowerCase()
        form.setData('new_tag_names', form.data.new_tag_names.filter((tagName) => tagName.toLowerCase() !== lowerName))
      }
    }
  }

  const addNewTagName = (rawValue) => {
    const nextTagName = normalizeNewTagName(rawValue)
    if (!nextTagName) return

    const lowerName = nextTagName.toLowerCase()
    const matchingExistingTag = (Array.isArray(props.tagOptions) ? props.tagOptions : []).find((tag) => String(tag.name || '').toLowerCase() === lowerName)

    if (matchingExistingTag) {
      if (!form.data.tag_ids.includes(matchingExistingTag.id)) {
        form.setData('tag_ids', [...form.data.tag_ids, matchingExistingTag.id])
      }
      return
    }

    if (form.data.new_tag_names.some((tagName) => tagName.toLowerCase() === lowerName)) {
      return
    }

    form.setData('new_tag_names', [...form.data.new_tag_names, nextTagName])
  }

  const removeNewTagName = (tagName) => {
    form.setData('new_tag_names', form.data.new_tag_names.filter((currentTagName) => currentTagName !== tagName))
  }

  const handleManualCoverChange = (nextValue) => {
    form.setData('cover_image', nextValue)

    if (stagedCoverPath && nextValue !== stagedCoverPath) {
      setStagedCoverPath('')
    }

    if (!nextValue) {
      setCoverPreviewUrl('')
      return
    }

    if (String(nextValue).startsWith('http://') || String(nextValue).startsWith('https://')) {
      setCoverPreviewUrl(nextValue)
      return
    }

    setCoverPreviewUrl(`${props.coverCdnBaseUrl}/${String(nextValue).replace(/^\/+/, '')}`)
  }

  const submit = (event) => {
    event.preventDefault()

    const options = {
      preserveScroll: true,
      preserveState: false,
      onSuccess: () => {
        setStagedCoverPath('')
        pushToast('Article saved successfully.', 'success')
      },
      onError: (errors) => {
        const errorMessages = Object.values(errors)
        const first = errorMessages[0] || 'The article could not be saved.'
        const extra = errorMessages.length > 1 ? ` (${errorMessages.length - 1} more field${errorMessages.length > 2 ? 's' : ''})` : ''
        pushToast(first + extra, 'error')
      },
    }

    form.transform((data) => buildSubmitPayload(data))

    if (props.updateUrl) {
      form.patch(props.updateUrl, options)
      return
    }

    form.post(props.storeUrl, options)
  }

  const deleteArticle = () => {
    if (!props.destroyUrl) return
    if (!window.confirm('Move this article to trash? This uses soft delete so the record stays in the database.')) return

    router.delete(props.destroyUrl, {
      preserveScroll: true,
    })
  }

  return (
    <StudioLayout title={props.title} subtitle={props.description}>
      <ToastStack toasts={toasts} onDismiss={dismissToast} />
      <form onSubmit={submit} className="grid gap-6 xl:grid-cols-[minmax(0,1.15fr)_minmax(340px,0.85fr)]">
        <div className="space-y-6">
          <SectionCard
            eyebrow="Story workspace"
            title={article.id ? 'Shape the full newsroom story before it goes live.' : 'Create a newsroom story that reads like an editorial feature, not a raw database form.'}
            description="The cover, excerpt, body, tags, and related entities are all tuned for homepage spotlight, archive browsing, and article detail pages."
            tone="feature"
          >
            <div className="grid gap-5">
              <label className="grid gap-2 text-sm text-slate-300">
                <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Title</span>
                <input value={form.data.title} onChange={(event) => form.setData('title', event.target.value)} placeholder="Headline that can carry the article alone" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
                <span className="text-xs leading-5 text-slate-500">Aim for a clear editorial headline that still makes sense in cards, notifications, and social previews.</span>
                <FieldError message={form.errors.title} />
              </label>

              <label className="grid gap-2 text-sm text-slate-300">
                <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Slug</span>
                <input value={form.data.slug} onChange={(event) => form.setData('slug', event.target.value)} placeholder="optional-manual-slug" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
                <span className="text-xs leading-5 text-slate-500">Leave blank to generate from the title, or set a durable URL manually when the story needs a stable public address.</span>
                <FieldError message={form.errors.slug} />
              </label>

              <label className="grid gap-2 text-sm text-slate-300">
                <span className="flex items-center justify-between gap-3 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">
                  <span>Excerpt</span>
                  <span className="text-slate-500">{excerptLength}/800</span>
                </span>
                <textarea value={form.data.excerpt} onChange={(event) => form.setData('excerpt', event.target.value)} rows={6} placeholder="Write the concise summary used in listing cards, metadata, and archive previews." className="rounded-[24px] border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
                <span className="text-xs leading-5 text-slate-500">Lead with the update, why it matters, and the audience hook. Two to four punchy sentences usually land better than one dense paragraph.</span>
                <FieldError message={form.errors.excerpt} />
              </label>

              <div className="grid gap-4">
                <WorldMediaUploadField
                  label="Cover image"
                  slot="cover"
                  value={form.data.cover_image}
                  previewUrl={coverPreviewUrl}
                  emptyLabel="Drop a cover image"
                  helperText="Upload the hero image directly to object storage. A wide landscape image works best for cards, preview surfaces, and social sharing."
                  uploadUrl={props.coverUploadUrl}
                  deleteUrl={props.coverDeleteUrl}
                  onChange={({ path, url }) => {
                    setStagedCoverPath(path || '')
                    form.setData('cover_image', path || '')
                    setCoverPreviewUrl(url || '')
                  }}
                  isTemporaryValue={Boolean(stagedCoverPath) && form.data.cover_image === stagedCoverPath}
                />
                <FieldError message={form.errors.cover_image} />

                <label className="grid gap-2 text-sm text-slate-300">
                  <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Advanced cover path or URL</span>
                  <input value={form.data.cover_image} onChange={(event) => handleManualCoverChange(event.target.value)} placeholder="Optional external URL or stored object path" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
                  <span className="text-xs leading-5 text-slate-500">Keep this for migrations, imported legacy stories, or when you already have the exact asset URL you want to use.</span>
                </label>
              </div>
            </div>
          </SectionCard>

          <SectionCard eyebrow="Full message" title="Body editor" description="Write the main article in a richer editing surface so the content reads like a polished story, not pasted plain text." actions={<div className="rounded-full border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.14em] text-white">{bodyWordCount.toLocaleString()} words</div>}>
            <div className="grid gap-3 text-sm text-slate-300">
              <RichTextEditor
                content={form.data.content}
                onChange={(nextValue) => form.setData('content', nextValue)}
                placeholder="Open with the update, add context, use links, pull quotes, headings, and imagery where the story needs structure."
                error={form.errors.content}
                minHeight={24}
                autofocus={false}
              />
              <div className="rounded-[24px] border border-white/10 bg-black/20 px-4 py-3 text-xs leading-6 text-slate-400">
                Story workflow suggestion: lead with the change, explain why it matters, add supporting detail, then end with a clear call to action or next step.
              </div>
            </div>
          </SectionCard>

          <SectionCard eyebrow="Context links" title="Related entities" description="Attach groups, artworks, collections, releases, projects, challenges, events, and profiles so the article becomes part of the rest of Nova instead of a dead-end page." actions={<button type="button" onClick={addRelation} className="rounded-full border border-sky-300/20 bg-sky-400/10 px-4 py-2 text-sm font-semibold text-sky-100">Add relation</button>}>
            <div className="grid gap-4">
              {form.data.relations.length > 0 ? form.data.relations.map((relation, index) => (
                <RelationCard
                  key={`${relation.entity_type}-${index}`}
                  relation={relation}
                  index={index}
                  onChange={updateRelation}
                  onRemove={removeRelation}
                  onSearch={runRelationSearch}
                  results={relationResults[index] || []}
                  relationTypeOptions={Array.isArray(props.relationTypeOptions) ? props.relationTypeOptions : []}
                />
              )) : <div className="rounded-[24px] border border-dashed border-white/10 bg-white/[0.02] p-5 text-sm text-slate-400">No related entities attached yet.</div>}
            </div>
          </SectionCard>
        </div>

        <div className="space-y-6">
          <SectionCard eyebrow="Editorial controls" title="Publishing" description="Set ownership, placement, timing, and surface behavior before the article leaves draft.">
            <div className="grid gap-4">
              {props.previewUrl ? <a href={props.previewUrl} target="_blank" rel="noreferrer" className="inline-flex items-center justify-center gap-2 rounded-2xl border border-indigo-300/20 bg-indigo-400/10 px-4 py-3 text-sm font-semibold text-indigo-100 transition hover:bg-indigo-400/15"><i className="fa-regular fa-eye" />Preview article</a> : null}

              <div className="grid gap-4 md:grid-cols-2">
                <div className="grid gap-2 text-sm text-slate-300">
                  <NovaSelect label="Type" value={form.data.type || null} onChange={(nextValue) => form.setData('type', String(nextValue || ''))} options={typeOptions} searchable={false} className="bg-black/20" error={form.errors.type} />
                </div>
                <div className="grid gap-2 text-sm text-slate-300">
                  <NovaSelect label="Category" value={form.data.category_id || ''} onChange={(nextValue) => form.setData('category_id', String(nextValue || ''))} options={categoryOptions} searchable={false} className="bg-black/20" error={form.errors.category_id} />
                </div>
              </div>

              <div className="grid gap-4 md:grid-cols-2">
                <div className="grid gap-2 text-sm text-slate-300">
                  <NovaSelect label="Workflow status" value={form.data.editorial_status || null} onChange={(nextValue) => form.setData('editorial_status', String(nextValue || ''))} options={statusOptions} searchable={false} className="bg-black/20" error={form.errors.editorial_status} />
                </div>
                <label className="grid gap-2 text-sm text-slate-300">
                  <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Publish at</span>
                  <input type="datetime-local" value={form.data.published_at || ''} onChange={(event) => form.setData('published_at', event.target.value)} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
                  <FieldError message={form.errors.published_at} />
                </label>
              </div>

              <div className="grid gap-3 rounded-[24px] border border-white/10 bg-black/20 p-4">
                <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Author</div>
                <div className="flex gap-2">
                  <input value={authorQuery} onChange={(event) => setAuthorQuery(event.target.value)} placeholder="Search for an author" className="min-w-0 flex-1 rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
                  <button type="button" onClick={runAuthorSearch} className="rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm font-semibold text-white">Search</button>
                </div>
                {selectedAuthor ? (
                  <div className="rounded-2xl border border-emerald-300/20 bg-emerald-400/10 p-4 text-sm text-emerald-50">
                    <div className="font-semibold">Selected author: {selectedAuthor.title}</div>
                    {selectedAuthor.subtitle ? <div className="mt-1 text-xs uppercase tracking-[0.14em] text-emerald-100/70">{selectedAuthor.subtitle}</div> : null}
                  </div>
                ) : null}
                <SearchResultList items={authorResults} onSelect={(item) => {
                  setSelectedAuthor(item)
                  setAuthorQuery(item.title)
                  form.setData('author_id', item.id)
                }} emptyLabel="Search to choose an author profile." />
                <FieldError message={form.errors.author_id} />
              </div>

              <div className="grid gap-3 md:grid-cols-2">
                <div className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3">
                  <Checkbox checked={form.data.is_featured} onChange={(event) => form.setData('is_featured', event.target.checked)} label="Feature on newsroom surfaces" size={20} variant="accent" />
                </div>
                <div className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3">
                  <Checkbox checked={form.data.is_pinned} onChange={(event) => form.setData('is_pinned', event.target.checked)} label="Pin to the top of the newsroom" size={20} variant="accent" />
                </div>
              </div>
            </div>
          </SectionCard>

          <SectionCard eyebrow="Taxonomy" title="Tags" description="Search and apply tags quickly instead of scanning a wall of checkboxes.">
            <TagPicker
              options={Array.isArray(props.tagOptions) ? props.tagOptions : []}
              selectedIds={form.data.tag_ids}
              newTagNames={form.data.new_tag_names}
              tagQuery={tagQuery}
              onTagQueryChange={setTagQuery}
              onToggle={toggleTag}
              onCreateTag={addNewTagName}
              onRemoveNewTag={removeNewTagName}
              manageUrl={props.tagsUrl}
            />
            <div className="mt-3">
              <FieldError message={form.errors.tag_ids || form.errors.new_tag_names} />
            </div>
          </SectionCard>

          <SectionCard eyebrow="Metadata" title="SEO and social" description="Keep search and sharing fields aligned with the main editorial package.">
            <div className="grid gap-4">
              <label className="grid gap-2 text-sm text-slate-300">
                <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Meta title</span>
                <input value={form.data.meta_title} onChange={(event) => form.setData('meta_title', event.target.value)} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
              </label>
              <label className="grid gap-2 text-sm text-slate-300">
                <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Meta description</span>
                <textarea value={form.data.meta_description} onChange={(event) => form.setData('meta_description', event.target.value)} rows={3} className="rounded-[24px] border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
              </label>
              <label className="grid gap-2 text-sm text-slate-300">
                <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Meta keywords</span>
                <input value={form.data.meta_keywords} onChange={(event) => form.setData('meta_keywords', event.target.value)} placeholder="creator-story, release, tutorial" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
              </label>
              <label className="grid gap-2 text-sm text-slate-300">
                <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Canonical URL</span>
                <input value={form.data.canonical_url} onChange={(event) => form.setData('canonical_url', event.target.value)} placeholder="https://..." className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
              </label>
              <div className="grid gap-4 md:grid-cols-2">
                <label className="grid gap-2 text-sm text-slate-300">
                  <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">OG title</span>
                  <input value={form.data.og_title} onChange={(event) => form.setData('og_title', event.target.value)} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
                </label>
                <label className="grid gap-2 text-sm text-slate-300">
                  <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">OG image</span>
                  <input value={form.data.og_image} onChange={(event) => form.setData('og_image', event.target.value)} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
                </label>
              </div>
              <label className="grid gap-2 text-sm text-slate-300">
                <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">OG description</span>
                <textarea value={form.data.og_description} onChange={(event) => form.setData('og_description', event.target.value)} rows={3} className="rounded-[24px] border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
              </label>
            </div>
          </SectionCard>

          <SectionCard eyebrow="Actions" title="Save and publish" description="Use the primary action for create or update, then promote, archive, or trash the article from the same control rail.">
            <div className="grid gap-3">
              {Object.keys(form.errors || {}).length > 0 ? <div className="rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100">The article was not saved. Fix the highlighted fields and try again.</div> : null}
              <button type="submit" disabled={form.processing} className="w-full rounded-full border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100 disabled:opacity-60">{form.processing ? 'Saving article…' : 'Save article'}</button>
              {props.publishUrl ? <button type="button" onClick={() => router.post(props.publishUrl)} className="w-full rounded-full border border-emerald-300/20 bg-emerald-400/10 px-4 py-3 text-sm font-semibold text-emerald-100">Publish now</button> : null}
              {props.archiveUrl ? <button type="button" onClick={() => router.post(props.archiveUrl)} className="w-full rounded-full border border-white/10 bg-white/[0.05] px-4 py-3 text-sm font-semibold text-white">Archive article</button> : null}
              {props.featureUrl ? <button type="button" onClick={() => router.post(props.featureUrl)} className="w-full rounded-full border border-white/10 bg-white/[0.05] px-4 py-3 text-sm font-semibold text-white">Toggle featured</button> : null}
              {props.pinUrl ? <button type="button" onClick={() => router.post(props.pinUrl)} className="w-full rounded-full border border-amber-300/20 bg-amber-400/10 px-4 py-3 text-sm font-semibold text-amber-100">Toggle pinned</button> : null}
              {props.destroyUrl ? <button type="button" onClick={deleteArticle} className="w-full rounded-full border border-rose-300/20 bg-rose-400/10 px-4 py-3 text-sm font-semibold text-rose-100">Move to trash</button> : null}
            </div>
          </SectionCard>
        </div>
      </form>
    </StudioLayout>
  )
}