"use client"

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  type ReactNode,
} from "react"
import type { FolderTreeNode, LabelRowItem } from "@/lib/sidebar-nav-data"
import { isSystemNavLabelId } 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"
import { useSidebarNavApiSync } from "@/lib/hooks/use-sidebar-nav-api"
import { isServerNavId } from "@/lib/sidebar-nav-server-id"
import { DEFAULT_NAV_COLOR } from "@/lib/nav-color"
import { findFolderNodeById } from "@/lib/sidebar-folder-tree-utils"
import {
  folderTreeToReorderItems,
  labelRowsToReorderItems,
} from "@/lib/nav-reorder-plan"

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, color?: 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 navApi = useSidebarNavApiSync()

  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 isFolderId = useCallback(
    (id: string) => findFolderNodeById(folderTree, id) != null,
    [folderTree]
  )

  const getLabelRow = useCallback(
    (id: string) => labelRows.find((r) => r.id === id),
    [labelRows]
  )

  const addFolder = useCallback(
    (parentId: string | null, name: string) => {
      if (navApi.apiEnabled) {
        navApi.createFolder({
          name,
          color: "bg-slate-400",
          parent_id: parentId ?? undefined,
        })
        return
      }
      navActions.addFolder(parentId, name)
    },
    [navApi]
  )

  const addSubfolder = useCallback(
    (parentId: string, name: string) => {
      addFolder(parentId, name)
    },
    [addFolder]
  )

  const addLabelRowFromSidebar = useCallback(
    (label: string, color = DEFAULT_NAV_COLOR) => {
      if (navApi.apiEnabled) {
        navApi.createLabel({ name: label.trim(), color })
        return
      }
      navActions.addLabelRowFromSidebar(label)
    },
    [navApi]
  )

  const addChildLabelRow = useCallback(
    (parentLabelRowId: string, childName: string) => {
      const parent = getLabelRow(parentLabelRowId)
      const child = childName.trim()
      if (!child) return
      const fullName = parent ? `${parent.label}/${child}` : child
      if (navApi.apiEnabled) {
        navApi.createLabel({
          name: fullName,
          color: parent?.color ?? DEFAULT_NAV_COLOR,
        })
        return
      }
      navActions.addChildLabelRow(parentLabelRowId, childName)
    },
    [getLabelRow, navApi]
  )

  const updateFolderOrLabelColor = useCallback(
    (id: string, color: string) => {
      if (navApi.apiEnabled && isServerNavId(id)) {
        if (isFolderId(id)) {
          const node = findFolderNodeById(folderTree, id)
          if (node) {
            navApi.updateFolder({
              id,
              name: node.label,
              color,
              parent_id: undefined,
            })
            return
          }
        }
        const row = getLabelRow(id)
        if (row && !isSystemNavLabelId(id)) {
          navApi.updateLabel({ id, name: row.label, color })
          return
        }
      }
      navActions.updateFolderOrLabelColor(id, color)
    },
    [folderTree, getLabelRow, isFolderId, navApi]
  )

  const renameFolderOrLabel = useCallback(
    (id: string, newLabel: string) => {
      if (navApi.apiEnabled && isServerNavId(id)) {
        if (isFolderId(id)) {
          const node = findFolderNodeById(folderTree, id)
          if (node) {
            navApi.updateFolder({
              id,
              name: newLabel.trim(),
              color: node.color ?? "bg-slate-400",
            })
            return
          }
        }
        const row = getLabelRow(id)
        if (row && !isSystemNavLabelId(id)) {
          const oldLabel = row.label
          navApi.updateLabel({ id, name: newLabel.trim(), color: row.color })
          queueMicrotask(() => {
            navEmailSyncRef.current?.renameLabel(oldLabel, newLabel.trim())
          })
          return
        }
      }

      const { idMap, emailRename } = useNavStore.getState().renameFolderOrLabel(id, newLabel)
      scheduleRouteFolderIdSync(idMap)
      if (emailRename) {
        queueMicrotask(() => {
          navEmailSyncRef.current?.renameLabel(emailRename.from, emailRename.to)
        })
      }
    },
    [folderTree, getLabelRow, isFolderId, navApi, scheduleRouteFolderIdSync]
  )

  const removeFolderOrLabelRow = useCallback(
    (id: string) => {
      if (navApi.apiEnabled && isServerNavId(id)) {
        if (isFolderId(id)) {
          navApi.deleteFolder(id)
          return
        }
        const row = getLabelRow(id)
        if (row && !isSystemNavLabelId(id)) {
          navApi.deleteLabel(id)
          queueMicrotask(() => {
            navEmailSyncRef.current?.removeLabel(row.label)
          })
          return
        }
      }

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

  const syncLabelOrder = useCallback(() => {
    if (!navApi.apiEnabled) return
    const items = labelRowsToReorderItems(useNavStore.getState().labelRows)
    navApi.reorderLabels(items)
  }, [navApi])

  const syncFolderOrder = useCallback(() => {
    if (!navApi.apiEnabled) return
    const items = folderTreeToReorderItems(useNavStore.getState().folderTree)
    navApi.reorderFolders(items)
  }, [navApi])

  const moveFolder = useCallback(
    (id: string, newParentId: string | null) => {
      if (navApi.apiEnabled && isServerNavId(id)) {
        const node = findFolderNodeById(folderTree, id)
        if (node) {
          useNavStore.getState().moveFolder(id, newParentId)
          queueMicrotask(syncFolderOrder)
          return
        }
      }
      const idMap = useNavStore.getState().moveFolder(id, newParentId)
      scheduleRouteFolderIdSync(idMap)
    },
    [folderTree, navApi, scheduleRouteFolderIdSync, syncFolderOrder]
  )

  const moveFolderRelative = useCallback(
    (
      draggedId: string,
      targetId: string,
      placement: "before" | "after" | "inside"
    ) => {
      if (navApi.apiEnabled && isServerNavId(draggedId)) {
        const idMap = useNavStore
          .getState()
          .moveFolderRelative(draggedId, targetId, placement)
        scheduleRouteFolderIdSync(idMap)
        queueMicrotask(syncFolderOrder)
        return
      }
      const idMap = useNavStore
        .getState()
        .moveFolderRelative(draggedId, targetId, placement)
      scheduleRouteFolderIdSync(idMap)
    },
    [navApi, scheduleRouteFolderIdSync, syncFolderOrder]
  )

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

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

  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
}
