/**
 * Logos page: BlockNote editor for the recollection. Content only; sidebar is at layout level.
 * Content persisted in recollection store.
 */

import React, { useCallback, useEffect, useMemo, useRef, useState, forwardRef } from 'react'
import { useParams, useLocation, useSearchParams } from 'react-router-dom'
import { usePlatform } from '@/app/kosmos/KosmosContext'
import { FluxIcon } from '@/lib/icons'
import { useCreateBlockNote, getDefaultReactSlashMenuItems, SuggestionMenuController } from '@blocknote/react'
import { BlockNoteView } from '@blocknote/shadcn'
import { useTheme } from '@/lib/themeContext'
import { useRecollectionActions } from '../layout/RecollectionActionsContext'
import { useRecollectionSidebar } from '../layout/WorkspaceTreeContext'
import {
  getLogosContentForPage,
  setLogosContentForPage,
  type StoredLogosContent,
} from '../state/recollectionStore'
import { logosSchema } from './logosSchema'
import { KatalogosPage } from '../katalogos/KatalogosPage'
import { toast } from 'sonner'

/** Wraps BlockNoteView so refs go to a div, not the function component (avoids ref warning). */
const BlockNoteViewWrapper = forwardRef<HTMLDivElement, React.ComponentProps<typeof BlockNoteView>>(
  function BlockNoteViewWrapper(props, ref) {
    const { className, ref: _ref, ...rest } = props as React.ComponentProps<typeof BlockNoteView> & { ref?: unknown }
    return (
      <div ref={ref} className={`logos-blocknote ${className ?? ''}`} style={{ minHeight: '100%', width: '100%' }}>
        <BlockNoteView {...rest} />
      </div>
    )
  }
)
const SAVE_DEBOUNCE_MS = 400

/** Known React ref warning from BlockNote/Radix internals; we can't fix it in our code. Suppress once at load so it's active before first BlockNote render. */
function isBlockNoteRefWarning(args: unknown[]): boolean {
  const s = args.map((a) => (typeof a === 'string' ? a : String(a))).join(' ')
  return (
    s.includes('Function components cannot be given refs') &&
    s.includes('ForwardRef') &&
    s.includes('blocknote')
  )
}
let blockNoteRefWarningPatched = false
function patchBlockNoteRefWarning() {
  if (blockNoteRefWarningPatched) return
  blockNoteRefWarningPatched = true
  const orig = console.error
  console.error = (...args: unknown[]) => {
    if (isBlockNoteRefWarning(args)) return
    orig.apply(console, args)
  }
}

export function LogosPage() {
  const { recollectionId } = useParams<{ recollectionId: string }>()
  const { pathname } = useLocation()
  const [searchParams, setSearchParams] = useSearchParams()
  const { recollections } = usePlatform()
  const { theme } = useTheme()
  const { setLogosSlot } = useRecollectionActions()
  const { tree, activePageId } = useRecollectionSidebar()
  const recollection = recollectionId ? recollections.find((p) => p.id === recollectionId) : null
  const [reloadKey, setReloadKey] = useState(0)
  const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const [saveStatus, setSaveStatus] = useState<'saved' | 'unsaved' | 'saving'>('saved')

  const isKatalogosView = pathname.endsWith('/logos/katalogos')

  // When on Logos (not Katalogos) with a selected page but no page param, sync URL so refresh keeps the page.
  useEffect(() => {
    if (isKatalogosView || !activePageId || searchParams.get('page') === activePageId) return
    setSearchParams({ page: activePageId }, { replace: true })
  }, [isKatalogosView, activePageId, searchParams, setSearchParams])

  const initialContent = useMemo(() => {
    if (!recollectionId || !activePageId) return undefined
    const content = getLogosContentForPage(recollectionId, activePageId)
    if (!content || !Array.isArray(content) || content.length === 0) return undefined
    return content
  }, [recollectionId, activePageId, reloadKey])

  const editor = useCreateBlockNote(
    { schema: logosSchema, initialContent },
    [recollectionId, activePageId, reloadKey]
  )

  const persistContent = useCallback(() => {
    if (!recollectionId || !activePageId || !editor) return
    setSaveStatus('saving')
    try {
      const doc = editor.document
      const serialized = JSON.parse(JSON.stringify(doc)) as StoredLogosContent
      setLogosContentForPage(recollectionId, activePageId, serialized)
      setSaveStatus('saved')
    } catch {
      setSaveStatus('unsaved')
    }
  }, [recollectionId, activePageId, editor])

  const onSave = useCallback(() => {
    persistContent()
    toast.success('Saved')
  }, [persistContent])

  useEffect(() => {
    if (!editor || !recollectionId || !activePageId) return
    const handleChange = () => {
      setSaveStatus('unsaved')
      if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current)
      saveTimeoutRef.current = setTimeout(() => {
        saveTimeoutRef.current = null
        persistContent()
      }, SAVE_DEBOUNCE_MS)
    }
    editor.onChange(handleChange)
    return () => {
      if (saveTimeoutRef.current) {
        clearTimeout(saveTimeoutRef.current)
        saveTimeoutRef.current = null
      }
    }
  }, [editor, recollectionId, activePageId, persistContent])

  useEffect(() => {
    if (isKatalogosView || !editor || !recollectionId || !activePageId) {
      if (isKatalogosView) setLogosSlot(null)
      return
    }
    const slot = {
      saveStatus,
      onSave,
      canSave: true,
      undo: () => (editor as { undo?: () => void }).undo?.(),
      redo: () => (editor as { redo?: () => void }).redo?.(),
      canUndo: true,
      canRedo: true,
      onRefreshFromStore: () => setReloadKey((k) => k + 1),
    }
    setLogosSlot(slot)
    return () => setLogosSlot(null)
  }, [isKatalogosView, setLogosSlot, saveStatus, onSave, editor, recollectionId, activePageId])

  const activePage = tree.find((p) => p.id === activePageId)

  patchBlockNoteRefWarning()

  const getSlashMenuItems = useCallback(
    async (query: string) => {
      const defaultItems = getDefaultReactSlashMenuItems(editor)
      const fluxItem = {
        title: 'Insert Artifact',
        subtext: 'Insert an Artifact produced by a Flux rendering node',
        icon: <FluxIcon className="size-4" />,
        onItemClick: () => {
          const pos = editor.getTextCursorPosition()
          editor.replaceBlocks([pos.block.id], [{ type: 'fluxOutput', props: {} }])
        },
        aliases: ['artifact', 'flux', 'output', 'render'] as const,
        group: 'Artifacts',
      }
      const all = [...defaultItems, fluxItem]
      const q = query.trim().toLowerCase()
      if (!q) return all
      return all.filter(
        (item) =>
          item.title.toLowerCase().includes(q) ||
          (item.aliases && item.aliases.some((a: string) => a.toLowerCase().includes(q)))
      )
    },
    [editor]
  )

  if (!recollectionId) {
    return (
      <div className="flex h-full min-h-0 flex-1 flex-col items-center justify-center p-8 text-muted-foreground">
        <p className="text-sm">No recollection selected.</p>
      </div>
    )
  }

  if (isKatalogosView) {
    return (
      <div className="flex h-full min-h-0 flex-1 flex-col overflow-auto bg-background">
        <KatalogosPage />
      </div>
    )
  }

  if (activePageId == null || !editor) {
    return (
      <div className="flex h-full min-h-0 flex-1 flex-col items-center justify-center bg-background text-sm text-muted-foreground">
        {tree.length === 0 ? 'Loading…' : 'Select a page'}
      </div>
    )
  }

  return (
    <div className="flex h-full min-h-0 flex-1 flex-col overflow-auto bg-background">
      <div className="mx-auto w-full max-w-3xl p-4">
        <BlockNoteViewWrapper
          editor={editor as any}
          theme={theme}
          className="min-h-full w-full"
          slashMenu={false}
        >
          <SuggestionMenuController triggerCharacter="/" getItems={getSlashMenuItems} />
        </BlockNoteViewWrapper>
      </div>
    </div>
  )
}
