"use client"

import * as React from "react"
import { ChevronDownIcon } from "lucide-react"

import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { cn } from "@/lib/utils"
import type { AclNode } from "@/lib/acl-routes"

export type { AclNode } from "@/lib/acl-routes"
export { buildAclNodes, getAllAclPermissionKeys } from "@/lib/acl-routes"

function flattenKeys(node: AclNode): string[] {
  const self = [node.key]
  const childKeys = node.children?.flatMap(flattenKeys) ?? []
  return [...self, ...childKeys]
}

function collectExpandableKeys(nodes: AclNode[]): string[] {
  const keys: string[] = []

  function walk(node: AclNode) {
    if (node.children?.length) {
      keys.push(node.key)
      node.children.forEach(walk)
    }
  }

  nodes.forEach(walk)
  return keys
}

function AclTreeBranch({
  node,
  depth,
  openKeys,
  onToggleOpen,
  valueSet,
  onTogglePermission,
}: {
  node: AclNode
  depth: number
  openKeys: Set<string>
  onToggleOpen: (key: string) => void
  valueSet: Set<string>
  onTogglePermission: (node: AclNode, next: boolean) => void
}) {
  const children = node.children ?? []
  const hasChildren = children.length > 0
  const isOpen = openKeys.has(node.key)
  const checked = valueSet.has(node.key)

  return (
    <div className={cn("grid gap-1", depth > 0 && "border-l pl-4")}>
      <div className="flex items-center gap-1">
        {hasChildren ? (
          <Button
            type="button"
            variant="ghost"
            size="icon-xs"
            className="shrink-0"
            aria-expanded={isOpen}
            aria-label={isOpen ? "Collapse" : "Expand"}
            onClick={() => onToggleOpen(node.key)}
          >
            <ChevronDownIcon
              className={cn(
                "size-4 text-muted-foreground transition-transform",
                isOpen ? "rotate-0" : "-rotate-90"
              )}
            />
          </Button>
        ) : (
          <span aria-hidden className="size-6 shrink-0" />
        )}
        <label className="flex min-w-0 flex-1 items-center gap-2 py-1 text-sm">
          <Checkbox
            checked={checked}
            onCheckedChange={(v) => onTogglePermission(node, Boolean(v))}
          />
          <span className={cn(hasChildren && "font-medium")}>{node.label}</span>
        </label>
      </div>

      {hasChildren && isOpen ? (
        <div
          className={cn(
            "grid gap-1",
            depth === 0 && "rounded-lg border bg-muted/20 p-3"
          )}
        >
          {children.map((child) => (
            <AclTreeBranch
              key={child.key}
              node={child}
              depth={depth + 1}
              openKeys={openKeys}
              onToggleOpen={onToggleOpen}
              valueSet={valueSet}
              onTogglePermission={onTogglePermission}
            />
          ))}
        </div>
      ) : null}
    </div>
  )
}

export function AclTree({
  nodes,
  value,
  onChange,
  className,
}: {
  nodes: AclNode[]
  value: string[]
  onChange: (keys: string[]) => void
  className?: string
}) {
  const valueSet = React.useMemo(() => new Set(value), [value])
  const [openKeys, setOpenKeys] = React.useState<Set<string>>(
    () => new Set(collectExpandableKeys(nodes))
  )

  function toggleOpen(key: string) {
    setOpenKeys((prev) => {
      const next = new Set(prev)
      if (next.has(key)) {
        next.delete(key)
      } else {
        next.add(key)
      }
      return next
    })
  }

  function togglePermission(node: AclNode, next: boolean) {
    const keys = flattenKeys(node)
    const nextSet = new Set(value)
    for (const k of keys) {
      if (next) nextSet.add(k)
      else nextSet.delete(k)
    }
    onChange(Array.from(nextSet))
  }

  return (
    <div className={cn("grid gap-2", className)}>
      {nodes.map((node) => (
        <AclTreeBranch
          key={node.key}
          node={node}
          depth={0}
          openKeys={openKeys}
          onToggleOpen={toggleOpen}
          valueSet={valueSet}
          onTogglePermission={togglePermission}
        />
      ))}
    </div>
  )
}
