export type OrgContextMember = {
  id?: string;
  email: string;
  name: string;
  phone?: string;
  department?: string;
  grade?: string;
  position?: string;
  jobTitle?: string;
  isOwner?: boolean;
  isLeader?: boolean;
  isPrimary?: boolean;
};

export type OrgContextTenant = {
  id: string;
  type: string;
  name: string;
  slug: string;
  parentId?: string | null;
  status: string;
  description: string;
  domains: string[];
  memberCount: number;
  visibility: string;
  orgUnitType?: string;
  createdAt: string;
  updatedAt: string;
  members: OrgContextMember[];
};

export type OrgContextTreeNode = OrgContextTenant & {
  children: OrgContextTreeNode[];
};

export type OrgContextResponse = {
  schemaVersion: "baron.org-context.v1";
  issuedAt: string;
  scope: {
    tenantId: string;
    tenantSlug: string;
  };
  tree: OrgContextTreeNode;
  tenants: OrgContextTenant[];
};

export type OrgChartNode = OrgContextTenant & {
  children: OrgChartNode[];
  depth: number;
  path: string[];
};

export type OrgChartMember = OrgContextMember & {
  tenantIds: string[];
};

export type OrgChartModel = {
  root: OrgChartNode;
  nodes: OrgChartNode[];
  tenantsById: Map<string, OrgChartNode>;
  tenantsBySlug: Map<string, OrgChartNode>;
  membersByEmail: Map<string, OrgChartMember>;
  response: OrgContextResponse;
};

export type OrgContextClientOptions = {
  baseUrl: string;
  credentials?: {
    keyId: string;
    keySecret: string;
  };
  fetch?: typeof fetch;
  headers?: Record<string, string>;
};

export type FetchOrgContextOptions = {
  tenantSlug?: string;
  includeUsers?: boolean;
  includeUserIds?: boolean;
};

export type OrgPickerSelection = {
  id: string;
  name: string;
  type: "tenant" | "user";
};

export type OrgPickerVariant = "default" | "orgfront";

export type OrgPickerOptions = {
  mode?: "single" | "multiple";
  selectable?: "tenant" | "user" | "both";
  includeDescendants?: boolean;
  injectStyles?: boolean;
  showDescendantToggle?: boolean;
  variant?: OrgPickerVariant;
  onCancel?: () => void;
  onChange?: (selection: OrgPickerSelection[]) => void;
  onConfirm?: (selection: OrgPickerSelection[]) => void;
};

export type OrgPickerController = {
  cancel: () => void;
  confirm: () => void;
  destroy: () => void;
  getSelection: () => OrgPickerSelection[];
};

const API_PATH = "/api/v1/integrations/org-context";
const DEFAULT_STYLE_ID = "baron-org-context-chart-default-style";
const DEFAULT_STYLE = `
.baron-org-chart,.baron-org-picker{box-sizing:border-box;color:#0f172a;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-size:14px;line-height:1.45}
.baron-org-chart *,.baron-org-picker *{box-sizing:border-box}
.baron-org-chart__tree{display:flex;min-width:100%;gap:24px;overflow:auto;padding:16px}
.baron-org-chart__node{min-width:220px;border:1px solid #d8dee9;border-radius:8px;background:#fff;box-shadow:0 10px 30px rgba(15,23,42,.08)}
.baron-org-chart__title{margin:0;padding:10px 12px;border-bottom:1px solid #e5e7eb;background:#f8fafc;font-size:14px;font-weight:700}
.baron-org-chart__meta{margin:0;padding:8px 12px;color:#64748b;font-size:12px}
.baron-org-chart__members{margin:0;padding:0 12px 12px 28px;color:#334155;font-size:12px}
.baron-org-chart__children{display:flex;gap:16px;margin:12px;padding:12px 0 0 16px;border-left:1px solid #d8dee9}
.baron-org-picker{width:100%;max-width:520px;border:1px solid #d8dee9;border-radius:8px;background:#fff;box-shadow:0 12px 34px rgba(15,23,42,.1);overflow:hidden}
.baron-org-picker__toolbar{display:flex;flex-direction:column;gap:10px;padding:12px;border-bottom:1px solid #e5e7eb;background:#f8fafc}
.baron-org-picker__search-wrap{position:relative}
.baron-org-picker__search-icon{pointer-events:none;position:absolute;left:12px;top:50%;width:16px;height:16px;transform:translateY(-50%);color:#64748b}
.baron-org-picker__search-icon::before{content:"";position:absolute;left:2px;top:2px;width:8px;height:8px;border:2px solid currentColor;border-radius:999px}
.baron-org-picker__search-icon::after{content:"";position:absolute;left:10px;top:11px;width:6px;height:2px;background:currentColor;border-radius:999px;transform:rotate(45deg);transform-origin:left center}
.baron-org-picker__search{width:100%;height:38px;border:1px solid #cbd5e1;border-radius:6px;background:#fff;padding:0 10px;color:#0f172a;font:inherit;outline:none}
.baron-org-picker__search:focus{border-color:#24449c;box-shadow:0 0 0 3px rgba(36,68,156,.18)}
.baron-org-picker__controls{display:flex;align-items:center;justify-content:space-between;gap:12px;color:#475569;font-size:12px}
.baron-org-picker__descendants{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.baron-org-picker__summary{color:#64748b}
.baron-org-picker__clear{border:0;background:transparent;color:#24449c;cursor:pointer;font:inherit;font-weight:700;padding:2px 0}
.baron-org-picker__clear:hover{text-decoration:underline}
.baron-org-picker__list,.baron-org-picker__children{list-style:none;margin:0;padding:0}
.baron-org-picker__list{max-height:420px;overflow:auto;padding:8px}
.baron-org-picker__item{margin:0}
.baron-org-picker__children{margin-left:8px;border-left:1px solid #e5e7eb}
.baron-org-picker__row{display:flex;min-height:32px;align-items:center;gap:8px;border-radius:6px;padding:4px 8px;color:#0f172a;cursor:pointer}
.baron-org-picker__row:hover{background:#f1f5f9}
.baron-org-picker__row--selected{background:#e8eefc;color:#18327a}
.baron-org-picker__row--member{color:#334155;font-size:13px}
.baron-org-picker__label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.baron-org-picker__label-primary,.baron-org-picker__label-secondary{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.baron-org-picker__label-primary{font-weight:600;line-height:20px}
.baron-org-picker__label-secondary{color:#64748b;font-size:12px;line-height:20px}
.baron-org-picker input[type="checkbox"],.baron-org-picker input[type="radio"]{accent-color:#24449c}
.baron-org-picker__empty{padding:28px 12px;color:#64748b;text-align:center}
.baron-org-picker__toggle,.baron-org-picker__toggle-placeholder{display:grid;width:24px;height:24px;flex:0 0 24px;place-items:center;border:0;border-radius:4px;background:transparent;color:#64748b;font:inherit;line-height:1}
.baron-org-picker__toggle{cursor:pointer}
.baron-org-picker__toggle:hover{background:#e2e8f0}
.baron-org-picker__chevron{position:relative;display:block;width:16px;height:16px;color:currentColor}
.baron-org-picker__chevron::before{content:"";position:absolute;width:6px;height:6px;border-right:2px solid currentColor;border-bottom:2px solid currentColor}
.baron-org-picker__chevron--open::before{left:4px;top:3px;transform:rotate(45deg)}
.baron-org-picker__chevron--closed::before{left:3px;top:4px;transform:rotate(-45deg)}
.baron-org-picker__select{min-width:0;flex:1;border:0;border-radius:4px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer;outline:none;padding:0 4px}
.baron-org-picker__select:focus-visible{box-shadow:0 0 0 2px #3a98e5}
.baron-org-picker__footer{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid #e5e7eb;background:#fff;padding:8px 12px}
.baron-org-picker__actions{display:flex;align-items:center;gap:8px}
.baron-org-picker__button{display:inline-flex;height:36px;align-items:center;justify-content:center;gap:8px;white-space:nowrap;border-radius:6px;border:1px solid #e5e7eb;background:#fff;color:#0f172a;padding:0 12px;font:inherit;font-size:14px;font-weight:600;cursor:pointer}
.baron-org-picker__button:hover{background:#f4b840;color:#0f172a}
.baron-org-picker__button--primary{border-color:#3a98e5;background:#3a98e5;color:#fff;box-shadow:0 1px 2px rgba(15,23,42,.12)}
.baron-org-picker__button--primary:hover{background:#2588d8;color:#fff}
.baron-org-picker__button:disabled{cursor:not-allowed;opacity:.5}
.baron-org-picker--orgfront{--boc-background:hsl(var(--background,0 0% 98%));--boc-foreground:hsl(var(--foreground,223 25% 12%));--boc-primary:hsl(var(--primary,209 79% 52%));--boc-secondary:hsl(var(--secondary,220 17% 94%));--boc-muted-foreground:hsl(var(--muted-foreground,223 15% 45%));--boc-accent:hsl(var(--accent,40 96% 62%));--boc-accent-foreground:hsl(var(--accent-foreground,223 25% 12%));--boc-border:hsl(var(--border,220 17% 90%));--boc-input:hsl(var(--input,220 17% 90%));--boc-ring:hsl(var(--ring,209 79% 52%));display:flex;height:100%;min-height:320px;max-width:none;flex-direction:column;border:0;border-radius:0;background:var(--boc-background);box-shadow:none;color:var(--boc-foreground);overflow:hidden}
.baron-org-picker--orgfront .baron-org-picker__toolbar{display:block;border-bottom:1px solid var(--boc-border);background:var(--boc-background);padding:8px}
.baron-org-picker--orgfront .baron-org-picker__toolbar-grid{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:8px}
.baron-org-picker--orgfront .baron-org-picker__search{height:36px;border-color:var(--boc-input);border-radius:6px;background:var(--boc-background);padding:0 12px 0 36px;font-size:14px}
.baron-org-picker--orgfront .baron-org-picker__search:focus{border-color:var(--boc-input);box-shadow:0 0 0 2px var(--boc-ring)}
.baron-org-picker--orgfront .baron-org-picker__controls{height:36px;font-size:14px}
.baron-org-picker--orgfront .baron-org-picker__descendants{height:36px;font-size:14px}
.baron-org-picker--orgfront .baron-org-picker__list{min-height:0;flex:1;max-height:none;overflow:auto;padding:12px}
.baron-org-picker--orgfront .baron-org-picker__children{margin-left:16px;border-left:0}
.baron-org-picker--orgfront .baron-org-picker__row{min-height:28px;gap:6px;border-radius:4px;padding:2px 6px 2px 4px;transition:background-color .15s,color .15s,box-shadow .15s}
.baron-org-picker--orgfront .baron-org-picker__row:hover{background:color-mix(in srgb,var(--boc-secondary) 50%,transparent)}
.baron-org-picker--orgfront .baron-org-picker__row--selected{background:color-mix(in srgb,var(--boc-primary) 15%,transparent);box-shadow:0 0 0 2px color-mix(in srgb,var(--boc-primary) 60%,transparent);color:var(--boc-foreground)}
.baron-org-picker--orgfront .baron-org-picker__row--member{font-size:14px;color:var(--boc-foreground)}
.baron-org-picker--orgfront .baron-org-picker__footer{border-top-color:var(--boc-border);background:var(--boc-background)}
.baron-org-picker--orgfront .baron-org-picker__summary{font-size:14px;color:var(--boc-muted-foreground)}
.baron-org-picker--orgfront .baron-org-picker__button{border-color:var(--boc-input);background:var(--boc-background);color:var(--boc-foreground)}
.baron-org-picker--orgfront .baron-org-picker__button:hover{background:var(--boc-accent);color:var(--boc-accent-foreground)}
.baron-org-picker--orgfront .baron-org-picker__button--primary{border-color:var(--boc-primary);background:var(--boc-primary);color:#fff}
.baron-org-picker--orgfront .baron-org-picker__empty{margin:12px;min-height:160px;border:1px dashed var(--boc-border);border-radius:6px;background:var(--boc-background);display:grid;place-items:center}
`;

export function createOrgContextClient(options: OrgContextClientOptions) {
  const fetcher = options.fetch ?? globalThis.fetch;
  if (!fetcher) {
    throw new Error("A fetch implementation is required.");
  }

  return {
    async fetchOrgContext(
      query: FetchOrgContextOptions = {},
    ): Promise<OrgContextResponse> {
      const url = new URL(API_PATH, normalizeBaseUrl(options.baseUrl));
      appendQuery(url, "tenantSlug", query.tenantSlug);
      appendQuery(url, "includeUsers", query.includeUsers);
      appendQuery(url, "includeUserIds", query.includeUserIds);

      const headers: Record<string, string> = {
        Accept: "application/json",
        ...options.headers,
      };
      if (options.credentials) {
        headers["X-Baron-Key-ID"] = options.credentials.keyId;
        headers["X-Baron-Key-Secret"] = options.credentials.keySecret;
      }

      const response = await fetcher(url.toString(), { headers });
      if (!response.ok) {
        throw new Error(`Org context request failed with ${response.status}`);
      }

      const payload = (await response.json()) as OrgContextResponse;
      if (payload.schemaVersion !== "baron.org-context.v1") {
        throw new Error(
          `Unsupported org context schema: ${payload.schemaVersion}`,
        );
      }
      return payload;
    },
  };
}

export function buildOrgChartModel(
  response: OrgContextResponse,
): OrgChartModel {
  if (response.schemaVersion !== "baron.org-context.v1") {
    throw new Error(
      `Unsupported org context schema: ${response.schemaVersion}`,
    );
  }

  const nodes: OrgChartNode[] = [];
  const tenantsById = new Map<string, OrgChartNode>();
  const tenantsBySlug = new Map<string, OrgChartNode>();
  const membersByEmail = new Map<string, OrgChartMember>();

  const visit = (
    node: OrgContextTreeNode,
    depth: number,
    ancestorPath: string[],
  ): OrgChartNode => {
    const path = [...ancestorPath, node.id];
    const chartNode: OrgChartNode = {
      ...node,
      children: [],
      depth,
      path,
    };
    nodes.push(chartNode);
    tenantsById.set(chartNode.id, chartNode);
    tenantsBySlug.set(chartNode.slug, chartNode);

    for (const member of chartNode.members) {
      const key = member.email.toLowerCase();
      const existing = membersByEmail.get(key);
      if (existing) {
        existing.tenantIds.push(chartNode.id);
      } else {
        membersByEmail.set(key, {
          ...member,
          tenantIds: [chartNode.id],
        });
      }
    }

    chartNode.children = node.children.map((child) =>
      visit(child, depth + 1, path),
    );
    return chartNode;
  };

  return {
    root: visit(response.tree, 0, []),
    nodes,
    tenantsById,
    tenantsBySlug,
    membersByEmail,
    response,
  };
}

export function renderOrgChart(
  container: HTMLElement,
  model: OrgChartModel,
): { destroy: () => void } {
  ensureDefaultStyles();
  container.replaceChildren();
  container.classList.add("baron-org-chart");
  const root = document.createElement("div");
  root.className = "baron-org-chart__tree";
  root.append(renderChartNode(model.root));
  container.append(root);
  return {
    destroy() {
      container.replaceChildren();
      container.classList.remove("baron-org-chart");
    },
  };
}

export function renderOrgPicker(
  container: HTMLElement,
  model: OrgChartModel,
  options: OrgPickerOptions = {},
): OrgPickerController {
  if (options.injectStyles !== false) {
    ensureDefaultStyles();
  }
  const mode = options.mode ?? "single";
  const selectable = options.selectable ?? "tenant";
  const variant = options.variant ?? "default";
  const isOrgfront = variant === "orgfront";
  let includeDescendants =
    options.includeDescendants ?? (isOrgfront && mode === "multiple");
  let searchQuery = "";
  const selected = new Map<string, OrgPickerSelection>();
  const expanded = new Set(model.nodes.map((node) => node.id));
  const showDescendantToggle = options.showDescendantToggle ?? true;

  const currentSelection = () => Array.from(selected.values());

  const emitChange = () => {
    const selection = currentSelection();
    options.onChange?.(selection);
    container.dispatchEvent(
      new CustomEvent("baron-org-picker-change", {
        bubbles: true,
        detail: { selection },
      }),
    );
  };

  const emitConfirm = () => {
    const selection = currentSelection();
    options.onConfirm?.(selection);
    container.dispatchEvent(
      new CustomEvent("baron-org-picker-confirm", {
        bubbles: true,
        detail: { selection },
      }),
    );
  };

  const emitCancel = () => {
    options.onCancel?.();
    container.dispatchEvent(
      new CustomEvent("baron-org-picker-cancel", {
        bubbles: true,
      }),
    );
  };

  const toggleSelection = (
    selection: OrgPickerSelection,
    checked: boolean,
    descendants: OrgPickerSelection[],
  ) => {
    if (mode === "single") {
      selected.clear();
      if (checked) {
        selected.set(selectionKey(selection), selection);
      }
      emitChange();
      rerender();
      return;
    }

    const targets =
      includeDescendants && selection.type === "tenant"
        ? [selection, ...descendants]
        : [selection];
    for (const target of targets) {
      if (checked) {
        selected.set(selectionKey(target), target);
      } else {
        selected.delete(selectionKey(target));
      }
    }
    emitChange();
    rerender();
  };

  const renderPickerNode = (node: OrgChartNode): HTMLElement => {
    const item = document.createElement("li");
    item.className = "baron-org-picker__item";
    const hasChildren = node.children.length > 0;

    const tenantSelection: OrgPickerSelection = {
      id: node.id,
      name: node.name,
      type: "tenant",
    };
    const row = document.createElement(isOrgfront ? "div" : "label");
    row.className = "baron-org-picker__row";
    if (selected.has(selectionKey(tenantSelection))) {
      row.classList.add("baron-org-picker__row--selected");
    }
    row.style.paddingLeft = `${node.depth * 16}px`;

    if (isOrgfront) {
      row.append(createExpandToggle(node, hasChildren));
      appendOrgfrontSelectionControl(row, node, tenantSelection);
    } else {
      if (selectable === "tenant" || selectable === "both") {
        row.append(
          createPickerInput({
            mode,
            selection: tenantSelection,
            selected,
            onToggle: (checked) =>
              toggleSelection(
                tenantSelection,
                checked,
                collectDescendantSelections(node, selectable),
              ),
          }),
        );
      }
      row.append(createLabelText(node.name, node.type));
    }
    item.append(row);

    if (selectable === "user" || selectable === "both") {
      for (const member of node.members) {
        item.append(
          renderMemberPickerRow(
            member,
            node,
            mode,
            selected,
            (value) =>
              toggleSelection(value, !selected.has(selectionKey(value)), []),
            isOrgfront,
          ),
        );
      }
    }

    if (hasChildren && (!isOrgfront || expanded.has(node.id))) {
      const children = document.createElement("ul");
      children.className = "baron-org-picker__children";
      for (const child of node.children) {
        children.append(renderPickerNode(child));
      }
      item.append(children);
    }
    return item;
  };

  const appendOrgfrontSelectionControl = (
    row: HTMLElement,
    node: OrgChartNode,
    tenantSelection: OrgPickerSelection,
  ) => {
    const canSelect = selectable === "tenant" || selectable === "both";
    if (!canSelect) {
      row.append(createOrgfrontLabelText(node.name));
      return;
    }

    if (mode === "multiple") {
      row.append(
        createPickerInput({
          mode,
          selection: tenantSelection,
          selected,
          onToggle: (checked) =>
            toggleSelection(
              tenantSelection,
              checked,
              collectDescendantSelections(node, selectable),
            ),
        }),
      );
      row.append(createOrgfrontLabelText(node.name));
      return;
    }

    const button = document.createElement("button");
    button.className = "baron-org-picker__select";
    button.dataset.baronOrgPickerValue = selectionKey(tenantSelection);
    button.type = "button";
    button.ariaPressed = String(selected.has(selectionKey(tenantSelection)));
    button.addEventListener("click", () =>
      toggleSelection(
        tenantSelection,
        true,
        collectDescendantSelections(node, selectable),
      ),
    );
    button.append(createOrgfrontLabelText(node.name));
    row.append(button);
  };

  const createExpandToggle = (node: OrgChartNode, hasChildren: boolean) => {
    if (!hasChildren) {
      const placeholder = document.createElement("span");
      placeholder.className = "baron-org-picker__toggle-placeholder";
      placeholder.ariaHidden = "true";
      return placeholder;
    }

    const toggle = document.createElement("button");
    toggle.className = "baron-org-picker__toggle";
    toggle.dataset.baronOrgPickerToggle = selectionKey({
      id: node.id,
      name: node.name,
      type: "tenant",
    });
    toggle.type = "button";
    const chevron = document.createElement("span");
    chevron.className = expanded.has(node.id)
      ? "baron-org-picker__chevron baron-org-picker__chevron--open"
      : "baron-org-picker__chevron baron-org-picker__chevron--closed";
    chevron.dataset.baronOrgPickerChevron = "true";
    chevron.ariaHidden = "true";
    toggle.append(chevron);
    toggle.ariaLabel = `${node.name} ${expanded.has(node.id) ? "접기" : "펼치기"}`;
    toggle.addEventListener("click", () => {
      if (expanded.has(node.id)) {
        expanded.delete(node.id);
      } else {
        expanded.add(node.id);
      }
      rerender();
    });
    return toggle;
  };

  const rerender = () => {
    container.replaceChildren();
    container.classList.add("baron-org-picker");
    container.classList.toggle("baron-org-picker--orgfront", isOrgfront);
    container.append(renderPickerToolbar());

    const visibleRoot = filterOrgChartNode(model.root, searchQuery, selectable);
    if (!visibleRoot) {
      const empty = document.createElement("div");
      empty.className = "baron-org-picker__empty";
      empty.textContent = isOrgfront
        ? "검색 결과가 없습니다."
        : "No matching organization or member.";
      container.append(empty);
      if (isOrgfront) {
        container.append(renderPickerFooter());
      }
      return;
    }

    const list = document.createElement("ul");
    list.className = "baron-org-picker__list";
    list.append(renderPickerNode(visibleRoot));
    container.append(list);
    if (isOrgfront) {
      container.append(renderPickerFooter());
    }
  };

  const renderPickerToolbar = () => {
    const toolbar = document.createElement("div");
    toolbar.className = "baron-org-picker__toolbar";
    const toolbarContent = isOrgfront ? document.createElement("div") : toolbar;
    if (isOrgfront) {
      toolbarContent.className = "baron-org-picker__toolbar-grid";
      toolbar.append(toolbarContent);
    }

    const searchWrap = document.createElement("div");
    searchWrap.className = "baron-org-picker__search-wrap";
    if (isOrgfront) {
      const searchIcon = document.createElement("span");
      searchIcon.className = "baron-org-picker__search-icon";
      searchIcon.dataset.baronOrgPickerSearchIcon = "true";
      searchIcon.ariaHidden = "true";
      searchWrap.append(searchIcon);
    }
    const search = document.createElement("input");
    search.className = "baron-org-picker__search";
    search.dataset.baronOrgPickerSearch = "true";
    search.placeholder = isOrgfront
      ? "ID, 이름, 이메일, 메타데이터"
      : "Search organization or member";
    search.type = "search";
    search.value = searchQuery;
    search.addEventListener("input", () => {
      searchQuery = search.value;
      rerender();
      const nextSearch = container.querySelector<HTMLInputElement>(
        "[data-baron-org-picker-search]",
      );
      nextSearch?.focus();
      nextSearch?.setSelectionRange(searchQuery.length, searchQuery.length);
    });
    searchWrap.append(search);
    toolbarContent.append(searchWrap);

    const controls = document.createElement("div");
    controls.className = "baron-org-picker__controls";
    if (mode === "multiple" && selectable !== "user" && showDescendantToggle) {
      const descendantsLabel = document.createElement("label");
      descendantsLabel.className = "baron-org-picker__descendants";
      const descendants = document.createElement("input");
      descendants.dataset.baronOrgPickerDescendants = "true";
      descendants.type = "checkbox";
      descendants.checked = includeDescendants;
      const updateDescendantSelection = () => {
        includeDescendants = descendants.checked;
        rerender();
      };
      descendants.addEventListener("change", updateDescendantSelection);
      descendants.addEventListener("click", updateDescendantSelection);
      descendantsLabel.append(
        descendants,
        isOrgfront ? "하위 선택" : "Include descendants",
      );
      controls.append(descendantsLabel);
    } else if (!isOrgfront) {
      controls.append(document.createElement("span"));
    }

    if (!isOrgfront) {
      const summary = document.createElement("span");
      summary.className = "baron-org-picker__summary";
      summary.dataset.baronOrgPickerSummary = "true";
      summary.textContent = `${selected.size} selected`;
      controls.append(summary);

      if (selected.size > 0) {
        const clear = document.createElement("button");
        clear.className = "baron-org-picker__clear";
        clear.type = "button";
        clear.textContent = "Clear";
        clear.addEventListener("click", () => {
          selected.clear();
          emitChange();
          rerender();
        });
        controls.append(clear);
      }
    }

    toolbarContent.append(controls);
    return toolbar;
  };

  const renderPickerFooter = () => {
    const footer = document.createElement("footer");
    footer.className = "baron-org-picker__footer";
    footer.dataset.baronOrgPickerFooter = "true";

    const summary = document.createElement("div");
    summary.className = "baron-org-picker__summary";
    summary.dataset.baronOrgPickerSummary = "true";
    summary.textContent =
      selected.size > 0
        ? `${selected.size}개 항목 선택됨`
        : "선택된 항목이 없습니다.";
    footer.append(summary);

    const actions = document.createElement("div");
    actions.className = "baron-org-picker__actions";

    const cancel = document.createElement("button");
    cancel.className = "baron-org-picker__button";
    cancel.dataset.baronOrgPickerCancel = "true";
    cancel.type = "button";
    cancel.textContent = "취소";
    cancel.addEventListener("click", emitCancel);
    actions.append(cancel);

    const confirm = document.createElement("button");
    confirm.className =
      "baron-org-picker__button baron-org-picker__button--primary";
    confirm.dataset.baronOrgPickerConfirm = "true";
    confirm.disabled = selected.size === 0;
    confirm.type = "button";
    confirm.textContent = "선택 완료";
    confirm.addEventListener("click", emitConfirm);
    actions.append(confirm);

    footer.append(actions);
    return footer;
  };

  rerender();

  return {
    cancel: emitCancel,
    confirm: emitConfirm,
    destroy() {
      container.replaceChildren();
      container.classList.remove(
        "baron-org-picker",
        "baron-org-picker--orgfront",
      );
      selected.clear();
    },
    getSelection() {
      return currentSelection();
    },
  };
}

function appendQuery(
  url: URL,
  key: string,
  value: string | boolean | undefined,
) {
  if (value !== undefined) {
    url.searchParams.set(key, String(value));
  }
}

function normalizeBaseUrl(baseUrl: string) {
  return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
}

function ensureDefaultStyles() {
  if (typeof document === "undefined") return;
  if (document.getElementById(DEFAULT_STYLE_ID)) return;
  const style = document.createElement("style");
  style.id = DEFAULT_STYLE_ID;
  style.dataset.baronOrgContextChartStyle = "default";
  style.textContent = DEFAULT_STYLE;
  document.head.append(style);
}

function renderChartNode(node: OrgChartNode): HTMLElement {
  const item = document.createElement("section");
  item.className = "baron-org-chart__node";
  item.dataset.baronOrgNode = node.id;

  const title = document.createElement("h3");
  title.className = "baron-org-chart__title";
  title.textContent = node.name;
  item.append(title);

  const meta = document.createElement("p");
  meta.className = "baron-org-chart__meta";
  meta.textContent = [node.type, node.orgUnitType, node.visibility]
    .filter(Boolean)
    .join(" · ");
  item.append(meta);

  if (node.members.length > 0) {
    const memberList = document.createElement("ul");
    memberList.className = "baron-org-chart__members";
    for (const member of node.members) {
      const memberItem = document.createElement("li");
      memberItem.textContent = formatMember(member);
      memberList.append(memberItem);
    }
    item.append(memberList);
  }

  if (node.children.length > 0) {
    const children = document.createElement("div");
    children.className = "baron-org-chart__children";
    for (const child of node.children) {
      children.append(renderChartNode(child));
    }
    item.append(children);
  }
  return item;
}

function renderMemberPickerRow(
  member: OrgContextMember,
  node: OrgChartNode,
  mode: "single" | "multiple",
  selected: Map<string, OrgPickerSelection>,
  onSelect: (selection: OrgPickerSelection) => void,
  isOrgfront = false,
) {
  const selection: OrgPickerSelection = {
    id: member.id || `${node.id}:${member.email}`,
    name: member.name,
    type: "user",
  };
  const row = document.createElement(isOrgfront ? "div" : "label");
  row.className = "baron-org-picker__row baron-org-picker__row--member";
  if (selected.has(selectionKey(selection))) {
    row.classList.add("baron-org-picker__row--selected");
  }
  row.style.paddingLeft = `${node.depth * 16 + 24}px`;
  if (isOrgfront && mode === "single") {
    row.append(createOrgfrontMemberSpacer());
    const button = document.createElement("button");
    button.className = "baron-org-picker__select";
    button.dataset.baronOrgPickerValue = selectionKey(selection);
    button.type = "button";
    button.ariaPressed = String(selected.has(selectionKey(selection)));
    button.addEventListener("click", () => onSelect(selection));
    button.append(createOrgfrontLabelText(member.name, member.email));
    row.append(button);
    return row;
  }

  if (isOrgfront) {
    row.append(createOrgfrontMemberSpacer());
  }
  row.append(
    createPickerInput({
      mode,
      selection,
      selected,
      onToggle: () => onSelect(selection),
    }),
  );
  row.append(
    isOrgfront
      ? createOrgfrontLabelText(member.name, member.email)
      : createLabelText(member.name, member.email),
  );
  return row;
}

function createPickerInput({
  mode,
  selection,
  selected,
  onToggle,
}: {
  mode: "single" | "multiple";
  selection: OrgPickerSelection;
  selected: Map<string, OrgPickerSelection>;
  onToggle: (checked: boolean) => void;
}) {
  const input = document.createElement("input");
  input.type = mode === "single" ? "radio" : "checkbox";
  input.name = "baron-org-picker";
  input.value = selectionKey(selection);
  input.checked = selected.has(selectionKey(selection));
  const handleToggle = () => {
    onToggle(mode === "single" ? true : !selected.has(selectionKey(selection)));
  };
  input.addEventListener("click", handleToggle);
  return input;
}

function createLabelText(primary: string, secondary?: string) {
  const text = document.createElement("span");
  text.className = "baron-org-picker__label";
  text.textContent = secondary ? `${primary} (${secondary})` : primary;
  return text;
}

function createOrgfrontMemberSpacer() {
  const spacer = document.createElement("span");
  spacer.className = "baron-org-picker__toggle-placeholder";
  spacer.ariaHidden = "true";
  return spacer;
}

function createOrgfrontLabelText(primary: string, secondary?: string) {
  const text = document.createElement("span");
  text.className = "baron-org-picker__label";
  const primaryText = document.createElement("span");
  primaryText.className = "baron-org-picker__label-primary";
  primaryText.textContent = primary;
  text.append(primaryText);
  if (secondary) {
    const secondaryText = document.createElement("span");
    secondaryText.className = "baron-org-picker__label-secondary";
    secondaryText.textContent = secondary;
    text.append(secondaryText);
  }
  return text;
}

function filterOrgChartNode(
  node: OrgChartNode,
  rawQuery: string,
  selectable: "tenant" | "user" | "both",
): OrgChartNode | null {
  const query = rawQuery.trim().toLowerCase();
  if (!query) return node;

  const childMatches = node.children
    .map((child) => filterOrgChartNode(child, rawQuery, selectable))
    .filter((child): child is OrgChartNode => Boolean(child));
  const tenantMatch = orgTenantMatchesSearch(node, query, selectable);
  const matchingMembers = orgMemberMatchesSearch(node, query, selectable);
  if (
    !tenantMatch &&
    matchingMembers.length === 0 &&
    childMatches.length === 0
  ) {
    return null;
  }
  return {
    ...node,
    members: tenantMatch ? node.members : matchingMembers,
    children: childMatches,
  };
}

function orgTenantMatchesSearch(
  node: OrgChartNode,
  query: string,
  selectable: "tenant" | "user" | "both",
) {
  const tenantValues = [
    node.id,
    node.name,
    node.slug,
    node.type,
    node.orgUnitType ?? "",
    node.visibility,
    ...node.domains,
  ];
  if (
    selectable !== "user" &&
    tenantValues.some((value) => value.toLowerCase().includes(query))
  ) {
    return true;
  }
  if (selectable === "tenant") {
    return false;
  }
  return false;
}

function orgMemberMatchesSearch(
  node: OrgChartNode,
  query: string,
  selectable: "tenant" | "user" | "both",
) {
  if (selectable === "tenant") {
    return [];
  }
  return node.members.filter((member) =>
    [
      member.id ?? "",
      member.email,
      member.name,
      member.department ?? "",
      member.grade ?? "",
      member.position ?? "",
      member.jobTitle ?? "",
    ].some((value) => value.toLowerCase().includes(query)),
  );
}

function collectDescendantSelections(
  node: OrgChartNode,
  selectable: "tenant" | "user" | "both",
): OrgPickerSelection[] {
  const selections: OrgPickerSelection[] = [];
  const visit = (child: OrgChartNode) => {
    if (selectable === "tenant" || selectable === "both") {
      selections.push({ id: child.id, name: child.name, type: "tenant" });
    }
    if (selectable === "user" || selectable === "both") {
      for (const member of child.members) {
        selections.push({
          id: member.id || `${child.id}:${member.email}`,
          name: member.name,
          type: "user",
        });
      }
    }
    for (const grandchild of child.children) {
      visit(grandchild);
    }
  };
  for (const child of node.children) {
    visit(child);
  }
  return selections;
}

function selectionKey(selection: OrgPickerSelection) {
  return `${selection.type}:${selection.id}`;
}

function formatMember(member: OrgContextMember) {
  return [member.name, member.position, member.jobTitle]
    .filter(Boolean)
    .join(" · ");
}
