import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection'
import {
  $getNodeByKey,
  CLICK_COMMAND,
  COMMAND_PRIORITY_LOW,
  DecoratorNode,
  NodeKey,
  SerializedLexicalNode,
  Spread,
} from 'lexical'
import { ReactNode, useEffect, useLayoutEffect, useRef, useState } from 'react'
import { Modal, ModalRootProps } from '../../Modal/index.js'
import { Button } from '../../Button/index.js'
import { Note } from '../../Note/index.js'
import { TextAreaInput } from '../../TextAreaInput/index.js'
import { radius } from '../../../utils/radius.js'
import { colors } from '../../../utils/colors.js'
import { shadows } from '../../../utils/shadows.js'

export class EmbedNode extends DecoratorNode<ReactNode> {
  __content: string

  static override getType(): string {
    return 'embed'
  }

  constructor(content: string, key?: NodeKey) {
    super(key)
    this.__content = content
  }

  static override clone(node: EmbedNode): EmbedNode {
    return new EmbedNode(node.__content, node.__key)
  }

  override createDOM() {
    return document.createElement('div')
  }

  override updateDOM() {
    return false
  }

  static override importJSON(
    serializedNode: Spread<{ content: string }, SerializedLexicalNode>,
  ): EmbedNode {
    return new EmbedNode(decodeURIComponent(serializedNode.content))
  }

  override exportJSON(): Spread<{ content: string }, SerializedLexicalNode> {
    return {
      version: 1,
      type: 'embed',
      content: encodeURIComponent(this.__content),
    }
  }

  override isIsolated(): false {
    return false
  }

  override isInline(): false {
    return false
  }

  canInsertTextBefore(): false {
    return false
  }

  canInsertTextAfter(): false {
    return false
  }

  override decorate() {
    return <Embed content={this.__content} nodeKey={this.__key} />
  }
}

function Embed({ content, nodeKey }: { content: string; nodeKey: NodeKey }) {
  const ref = useRef<HTMLDivElement>(null)
  const [editor] = useLexicalComposerContext()
  const [isSelected, setSelected, clearSelection] =
    useLexicalNodeSelection(nodeKey)
  const [editModalOpen, setEditModalOpen] = useState(false)

  useLayoutEffect(() => {
    return editor.registerCommand(
      CLICK_COMMAND,
      (event) => {
        if (ref.current && ref.current.contains(event.target as Node)) {
          clearSelection()
          setSelected(true)
          return true
        }

        return false
      },
      COMMAND_PRIORITY_LOW,
    )
  }, [editor, clearSelection, setSelected])

  return (
    <div
      ref={ref}
      style={{
        position: 'relative',
        width: '100%',
        minHeight: 250,
        borderRadius: radius[8],
        padding: 32,
        background: colors.neutral5,
        border: `1px solid ${colors.neutral20Adjusted}`,
        ...(isSelected && {
          border: `1px solid ${colors.neutral100}`,
          outline: `4px solid ${colors.neutral20}`,
        }),
      }}
    >
      <div
        dangerouslySetInnerHTML={{ __html: content }}
        style={{
          pointerEvents: 'none',
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
        }}
      />
      {isSelected && (
        <div
          style={{
            position: 'absolute',
            right: 16,
            top: 16,
            background: colors.neutralInverted100,
            borderRadius: radius[12],
            boxShadow: shadows.popoverSmall,
          }}
        >
          <div
            style={{
              display: 'flex',
              justifyContent: 'center',
              alignItems: 'center',
              gap: 4,
              padding: 4,
              background: colors.neutral10Background,
              borderRadius: radius[12],
              border: `1px solid ${colors.neutral10Adjusted}`,
            }}
          >
            <Button
              size="small"
              variant="ghost"
              leadIcon="edit"
              onClick={(e) => {
                e.stopPropagation()
                setEditModalOpen(true)
              }}
            >
              Edit
            </Button>
            {editModalOpen && (
              <EmbedModal
                onOpenChange={setEditModalOpen}
                defaultValue={content}
                onSave={(value) => {
                  editor.update(() => {
                    const node: EmbedNode = $getNodeByKey(nodeKey)
                    const writeable = node.getWritable()
                    writeable.__content = value
                  })
                }}
              />
            )}
            <div
              style={{ width: 1, background: colors.neutral10, height: 16 }}
            />
            <Button
              size="small"
              variant="ghost"
              color="red"
              leadIcon="delete"
              onClick={(e) => {
                e.stopPropagation()
                editor.update(() => {
                  const node: EmbedNode = $getNodeByKey(nodeKey)
                  node.remove()
                })
              }}
            >
              Remove
            </Button>
          </div>
        </div>
      )}
    </div>
  )
}

type EmbedModalProps = {
  defaultValue?: string
  onSave: (value: string) => void
} & Pick<ModalRootProps, 'onOpenChange'>

export function EmbedModal({
  onOpenChange,
  onSave,
  defaultValue,
}: EmbedModalProps) {
  const [value, setValue] = useState(defaultValue)

  return (
    <Modal open onOpenChange={onOpenChange} size="large">
      <Modal.Header title="Embed code" />
      <Modal.Body>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <Note
            title="Remove dimensions for automatic resizing"
            description="If you would like your embedded frame to be resized automatically, remove dimensions from the code."
          />
          <TextAreaInput
            placeholder="Enter your code here"
            value={value}
            onChange={setValue}
            rows={16}
            autoFocus
            onFocus={(e) => {
              e.target.select()
            }}
          />
        </div>
      </Modal.Body>
      <Modal.Actions>
        {({ close }) => (
          <>
            <Button variant="border" onClick={close}>
              Cancel
            </Button>
            <Button
              onClick={() => {
                onSave(value)
                close()
              }}
            >
              Save
            </Button>
          </>
        )}
      </Modal.Actions>
    </Modal>
  )
}
