import { memo, useMemo, useRef } from "react"
import { css } from "@emotion/css"
import { useEventListener } from "@aet/hooks"
import { gql, useQuery } from "~/generated/graphql"
import { Allotment, type AllotmentHandle, Pane } from "~/vendor/allotment"
import { token } from "./theme"
import { Tab as PaneTab, Panels } from "./panels"
import { Editor } from "./editor"
import { Sidebar, SidebarTab, useActiveSidebarTab } from "./tabs/Sidebar"
import { S, useLocalStorageState } from "../shared/hooks/useLocalStorage"
import { ErrorBoundary } from "~/shared/ErrorBoundary"

export const enum PanelPosition {
  Right = "Right",
  Bottom = "Bottom",
}

const AppQuery = gql<"AppQuery">`
  query AppQuery {
    preferences {
      debugMode
    }
  }
`

const getAllotmentCacheKey = (key: string) => `options.allotment.${key}PaneSize`

export default function App(): React.ReactElement {
  const [activeTab, setActiveTab] = useLocalStorageState<SidebarTab | null>(
    S.ActiveTab,
    SidebarTab.Files,
  )
  const [allotmentSize, setAllotmentSize] = useLocalStorageState(
    getAllotmentCacheKey("tab"),
    400,
  )
  const debugMode = useQuery(AppQuery).data?.preferences.debugMode ?? false
  const tab = useActiveSidebarTab(activeTab)

  return (
    <div
      className={css`
        background-color: ${token.pageHeaderBg};
        border-radius: 5px;
        height: 100%;
      `}
    >
      <Allotment
        onChange={sizes => {
          if (sizes.length > 1) {
            setAllotmentSize(sizes[1])
          }
        }}
        className={css`
          background-color: ${token.canvas.default};
        `}
      >
        <Pane key="sidebar" minSize={46} maxSize={46}>
          <Sidebar
            activeTab={activeTab}
            setActiveTab={setActiveTab}
            debugMode={debugMode}
          />
        </Pane>
        {activeTab != null && (
          <Pane preferredSize={allotmentSize} maxSize={370} key="activeTab">
            <div
              className={css`
                height: 100%;
              `}
            >
              {tab}
            </div>
          </Pane>
        )}
        <MainPane />
      </Allotment>
    </div>
  )
}

const MainPane = memo(() => {
  const allotment = useRef<AllotmentHandle>(null)
  const paneRef = useRef<HTMLDivElement>(null)
  const [paneVisible, setPaneVisible] = useLocalStorageState(S.PanelVisible, false)
  const paneVisibleRef = useRef(paneVisible)

  const [panelPosition, setPanelPosition] = useLocalStorageState(
    S.PanelPosition,
    PanelPosition.Bottom,
  )
  const [activePaneTab, setActivePaneTab] = useLocalStorageState<PaneTab>(
    S.ActivePaneTab,
    PaneTab.Rules,
  )

  const isRight = panelPosition === PanelPosition.Right

  const [allotmentSizes, setAllotmentSize] = useAllotmentSizes({
    editor: isRight ? "50%" : "60%",
    bottomPanel: undefined,
    rightPanel: undefined,
  })

  const panelSize = isRight ? allotmentSizes.rightPanel : allotmentSizes.bottomPanel

  const onSubAllotmentSizeChange = (sizes: number[]) => {
    const paneVisible = paneVisibleRef.current
    if (!paneVisible) {
      return
    }
    if (sizes.length) {
      setAllotmentSize("editor", sizes[0])
    }
    if (sizes.length > 1) {
      setAllotmentSize(isRight ? "rightPanel" : "bottomPanel", sizes[1])
    }
  }

  useEventListener(window)("keydown", e => {
    if (e.ctrlKey && e.key === "`") {
      e.preventDefault()
      setPaneVisible(!paneVisible)
      paneVisibleRef.current = !paneVisible
    }
  })

  const panelSizeVar = useMemo(
    () => ({ "--panel-size": panelSize + "px" }) as React.CSSProperties,
    [panelSize],
  )

  return (
    <Allotment
      key={!isRight ? "middle" : "right"}
      vertical={!isRight}
      onChange={onSubAllotmentSizeChange}
      ref={allotment}
    >
      <Pane preferredSize={allotmentSizes.editor} snap key="editor">
        <ErrorBoundary>
          <Editor />
        </ErrorBoundary>
      </Pane>
      <Pane
        ref={paneRef}
        preferredSize={panelSize}
        snap
        key="panel"
        visible={paneVisible}
      >
        <Panels
          style={panelSizeVar}
          pane={activePaneTab}
          setPane={setActivePaneTab}
          position={panelPosition}
          setPosition={setPanelPosition}
        />
      </Pane>
    </Allotment>
  )
})

function useAllotmentSizes<K extends string>(initialSize: {
  [key in K]?: string | number
}) {
  const keys = Object.keys(initialSize) as K[]
  const hooks = keys.map(key =>
    // eslint-disable-next-line react-hooks/rules-of-hooks
    useLocalStorageState(getAllotmentCacheKey(key), initialSize[key]),
  )

  const set = (key: K, size: string | number) => {
    const keyIndex = keys.indexOf(key)
    hooks[keyIndex][1](size)
  }

  const sizes = Object.fromEntries(keys.map((key, index) => [key, hooks[index][0]])) as {
    [key in K]: string | number
  }

  return [sizes, set] as const
}
