"use client"

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  type ReactNode,
} from "react"
import type { FolderTreeNode, LabelRowItem } from "@/lib/sidebar-nav-data"
import {
  useNavStore,
  type LabelListSidebarVisibility,
  type LabelInMessageListVisibility,
  type NavItemPrefs,
} from "@/lib/stores/nav-store"
import {
  buildEmailLabelToSidebarFolderId,
  buildFolderIdToLabelRecord,
} from "@/lib/sidebar-nav-maps"

export type { LabelListSidebarVisibility, LabelInMessageListVisibility, NavItemPrefs }

export type NavEmailSync = {
  renameLabel: (from: string, to: string) => void
  removeLabel: (label: string) => void
}

const navEmailSyncRef = { current: null as NavEmailSync | null }

export function registerNavEmailSync(cb: NavEmailSync | null) {
  navEmailSyncRef.current = cb
}

type SidebarNavContextValue = {
  folderTree: FolderTreeNode[]
  labelRows: LabelRowItem[]
  folderIdToLabel: Record<string, string>
  emailLabelToSidebarFolderId: Record<string, string>
  getNavItemPrefs: (id: string) => Required<Pick<NavItemPrefs, "sidebar" | "messages">>
  setNavItemSidebarVisibility: (id: string, v: LabelListSidebarVisibility) => void
  setNavItemMessageVisibility: (id: string, v: LabelInMessageListVisibility) => void
  ensureLabelRowForLabelText: (label: string) => void
  addLabelRowFromSidebar: (label: string) => void
  addFolder: (parentId: string | null, name: string) => void
  updateFolderOrLabelColor: (id: string, color: string) => void
  renameFolderOrLabel: (id: string, newLabel: string) => void
  removeFolderOrLabelRow: (id: string) => void
  moveFolder: (id: string, newParentId: string | null) => void
  reorderLabelRows: (
    draggedId: string,
    targetId: string,
    placement: "before" | "after"
  ) => void
  moveFolderRelative: (
    draggedId: string,
    targetId: string,
    placement: "before" | "after" | "inside"
  ) => void
  addSubfolder: (parentId: string, name: string) => void
  addChildLabelRow: (parentLabelRowId: string, childName: string) => void
  setLabelRowEnabled: (id: string, enabled: boolean) => void
}

const SidebarNavContext = createContext<SidebarNavContextValue | null>(null)

type SidebarNavProviderProps = {
  children: ReactNode
  routeFolderId?: string | null
  onRouteFolderIdChange?: (nextFolderId: string) => void
}

export function SidebarNavProvider({
  children,
  routeFolderId,
  onRouteFolderIdChange,
}: SidebarNavProviderProps) {
  const routeSyncRef = useRef({
    routeFolderId: null as string | null | undefined,
    onRouteFolderIdChange: undefined as
      | ((nextFolderId: string) => void)
      | undefined,
  })
  useEffect(() => {
    routeSyncRef.current = { routeFolderId, onRouteFolderIdChange }
  }, [routeFolderId, onRouteFolderIdChange])

  const scheduleRouteFolderIdSync = useCallback(
    (idMap: Record<string, string>) => {
      if (Object.keys(idMap).length === 0) return
      queueMicrotask(() => {
        const { routeFolderId: rid, onRouteFolderIdChange: on } =
          routeSyncRef.current
        if (!rid || !on) return
        const next = idMap[rid]
        if (next) on(next)
      })
    },
    []
  )

  const folderTree = useNavStore((s) => s.folderTree)
  const labelRows = useNavStore((s) => s.labelRows)
  const navItemPrefs = useNavStore((s) => s.navItemPrefs)
  const navActions = useNavStore.getState()

  const folderIdToLabel = useMemo(
    () => buildFolderIdToLabelRecord(folderTree, labelRows),
    [folderTree, labelRows]
  )

  const emailLabelToSidebarFolderId = useMemo(
    () => buildEmailLabelToSidebarFolderId(folderIdToLabel),
    [folderIdToLabel]
  )

  const getNavItemPrefs = useCallback(
    (id: string): Required<Pick<NavItemPrefs, "sidebar" | "messages">> => {
      const p = navItemPrefs[id]
      return {
        sidebar: p?.sidebar ?? "show",
        messages: p?.messages ?? "show",
      }
    },
    [navItemPrefs]
  )

  const renameFolderOrLabel = useCallback(
    (id: string, newLabel: string) => {
      const { idMap, emailRename } = useNavStore.getState().renameFolderOrLabel(id, newLabel)
      scheduleRouteFolderIdSync(idMap)
      if (emailRename) {
        queueMicrotask(() => {
          navEmailSyncRef.current?.renameLabel(emailRename.from, emailRename.to)
        })
      }
    },
    [scheduleRouteFolderIdSync]
  )

  const removeFolderOrLabelRow = useCallback(
    (id: string) => {
      const labelsToRemove = useNavStore.getState().removeFolderOrLabelRow(id)
      if (labelsToRemove.length > 0) {
        queueMicrotask(() => {
          for (const lab of labelsToRemove) {
            navEmailSyncRef.current?.removeLabel(lab)
          }
        })
      }
    },
    []
  )

  const moveFolder = useCallback(
    (id: string, newParentId: string | null) => {
      const idMap = useNavStore.getState().moveFolder(id, newParentId)
      scheduleRouteFolderIdSync(idMap)
    },
    [scheduleRouteFolderIdSync]
  )

  const moveFolderRelative = useCallback(
    (
      draggedId: string,
      targetId: string,
      placement: "before" | "after" | "inside"
    ) => {
      const idMap = useNavStore
        .getState()
        .moveFolderRelative(draggedId, targetId, placement)
      scheduleRouteFolderIdSync(idMap)
    },
    [scheduleRouteFolderIdSync]
  )

  const reorderLabelRows = useCallback(
    (
      draggedId: string,
      targetId: string,
      placement: "before" | "after"
    ) => {
      useNavStore.getState().reorderLabelRows(draggedId, targetId, placement)
    },
    []
  )

  const value = useMemo(
    () => ({
      folderTree,
      labelRows,
      folderIdToLabel,
      emailLabelToSidebarFolderId,
      getNavItemPrefs,
      setNavItemSidebarVisibility: navActions.setNavItemSidebarVisibility,
      setNavItemMessageVisibility: navActions.setNavItemMessageVisibility,
      ensureLabelRowForLabelText: navActions.ensureLabelRowForLabelText,
      addLabelRowFromSidebar: navActions.addLabelRowFromSidebar,
      addFolder: navActions.addFolder,
      updateFolderOrLabelColor: navActions.updateFolderOrLabelColor,
      renameFolderOrLabel,
      removeFolderOrLabelRow,
      moveFolder,
      reorderLabelRows,
      moveFolderRelative,
      addSubfolder: navActions.addSubfolder,
      addChildLabelRow: navActions.addChildLabelRow,
      setLabelRowEnabled: navActions.setLabelRowEnabled,
    }),
    [
      folderTree,
      labelRows,
      folderIdToLabel,
      emailLabelToSidebarFolderId,
      getNavItemPrefs,
      navActions,
      renameFolderOrLabel,
      removeFolderOrLabelRow,
      moveFolder,
      reorderLabelRows,
      moveFolderRelative,
    ]
  )

  return (
    <SidebarNavContext.Provider value={value}>
      {children}
    </SidebarNavContext.Provider>
  )
}

export function useSidebarNav(): SidebarNavContextValue {
  const ctx = useContext(SidebarNavContext)
  if (!ctx) {
    throw new Error("useSidebarNav must be used within SidebarNavProvider")
  }
  return ctx
}

export function folderMoveParentOptions(
  tree: FolderTreeNode[],
  excludeId: string
): { value: string; label: string; depth: number }[] {
  function findNode(nodes: FolderTreeNode[], id: string): FolderTreeNode | null {
    for (const n of nodes) {
      if (n.id === id) return n
      if (n.children?.length) {
        const hit = findNode(n.children, id)
        if (hit) return hit
      }
    }
    return null
  }
  function collectIds(node: FolderTreeNode): Set<string> {
    const s = new Set<string>([node.id])
    if (node.children?.length) {
      for (const c of node.children) {
        for (const x of collectIds(c)) s.add(x)
      }
    }
    return s
  }
  const ex = findNode(tree, excludeId)
  const banned = ex ? collectIds(ex) : new Set([excludeId])
  const out: { value: string; label: string; depth: number }[] = [
    { value: "__root__", label: "Racine", depth: 0 },
  ]
  const walk = (nodes: FolderTreeNode[], depth: number) => {
    for (const n of nodes) {
      if (banned.has(n.id)) continue
      out.push({
        value: n.id,
        label: `${"\u2003".repeat(depth * 2)}${n.label}`,
        depth,
      })
      if (n.children?.length) walk(n.children, depth + 1)
    }
  }
  walk(tree, 0)
  return out
}
