import { For, Match, Show, Switch, createSignal, splitProps } from "solid-js"
import { IconButton, Tabs, Tooltip } from "@opencode-ai/ui"
import { FileIcon } 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 (
    <DragDropProvider
      onDragStart={handleDragStart}
      onDragEnd={handleDragEnd}
      onDragOver={handleDragOver}
      collisionDetector={closestCenter}
    >
      <DragDropSensors />
      <ConstrainDragYAxis />
      <Tabs value={local.file.active()?.path} onChange={handleTabChange}>
        <div class="sticky top-0 shrink-0 flex">
          <Tabs.List>
            <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="hidden 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 icon="arrow-up" variant="ghost" onClick={() => navigateChange(-1)} />
                        </Tooltip>
                        <Tooltip value="Next change" placement="bottom">
                          <IconButton icon="arrow-down" variant="ghost" onClick={() => navigateChange(1)} />
                        </Tooltip>
                      </div>
                    </Show>
                    <Tooltip value="Raw" placement="bottom">
                      <IconButton
                        icon="file-text"
                        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")}
                      />
                    </Tooltip>
                    <Tooltip value="Unified diff" placement="bottom">
                      <IconButton
                        icon="checklist"
                        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")}
                      />
                    </Tooltip>
                    <Tooltip value="Split diff" placement="bottom">
                      <IconButton
                        icon="columns"
                        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")}
                      />
                    </Tooltip>
                  </div>
                )
              })()}
            </Show>
          </div>
        </div>
        <For each={local.file.opened()}>
          {(file) => (
            <Tabs.Content value={file.path} class="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>
  )
}

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
            icon="close"
            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"
            variant="ghost"
            onClick={() => props.onTabClose(props.file)}
          />
        </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
}
