import { FileIcon, Icon, IconButton, Tooltip } from "@/ui"
import * as KobalteTabs from "@kobalte/core/tabs"
import FileTree from "@/components/file-tree"
import EditorPane from "@/components/editor-pane"
import { For, Match, onCleanup, onMount, Show, Switch } from "solid-js"
import { SelectDialog } from "@/components/select-dialog"
import { useSync, useSDK, useLocal } from "@/context"
import type { LocalFile, TextSelection } from "@/context/local"
import SessionList from "@/components/session-list"
import SessionTimeline from "@/components/session-timeline"
import PromptForm, { type PromptContentPart, type PromptSubmitValue } from "@/components/prompt-form"
import { createStore } from "solid-js/store"
import { getDirectory, getFilename } from "@/utils"
import { Select } from "@/components/select"
import { Tabs } from "@/ui/tabs"
import { Code } from "@/components/code"

export default function Page() {
  const local = useLocal()
  const sync = useSync()
  const sdk = useSDK()
  const [store, setStore] = createStore({
    clickTimer: undefined as number | undefined,
    modelSelectOpen: false,
    fileSelectOpen: false,
  })

  let inputRef: HTMLTextAreaElement | undefined = undefined

  const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control"

  onMount(() => {
    document.addEventListener("keydown", handleKeyDown)
  })

  onCleanup(() => {
    document.removeEventListener("keydown", handleKeyDown)
  })

  const handleKeyDown = (event: KeyboardEvent) => {
    if (event.getModifierState(MOD) && event.shiftKey && event.key.toLowerCase() === "p") {
      event.preventDefault()
      // TODO: command palette
      return
    }
    if (event.getModifierState(MOD) && event.key.toLowerCase() === "p") {
      event.preventDefault()
      setStore("fileSelectOpen", true)
      return
    }

    const focused = document.activeElement === inputRef
    if (focused) {
      if (event.key === "Escape") {
        inputRef?.blur()
      }
      return
    }

    if (local.file.active()) {
      const active = local.file.active()!
      if (event.key === "Enter" && active.selection) {
        local.context.add({
          type: "file",
          path: active.path,
          selection: { ...active.selection },
        })
        return
      }

      if (event.getModifierState(MOD)) {
        if (event.key.toLowerCase() === "a") {
          return
        }
        if (event.key.toLowerCase() === "c") {
          return
        }
      }
    }

    if (event.key.length === 1 && event.key !== "Unidentified") {
      inputRef?.focus()
    }
  }

  const resetClickTimer = () => {
    if (!store.clickTimer) return
    clearTimeout(store.clickTimer)
    setStore("clickTimer", undefined)
  }

  const startClickTimer = () => {
    const newClickTimer = setTimeout(() => {
      setStore("clickTimer", undefined)
    }, 300)
    setStore("clickTimer", newClickTimer as unknown as number)
  }

  const handleFileClick = async (file: LocalFile) => {
    if (store.clickTimer) {
      resetClickTimer()
      local.file.update(file.path, { ...file, pinned: true })
    } else {
      local.file.open(file.path)
      startClickTimer()
    }
  }

  const handlePromptSubmit = async (prompt: PromptSubmitValue) => {
    const existingSession = local.session.active()
    let session = existingSession
    if (!session) {
      const created = await sdk.session.create()
      session = created.data ?? undefined
    }
    if (!session) return
    local.session.setActive(session.id)

    interface SubmissionAttachment {
      path: string
      selection?: TextSelection
      label: string
    }

    const createAttachmentKey = (path: string, selection?: TextSelection) => {
      if (!selection) return path
      return `${path}:${selection.startLine}:${selection.startChar}:${selection.endLine}:${selection.endChar}`
    }

    const formatAttachmentLabel = (path: string, selection?: TextSelection) => {
      if (!selection) return getFilename(path)
      return `${getFilename(path)} (${selection.startLine}-${selection.endLine})`
    }

    const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path))

    const attachments = new Map<string, SubmissionAttachment>()

    const registerAttachment = (path: string, selection: TextSelection | undefined, label?: string) => {
      if (!path) return
      const key = createAttachmentKey(path, selection)
      if (attachments.has(key)) return
      attachments.set(key, {
        path,
        selection,
        label: label ?? formatAttachmentLabel(path, selection),
      })
    }

    const promptAttachments = prompt.parts.filter(
      (part): part is Extract<PromptContentPart, { kind: "attachment" }> => part.kind === "attachment",
    )

    for (const part of promptAttachments) {
      registerAttachment(part.path, part.selection, part.display)
    }

    const activeFile = local.context.active()
    if (activeFile) {
      registerAttachment(
        activeFile.path,
        activeFile.selection,
        activeFile.name ?? formatAttachmentLabel(activeFile.path, activeFile.selection),
      )
    }

    for (const contextFile of local.context.all()) {
      registerAttachment(
        contextFile.path,
        contextFile.selection,
        formatAttachmentLabel(contextFile.path, contextFile.selection),
      )
    }

    const attachmentParts = Array.from(attachments.values()).map((attachment) => {
      const absolute = toAbsolutePath(attachment.path)
      const query = attachment.selection
        ? `?start=${attachment.selection.startLine}&end=${attachment.selection.endLine}`
        : ""
      return {
        type: "file" as const,
        mime: "text/plain",
        url: `file://${absolute}${query}`,
        filename: getFilename(attachment.path),
        source: {
          type: "file" as const,
          text: {
            value: `@${attachment.label}`,
            start: 0,
            end: 0,
          },
          path: absolute,
        },
      }
    })

    await sdk.session.prompt({
      path: { id: session.id },
      body: {
        agent: local.agent.current()!.name,
        model: {
          modelID: local.model.current()!.id,
          providerID: local.model.current()!.provider.id,
        },
        parts: [
          {
            type: "text",
            text: prompt.text,
          },
          ...attachmentParts,
        ],
      },
    })
  }

  return (
    <div class="relative">
      <div class="h-screen flex">
        <div class="shrink-0 w-56">
          <SessionList />
        </div>
        <div class="grow w-full min-w-0 overflow-y-auto flex justify-center">
          <Show when={local.session.active()}>
            {(activeSession) => <SessionTimeline session={activeSession().id} class="max-w-xl" />}
          </Show>
        </div>
        <div class="hidden shrink-0 w-56 p-2 h-full overflow-y-auto">
          <FileTree path="" onFileClick={handleFileClick} />
        </div>
        <div class="hidden shrink-0 w-56 p-2">
          <Show
            when={local.file.changes().length}
            fallback={<div class="px-2 text-xs text-text-muted">No changes</div>}
          >
            <ul class="">
              <For each={local.file.changes()}>
                {(path) => (
                  <li>
                    <button
                      onClick={() => local.file.open(path, { view: "diff-unified", pinned: true })}
                      class="w-full flex items-center px-2 py-0.5 gap-x-2 text-text-muted grow min-w-0 cursor-pointer hover:bg-background-element"
                    >
                      <FileIcon node={{ path, type: "file" }} class="shrink-0 size-3" />
                      <span class="text-xs text-text whitespace-nowrap">{getFilename(path)}</span>
                      <span class="text-xs text-text-muted/60 whitespace-nowrap truncate min-w-0">
                        {getDirectory(path)}
                      </span>
                    </button>
                  </li>
                )}
              </For>
            </ul>
          </Show>
        </div>
        <div class="hidden grow min-w-0">
          <EditorPane onFileClick={handleFileClick} />
        </div>
        <div class="absolute bottom-4 right-4 border border-border-subtle/60 p-2 rounded-xl bg-background w-xl flex flex-col gap-2 z-50">
          <div class="flex items-center gap-2">
            <Select
              options={sync.data.session}
              current={local.session.active()}
              placeholder="New Session"
              value={(x) => x.id}
              label={(x) => x.title}
              onSelect={(s) => local.session.setActive(s?.id)}
              class="bg-transparent! max-w-48 pl-0! text-text-muted!"
            />
            <Show when={local.session.active()}>
              <>
                <div>/</div>
                <Select
                  options={sync.data.message[local.session.active()!.id]?.filter((m) => m.role === "user") ?? []}
                  label={(m) => sync.data.part[m.id].find((p) => p.type === "text")!.text}
                  class="bg-transparent! max-w-48 pl-0! text-text-muted!"
                />
              </>
            </Show>
          </div>
          <div class="h-72 text-xs overflow-x-scroll no-scrollbar w-full min-w-0">
            <Tabs
              class="relative grow w-full flex flex-col gap-1 h-full"
              value={local.context.activeFile()?.path}
              onChange={local.context.setActiveFile}
            >
              <div class="sticky top-0 shrink-0 flex items-center gap-1">
                <IconButton
                  class="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="secondary"
                  onClick={() => setStore("fileSelectOpen", true)}
                >
                  <Icon name="plus" size={12} />
                </IconButton>
                <Tabs.List class="grow after:hidden! h-full divide-none! gap-1">
                  <For each={local.context.files()}>
                    {(file) => (
                      <KobalteTabs.Trigger
                        value={file.path}
                        class="h-full"
                        // onClick={() => props.onTabClick(props.file)}
                      >
                        <div class="flex items-center gap-x-1 rounded-md bg-background-panel px-2 h-full">
                          <FileIcon node={file} class="shrink-0 size-3!" />
                          <span class="text-xs text-text whitespace-nowrap">{getFilename(file.path)}</span>
                        </div>
                      </KobalteTabs.Trigger>
                    )}
                  </For>
                </Tabs.List>
              </div>
              <For each={local.context.files()}>
                {(file) => (
                  <Tabs.Content value={file.path} class="grow h-full pt-1 select-text rounded-md">
                    <Code path={file.path} code={file.content?.content ?? ""} />
                  </Tabs.Content>
                )}
              </For>
            </Tabs>
          </div>
          <PromptForm
            onSubmit={handlePromptSubmit}
            onOpenModelSelect={() => setStore("modelSelectOpen", true)}
            onInputRefChange={(element: HTMLTextAreaElement | undefined) => {
              inputRef = element ?? undefined
            }}
          />
          <div class="hidden relative flex-1 min-h-0 overflow-y-auto overflow-x-hidden">
            <Show when={local.session.active()}>
              {(activeSession) => (
                <div class="relative">
                  <div class="sticky top-0 bg-background z-50 px-2 h-8 border-b border-border-subtle/30">
                    <div class="h-full flex items-center gap-2">
                      <h2 class="text-sm font-medium text-text truncate">
                        {activeSession().title || "Untitled Session"}
                      </h2>
                    </div>
                  </div>
                  <SessionTimeline session={activeSession().id} />
                </div>
              )}
            </Show>
          </div>
        </div>
      </div>
      <Show when={store.modelSelectOpen}>
        <SelectDialog
          key={(x) => `${x.provider.id}:${x.id}`}
          items={local.model.list()}
          current={local.model.current()}
          render={(i) => (
            <div class="w-full flex items-center justify-between">
              <div class="flex items-center gap-x-2 text-text-muted grow min-w-0">
                <img src={`https://models.dev/logos/${i.provider.id}.svg`} class="size-4 invert opacity-40" />
                <span class="text-xs text-text whitespace-nowrap">{i.name}</span>
                <span class="text-xs text-text-muted/80 whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
                  {i.id}
                </span>
              </div>
              <div class="flex items-center gap-x-1 text-text-muted/40 shrink-0">
                <Tooltip forceMount={false} value="Reasoning">
                  <Icon name="brain" size={16} classList={{ "text-accent": i.reasoning }} />
                </Tooltip>
                <Tooltip forceMount={false} value="Tools">
                  <Icon name="hammer" size={16} classList={{ "text-secondary": i.tool_call }} />
                </Tooltip>
                <Tooltip forceMount={false} value="Attachments">
                  <Icon name="photo" size={16} classList={{ "text-success": i.attachment }} />
                </Tooltip>
                <div class="rounded-full bg-text-muted/20 text-text-muted/80 w-9 h-4 flex items-center justify-center text-[10px]">
                  {new Intl.NumberFormat("en-US", {
                    notation: "compact",
                    compactDisplay: "short",
                  }).format(i.limit.context)}
                </div>
                <Tooltip forceMount={false} value={`$${i.cost?.input}/1M input, $${i.cost?.output}/1M output`}>
                  <div class="rounded-full bg-success/20 text-success/80 w-9 h-4 flex items-center justify-center text-[10px]">
                    <Switch fallback="FREE">
                      <Match when={i.cost?.input > 10}>$$$</Match>
                      <Match when={i.cost?.input > 1}>$$</Match>
                      <Match when={i.cost?.input > 0.1}>$</Match>
                    </Switch>
                  </div>
                </Tooltip>
              </div>
            </div>
          )}
          filter={["provider.name", "name", "id"]}
          groupBy={(x) => x.provider.name}
          onClose={() => setStore("modelSelectOpen", false)}
          onSelect={(x) => local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined)}
        />
      </Show>
      <Show when={store.fileSelectOpen}>
        <SelectDialog
          items={local.file.search}
          key={(x) => x}
          render={(i) => (
            <div class="w-full flex items-center justify-between">
              <div class="flex items-center gap-x-2 text-text-muted grow min-w-0">
                <FileIcon node={{ path: i, type: "file" }} class="shrink-0 size-4" />
                <span class="text-xs text-text whitespace-nowrap">{getFilename(i)}</span>
                <span class="text-xs text-text-muted/80 whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
                  {getDirectory(i)}
                </span>
              </div>
              <div class="flex items-center gap-x-1 text-text-muted/40 shrink-0"></div>
            </div>
          )}
          onClose={() => setStore("fileSelectOpen", false)}
          onSelect={(x) => (x ? local.context.openFile(x) : undefined)}
          // onSelect={(x) => (x ? local.file.open(x, { pinned: true }) : undefined)}
        />
      </Show>
    </div>
  )
}
