import { useEffect, useMemo, useState, type CSSProperties } from "react";
import { ChevronDown, ChevronRight } from "lucide-react";
import { usePlatformLanguage } from "../i18n/LanguageContext";

export type TreeSubnavItem<T extends string> = {
  id: T;
  label: string;
  subtle?: boolean;
};

export type TreeSubnavBranch<T extends string> = {
  branchId: string;
  label: string;
  children: TreeSubnavNode<T>[];
  defaultExpanded?: boolean;
};

export type TreeSubnavNode<T extends string> =
  | TreeSubnavItem<T>
  | TreeSubnavBranch<T>;

function isBranch<T extends string>(
  node: TreeSubnavNode<T>
): node is TreeSubnavBranch<T> {
  return "branchId" in node;
}

function branchIdsContaining<T extends string>(
  nodes: TreeSubnavNode<T>[],
  active: T
): string[] {
  const result: string[] = [];
  for (const node of nodes) {
    if (!isBranch(node)) continue;
    const nested = branchIdsContaining(node.children, active);
    if (
      nested.length > 0 ||
      node.children.some((child) => !isBranch(child) && child.id === active)
    ) {
      result.push(node.branchId, ...nested);
    }
  }
  return result;
}

function defaultExpandedBranchIds<T extends string>(
  nodes: TreeSubnavNode<T>[]
): string[] {
  return nodes.flatMap((node) => {
    if (!isBranch(node)) return [];
    return [
      ...(node.defaultExpanded === false ? [] : [node.branchId]),
      ...defaultExpandedBranchIds(node.children)
    ];
  });
}

export default function TreeSubnav<T extends string>({
  active,
  nodes,
  onSelect,
  ariaLabel,
  className = ""
}: {
  active: T;
  nodes: TreeSubnavNode<T>[];
  onSelect: (section: T) => void;
  ariaLabel: string;
  className?: string;
}) {
  const { translateText } = usePlatformLanguage();
  const initialExpanded = useMemo(
    () => defaultExpandedBranchIds(nodes),
    [nodes]
  );
  const [expanded, setExpanded] = useState<Set<string>>(
    () => new Set(initialExpanded)
  );

  useEffect(() => {
    const activeBranchIds = branchIdsContaining(nodes, active);
    setExpanded((current) => {
      if (activeBranchIds.every((branchId) => current.has(branchId))) {
        return current;
      }
      const next = new Set(current);
      activeBranchIds.forEach((branchId) => next.add(branchId));
      return next;
    });
  }, [active, nodes]);

  function toggleBranch(branchId: string) {
    setExpanded((current) => {
      const next = new Set(current);
      if (next.has(branchId)) next.delete(branchId);
      else next.add(branchId);
      return next;
    });
  }

  function renderNodes(
    currentNodes: TreeSubnavNode<T>[],
    depth: number
  ) {
    return currentNodes.map((node) => {
      const depthStyle = { "--tree-depth": depth } as CSSProperties;
      if (isBranch(node)) {
        const isExpanded = expanded.has(node.branchId);
        const containsActive = branchIdsContaining([node], active).length > 0;
        return (
          <div
            className={`tree-subnav-branch${containsActive ? " contains-active" : ""}`}
            key={node.branchId}
          >
            <button
              type="button"
              className="tree-subnav-disclosure"
              style={depthStyle}
              aria-expanded={isExpanded}
              onClick={() => toggleBranch(node.branchId)}
            >
              {isExpanded ? (
                <ChevronDown aria-hidden="true" size={16} />
              ) : (
                <ChevronRight aria-hidden="true" size={16} />
              )}
              <span>{translateText(node.label)}</span>
            </button>
            {isExpanded && (
              <div className="tree-subnav-children">
                {renderNodes(node.children, depth + 1)}
              </div>
            )}
          </div>
        );
      }
      return (
        <button
          type="button"
          key={node.id}
          className={`tree-subnav-link${node.subtle ? " subtle" : ""}${
            active === node.id ? " active" : ""
          }`}
          style={depthStyle}
          aria-current={active === node.id ? "page" : undefined}
          onClick={() => onSelect(node.id)}
        >
          {translateText(node.label)}
        </button>
      );
    });
  }

  return (
    <aside
      className={`section-sidebar tree-subnav ${className}`.trim()}
      aria-label={translateText(ariaLabel)}
    >
      {renderNodes(nodes, 0)}
    </aside>
  );
}
