import { For, Match, Show, Switch, createSignal, splitProps } from "solid-js"
import { Tabs } from "@/ui/tabs"
import { FileIcon, Icon, IconButton, Logo, Tooltip } from "@/ui"
import {
  DragDropProvider,
  DragDropSensors,
  DragOverlay,
  SortableProvider,
  closestCenter,
  createSortable,
  useDragDropContext,
} from "@thisbeyond/solid-dnd"
import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd"
import type { LocalFile } from "@/context/local"
import { Code } from "@/components/code"
import { useLocal } from "@/context"
import type { JSX } from "solid-js"

interface EditorPaneProps {
  onFileClick: (file: LocalFile) => void
}

export default function EditorPane(props: EditorPaneProps): JSX.Element {
  const [localProps] = splitProps(props, ["onFileClick"])
  const local = useLocal()
  const [activeItem, setActiveItem] = createSignal<string | undefined>(undefined)

  const navigateChange = (dir: 1 | -1) => {
    const active = local.file.active()
    if (!active) return
    const current = local.file.changeIndex(active.path)
    const next = current === undefined ? (dir === 1 ? 0 : -1) : current + dir
    local.file.setChangeIndex(active.path, next)
  }

  const handleTabChange = (path: string) => {
    local.file.open(path)
  }

  const handleTabClose = (file: LocalFile) => {
    local.file.close(file.path)
  }

  const handleDragStart = (event: unknown) => {
    const id = getDraggableId(event)
    if (!id) return
    setActiveItem(id)
  }

  const handleDragOver = (event: DragEvent) => {
    const { draggable, droppable } = event
    if (draggable && droppable) {
      const currentFiles = local.file.opened().map((file) => file.path)
      const fromIndex = currentFiles.indexOf(draggable.id.toString())
      const toIndex = currentFiles.indexOf(droppable.id.toString())
      if (fromIndex !== toIndex) {
        local.file.move(draggable.id.toString(), toIndex)
      }
    }
  }

  const handleDragEnd = () => {
    setActiveItem(undefined)
  }

  return (
    <div class="relative flex h-full flex-col">
      <DragDropProvider
        onDragStart={handleDragStart}
        onDragEnd={handleDragEnd}
        onDragOver={handleDragOver}
        collisionDetector={closestCenter}
      >
        <DragDropSensors />
        <ConstrainDragYAxis />
        <Tabs
          class="relative grow w-full flex flex-col h-full"
          value={local.file.active()?.path}
          onChange={handleTabChange}
        >
          <div class="sticky top-0 shrink-0 flex">
            <Tabs.List class="grow">
              <SortableProvider ids={local.file.opened().map((file) => file.path)}>
                <For each={local.file.opened()}>
                  {(file) => (
                    <SortableTab file={file} onTabClick={localProps.onFileClick} onTabClose={handleTabClose} />
                  )}
                </For>
              </SortableProvider>
            </Tabs.List>
            <div class="shrink-0 h-full flex items-center gap-1 px-2 border-b border-border-subtle/40">
              <Show when={local.file.active() && local.file.active()!.content?.diff}>
                {(() => {
                  const activeFile = local.file.active()!
                  const view = local.file.view(activeFile.path)
                  return (
                    <div class="flex items-center gap-1">
                      <Show when={view !== "raw"}>
                        <div class="mr-1 flex items-center gap-1">
                          <Tooltip value="Previous change" placement="bottom">
                            <IconButton size="xs" variant="ghost" onClick={() => navigateChange(-1)}>
                              <Icon name="arrow-up" size={14} />
                            </IconButton>
                          </Tooltip>
                          <Tooltip value="Next change" placement="bottom">
                            <IconButton size="xs" variant="ghost" onClick={() => navigateChange(1)}>
                              <Icon name="arrow-down" size={14} />
                            </IconButton>
                          </Tooltip>
                        </div>
                      </Show>
                      <Tooltip value="Raw" placement="bottom">
                        <IconButton
                          size="xs"
                          variant="ghost"
                          classList={{
                            "text-text": view === "raw",
                            "text-text-muted/70": view !== "raw",
                            "bg-background-element": view === "raw",
                          }}
                          onClick={() => local.file.setView(activeFile.path, "raw")}
                        >
                          <Icon name="file-text" size={14} />
                        </IconButton>
                      </Tooltip>
                      <Tooltip value="Unified diff" placement="bottom">
                        <IconButton
                          size="xs"
                          variant="ghost"
                          classList={{
                            "text-text": view === "diff-unified",
                            "text-text-muted/70": view !== "diff-unified",
                            "bg-background-element": view === "diff-unified",
                          }}
                          onClick={() => local.file.setView(activeFile.path, "diff-unified")}
                        >
                          <Icon name="checklist" size={14} />
                        </IconButton>
                      </Tooltip>
                      <Tooltip value="Split diff" placement="bottom">
                        <IconButton
                          size="xs"
                          variant="ghost"
                          classList={{
                            "text-text": view === "diff-split",
                            "text-text-muted/70": view !== "diff-split",
                            "bg-background-element": view === "diff-split",
                          }}
                          onClick={() => local.file.setView(activeFile.path, "diff-split")}
                        >
                          <Icon name="columns" size={14} />
                        </IconButton>
                      </Tooltip>
                    </div>
                  )
                })()}
              </Show>
            </div>
          </div>
          <For each={local.file.opened()}>
            {(file) => (
              <Tabs.Content value={file.path} class="grow h-full pt-1 select-text">
                {(() => {
                  const view = local.file.view(file.path)
                  const showRaw = view === "raw" || !file.content?.diff
                  const code = showRaw ? (file.content?.content ?? "") : (file.content?.diff ?? "")
                  return <Code path={file.path} code={code} class="[&_code]:pb-60" />
                })()}
              </Tabs.Content>
            )}
          </For>
        </Tabs>
        <DragOverlay>
          {(() => {
            const id = activeItem()
            if (!id) return null
            const draggedFile = local.file.node(id)
            if (!draggedFile) return null
            return (
              <div class="relative px-3 h-8 flex items-center text-sm font-medium text-text whitespace-nowrap shrink-0 bg-background-panel border-x border-border-subtle/40 border-b border-b-transparent">
                <TabVisual file={draggedFile} />
              </div>
            )
          })()}
        </DragOverlay>
      </DragDropProvider>
    </div>
  )
}

function TabVisual(props: { file: LocalFile }): JSX.Element {
  return (
    <div class="flex items-center gap-x-1.5">
      <FileIcon node={props.file} class="" />
      <span classList={{ "text-xs": true, "text-primary": !!props.file.status?.status, italic: !props.file.pinned }}>
        {props.file.name}
      </span>
      <span class="text-xs opacity-70">
        <Switch>
          <Match when={props.file.status?.status === "modified"}>
            <span class="text-primary">M</span>
          </Match>
          <Match when={props.file.status?.status === "added"}>
            <span class="text-success">A</span>
          </Match>
          <Match when={props.file.status?.status === "deleted"}>
            <span class="text-error">D</span>
          </Match>
        </Switch>
      </span>
    </div>
  )
}

function SortableTab(props: {
  file: LocalFile
  onTabClick: (file: LocalFile) => void
  onTabClose: (file: LocalFile) => void
}): JSX.Element {
  const sortable = createSortable(props.file.path)

  return (
    // @ts-ignore
    <div use:sortable classList={{ "opacity-0": sortable.isActiveDraggable }}>
      <Tooltip value={props.file.path} placement="bottom">
        <div class="relative">
          <Tabs.Trigger value={props.file.path} class="peer/tab pr-7" onClick={() => props.onTabClick(props.file)}>
            <TabVisual file={props.file} />
          </Tabs.Trigger>
          <IconButton
            class="absolute right-1 top-1.5 opacity-0 text-text-muted/60 peer-data-[selected]/tab:opacity-100 peer-data-[selected]/tab:text-text peer-data-[selected]/tab:hover:bg-border-subtle hover:opacity-100 peer-hover/tab:opacity-100"
            size="xs"
            variant="ghost"
            onClick={() => props.onTabClose(props.file)}
          >
            <Icon name="close" size={16} />
          </IconButton>
        </div>
      </Tooltip>
    </div>
  )
}

function ConstrainDragYAxis(): JSX.Element {
  const context = useDragDropContext()
  if (!context) return <></>
  const [, { onDragStart, onDragEnd, addTransformer, removeTransformer }] = context
  const transformer: Transformer = {
    id: "constrain-y-axis",
    order: 100,
    callback: (transform) => ({ ...transform, y: 0 }),
  }
  onDragStart((event) => {
    const id = getDraggableId(event)
    if (!id) return
    addTransformer("draggables", id, transformer)
  })
  onDragEnd((event) => {
    const id = getDraggableId(event)
    if (!id) return
    removeTransformer("draggables", id, transformer.id)
  })
  return <></>
}

const getDraggableId = (event: unknown): string | undefined => {
  if (typeof event !== "object" || event === null) return undefined
  if (!("draggable" in event)) return undefined
  const draggable = (event as { draggable?: { id?: unknown } }).draggable
  if (!draggable) return undefined
  return typeof draggable.id === "string" ? draggable.id : undefined
}
