import { useCallback, useState, type FormEvent } from 'react'
import type { EntityTarget, MenuItemNode, MenuTarget } from './client.js'
import { useMenuTree } from './hooks.js'

export interface MenuEditorProps {
  menuKey: string
  availableTargets: EntityTarget[]
}

function findNode(items: MenuItemNode[], itemId: string): MenuItemNode | null {
  for (const item of items) {
    if (item.id === itemId) return item
    const found = findNode(item.children, itemId)
    if (found) return found
  }
  return null
}

function targetFromNode(item: MenuItemNode): string {
  if (item.targetKind === 'entity' && item.targetEntityType && item.targetEntityId) {
    return `entity:${item.targetEntityType}:${item.targetEntityId}`
  }
  return 'custom'
}

function parseTargetChoice(
  value: string,
  customUrl: string,
  availableTargets: EntityTarget[],
): MenuTarget {
  if (value === 'custom') {
    return { kind: 'url', url: customUrl }
  }
  if (value.startsWith('entity:')) {
    const [, entityType, entityId] = value.split(':')
    if (entityType && entityId) {
      return { kind: 'entity', entityType, entityId }
    }
  }
  const match = availableTargets.find((t) => `entity:${t.entityType}:${t.entityId}` === value)
  if (match) {
    return { kind: 'entity', entityType: match.entityType, entityId: match.entityId }
  }
  return { kind: 'url', url: customUrl }
}

type TreeActions = {
  menuId: string
  reorder: (menuId: string, parentId: string | null, orderedIds: string[]) => Promise<void>
  moveItem: (
    itemId: string,
    input: { newParentId: string | null; newPosition: number },
  ) => Promise<unknown>
  removeItem: (itemId: string) => Promise<void>
  updateItem: (
    itemId: string,
    patch: { label?: string; target?: MenuTarget; openInNew?: boolean },
  ) => Promise<unknown>
}

function MenuTreeItem({
  item,
  siblings,
  index,
  availableTargets,
  actions,
  allItems,
}: {
  item: MenuItemNode
  siblings: MenuItemNode[]
  index: number
  availableTargets: EntityTarget[]
  actions: TreeActions
  allItems: MenuItemNode[]
}) {
  const [label, setLabel] = useState(item.label)
  const [targetChoice, setTargetChoice] = useState(targetFromNode(item))
  const [customUrl, setCustomUrl] = useState(item.url ?? '')

  const commitLabel = useCallback(() => {
    if (label !== item.label) void actions.updateItem(item.id, { label }).catch(() => {})
  }, [actions, item.id, item.label, label])

  const commitTarget = useCallback(
    (nextChoice: string, nextUrl: string) => {
      void actions
        .updateItem(item.id, {
          target: parseTargetChoice(nextChoice, nextUrl, availableTargets),
        })
        .catch(() => {})
    },
    [actions, availableTargets, item.id],
  )

  const handleMoveUp = useCallback(() => {
    if (index === 0) return
    const orderedIds = siblings.map((s) => s.id)
    ;[orderedIds[index - 1], orderedIds[index]] = [orderedIds[index]!, orderedIds[index - 1]!]
    void actions.reorder(actions.menuId, item.parentId, orderedIds).catch(() => {})
  }, [actions, index, item.parentId, siblings])

  const handleMoveDown = useCallback(() => {
    if (index >= siblings.length - 1) return
    const orderedIds = siblings.map((s) => s.id)
    ;[orderedIds[index], orderedIds[index + 1]] = [orderedIds[index + 1]!, orderedIds[index]!]
    void actions.reorder(actions.menuId, item.parentId, orderedIds).catch(() => {})
  }, [actions, index, item.parentId, siblings])

  const handleIndent = useCallback(() => {
    if (index === 0) return
    const prev = siblings[index - 1]!
    void actions
      .moveItem(item.id, { newParentId: prev.id, newPosition: prev.children.length })
      .catch(() => {})
  }, [actions, index, item.id, siblings])

  const handleOutdent = useCallback(() => {
    if (item.parentId === null) return
    const parent = findNode(allItems, item.parentId)
    if (!parent) return
    void actions
      .moveItem(item.id, {
        newParentId: parent.parentId,
        newPosition: parent.position + 1,
      })
      .catch(() => {})
  }, [actions, allItems, item.id, item.parentId])

  return (
    <li
      role="treeitem"
      aria-expanded={item.children.length > 0 ? true : undefined}
      className="menus-editor__item"
    >
      <input
        type="text"
        aria-label={item.label}
        value={label}
        className="menus-editor__label"
        onChange={(e) => setLabel(e.target.value)}
        onBlur={commitLabel}
      />
      <select
        aria-label={`${item.label} target`}
        value={targetChoice}
        className="menus-editor__target"
        onChange={(e) => {
          const next = e.target.value
          setTargetChoice(next)
          commitTarget(next, customUrl)
        }}
      >
        <option value="custom">Custom URL</option>
        {availableTargets.map((target) => (
          <option
            key={`${target.entityType}:${target.entityId}`}
            value={`entity:${target.entityType}:${target.entityId}`}
          >
            {target.label}
          </option>
        ))}
      </select>
      <input
        type="text"
        aria-label={`${item.label} custom URL`}
        value={customUrl}
        className="menus-editor__url"
        onChange={(e) => setCustomUrl(e.target.value)}
        onBlur={() => commitTarget(targetChoice, customUrl)}
      />
      <button
        type="button"
        aria-label={`Move ${item.label} up`}
        aria-disabled={index === 0}
        onClick={handleMoveUp}
      >
        Move up
      </button>
      <button
        type="button"
        aria-label={`Move ${item.label} down`}
        aria-disabled={index === siblings.length - 1}
        onClick={handleMoveDown}
      >
        Move down
      </button>
      <button
        type="button"
        aria-label={`Indent ${item.label}`}
        aria-disabled={index === 0}
        onClick={handleIndent}
      >
        Indent
      </button>
      <button
        type="button"
        aria-label={`Outdent ${item.label}`}
        aria-disabled={item.parentId === null}
        onClick={handleOutdent}
      >
        Outdent
      </button>
      <button
        type="button"
        aria-label={`Remove ${item.label}`}
        onClick={() => void actions.removeItem(item.id).catch(() => {})}
      >
        Remove
      </button>
      {item.children.length > 0 ? (
        <ul role="group" className="menus-editor__children">
          {item.children.map((child, childIndex) => (
            <MenuTreeItem
              key={child.id}
              item={child}
              siblings={item.children}
              index={childIndex}
              availableTargets={availableTargets}
              actions={actions}
              allItems={allItems}
            />
          ))}
        </ul>
      ) : null}
    </li>
  )
}

function MenuTreeList({
  items,
  availableTargets,
  actions,
  rootItems,
}: {
  items: MenuItemNode[]
  availableTargets: EntityTarget[]
  actions: TreeActions
  rootItems: MenuItemNode[]
}) {
  return (
    <>
      {items.map((item, index) => (
        <MenuTreeItem
          key={item.id}
          item={item}
          siblings={items}
          index={index}
          availableTargets={availableTargets}
          actions={actions}
          allItems={rootItems}
        />
      ))}
    </>
  )
}

export function MenuEditor({ menuKey, availableTargets }: MenuEditorProps) {
  const { tree, loading, error, addItem, updateItem, removeItem, moveItem, reorder } =
    useMenuTree(menuKey)
  const [newLabel, setNewLabel] = useState('')
  const [newTargetChoice, setNewTargetChoice] = useState('custom')
  const [newCustomUrl, setNewCustomUrl] = useState('')

  const actions: TreeActions = {
    menuId: tree?.menu.id ?? '',
    reorder,
    moveItem,
    removeItem,
    updateItem,
  }

  const handleAddItem = useCallback(
    (e: FormEvent) => {
      e.preventDefault()
      if (!tree || !newLabel.trim()) return
      void addItem(tree.menu.id, {
        label: newLabel.trim(),
        target: parseTargetChoice(newTargetChoice, newCustomUrl, availableTargets),
        parentId: null,
      }).catch(() => {})
      setNewLabel('')
      setNewCustomUrl('')
      setNewTargetChoice('custom')
    },
    [addItem, availableTargets, newCustomUrl, newLabel, newTargetChoice, tree],
  )

  if (loading) return <p className="menus-editor__loading">Loading menu…</p>
  if (!tree) return <p className="menus-editor__empty">No menu.</p>

  return (
    <div className="menus-editor" style={{ containerType: 'inline-size' }}>
      {error ? (
        <div role="alert" tabIndex={0} className="menus-editor__error">
          {error.message}
        </div>
      ) : null}
      <ul role="tree" className="menus-editor__tree">
        <MenuTreeList
          items={tree.items}
          availableTargets={availableTargets}
          actions={actions}
          rootItems={tree.items}
        />
      </ul>
      <form className="menus-editor__add" onSubmit={handleAddItem}>
        <input
          type="text"
          aria-label="New item label"
          value={newLabel}
          className="menus-editor__new-label"
          onChange={(e) => setNewLabel(e.target.value)}
        />
        <select
          aria-label="New item target"
          value={newTargetChoice}
          className="menus-editor__new-target"
          onChange={(e) => setNewTargetChoice(e.target.value)}
        >
          <option value="custom">Custom URL</option>
          {availableTargets.map((target) => (
            <option
              key={`new-${target.entityType}:${target.entityId}`}
              value={`entity:${target.entityType}:${target.entityId}`}
            >
              {target.label}
            </option>
          ))}
        </select>
        <input
          type="text"
          aria-label="New item custom URL"
          value={newCustomUrl}
          className="menus-editor__new-url"
          onChange={(e) => setNewCustomUrl(e.target.value)}
        />
        <button type="submit" className="menus-editor__add-button">
          Add menu item
        </button>
      </form>
    </div>
  )
}
