'use client'

import { Node, mergeAttributes } from '@tiptap/core'
import { ReactNodeViewRenderer, NodeViewWrapper, type NodeViewProps } from '@tiptap/react'
import { useCallback } from 'react'
import type { Editor } from '@tiptap/core'
import type { Node as PMNode } from '@tiptap/pm/model'
import { DatabaseBlockEditor } from '@/components/database-block-editor'
import { NOTE_REQUEST_SAVE_EVENT } from '@/lib/note-change-sync'
import {
  createDefaultDatabaseBlockData,
  parseDatabaseBlockAttrs,
  serializeDatabaseBlockData,
  type DatabaseBlockData,
} from '@/lib/editor/database-block-types'

function DatabaseBlockView({ node, updateAttributes, editor }: NodeViewProps) {
  const data = parseDatabaseBlockAttrs(node.attrs)

  const requestSave = useCallback(() => {
    const hostNoteId = editor.storage.liveBlock?.hostNoteId
    if (hostNoteId) {
      window.dispatchEvent(new CustomEvent(NOTE_REQUEST_SAVE_EVENT, {
        detail: { noteId: hostNoteId, reason: 'database-block-mutation' },
      }))
    }
  }, [editor])

  const handleChange = useCallback((next: DatabaseBlockData) => {
    updateAttributes(serializeDatabaseBlockData(next))
    requestSave()
  }, [requestSave, updateAttributes])

  return (
    <NodeViewWrapper className="database-block-wrapper" data-drag-handle contentEditable={false}>
      <DatabaseBlockEditor data={data} onChange={handleChange} />
    </NodeViewWrapper>
  )
}

export const DatabaseBlockExtension = Node.create({
  name: 'databaseBlock',
  group: 'block',
  atom: true,
  draggable: true,
  selectable: true,

  addAttributes() {
    return {
      dbId: {
        default: '',
        parseHTML: (element) => element.getAttribute('data-db-id') || '',
        renderHTML: (attributes) => (attributes.dbId ? { 'data-db-id': attributes.dbId } : {}),
      },
      dbView: {
        default: 'table',
        parseHTML: (element) => element.getAttribute('data-db-view') || 'table',
        renderHTML: (attributes) => ({ 'data-db-view': attributes.dbView || 'table' }),
      },
      dbAuthorsJson: {
        default: '[]',
        parseHTML: (element) => element.getAttribute('data-db-authors') || '[]',
        renderHTML: (attributes) => ({ 'data-db-authors': attributes.dbAuthorsJson || '[]' }),
      },
      dbBooksJson: {
        default: '[]',
        parseHTML: (element) => element.getAttribute('data-db-books') || '[]',
        renderHTML: (attributes) => ({ 'data-db-books': attributes.dbBooksJson || '[]' }),
      },
    }
  },

  parseHTML() {
    return [{ tag: 'div[data-database-block]' }]
  },

  renderHTML({ HTMLAttributes }) {
    return ['div', mergeAttributes(HTMLAttributes, { 'data-database-block': 'true' })]
  },

  addNodeView() {
    return ReactNodeViewRenderer(DatabaseBlockView)
  },
})

export function insertDatabaseBlockAtSelection(editor: Editor): boolean {
  const type = editor.schema.nodes.databaseBlock
  if (!type) return false

  const attrs = serializeDatabaseBlockData(createDefaultDatabaseBlockData())
  const { empty, $from } = editor.state.selection
  const parent = $from.parent

  if (empty && parent.type.name === 'paragraph' && parent.content.size === 0) {
    const pos = $from.before()
    return editor
      .chain()
      .focus()
      .command(({ tr, dispatch }) => {
        tr.replaceWith(pos, pos + parent.nodeSize, type.create(attrs))
        if (dispatch) dispatch(tr)
        return true
      })
      .run()
  }

  return editor.chain().focus().insertContent({ type: 'databaseBlock', attrs }).run()
}

export function replaceBlockWithDatabase(editor: Editor, blockPos: number, blockNode: PMNode): void {
  const type = editor.schema.nodes.databaseBlock
  if (!type || blockPos < 0 || !blockNode) return
  const attrs = serializeDatabaseBlockData(createDefaultDatabaseBlockData())
  const dbNode = type.create(attrs)
  editor.view.dispatch(editor.state.tr.replaceWith(blockPos, blockPos + blockNode.nodeSize, dbNode))
  editor.commands.focus()
}
