"use client";

import * as React from "react";
import { createPortal } from "react-dom";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { BarChart3, Check, ChevronDown, FlaskConical, Layers, ListChecks, Scale, Search, Settings2, Plus, Save, Trash2, User, X } from "lucide-react";
import { toast } from "sonner";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetTitle,
} from "@/components/ui/sheet";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";

import {
  DRIFT_ISIN_ALL,
  loadDriftPositions,
  loadDriftSettings,
  saveDriftSettings,
} from "../_lib/tenant-settings-api";
import { useDefaultLayout } from "@/app/customer/_lib/use-default-layout";
import { useAssetClassFilterCatalog } from "@/app/customer/[tenant]/reports/_shared/lib/use-asset-class-filter-catalog";
import type {
  DriftAllocationComparison,
  DriftDimensionKey,
  DriftIsinKind,
  DriftIsinRule,
  DriftLayout,
  DriftLayoutParent,
  DriftPositionsData,
  DriftRule,
  DriftSettingsData,
} from "../_lib/tenant-settings-server-api";
import { cn } from "@/lib/utils";
import { resolveAssetClassColor } from "@/lib/chart/asset-class-colors";
import { formatQuantity } from "@/lib/format/numbers";
import { errorMessageOr, toastApiError } from "@/lib/toast-api-error";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog";

const DIMENSIONS: Array<[DriftDimensionKey, string]> = [
  ["asset_class", "Asset Class"],
  ["asset_type", "Asset Type"],
  ["industry", "Industry"],
  ["sector", "Sector"],
  ["currency", "Currency"],
  ["country", "Country"],
];
const EMPTY_RULE = (): DriftRule => ({ dimensions: {}, target_percentage: 0 });
const MASTER_GROUP_BRIDGE_DENIED =
  "Master rule groups cannot be created for Bridge layout. Create Bridge rules from the customer side.";

type DriftMasterGroup = { id: number; name: string; layout?: string };

function masterGroupsForLayout(groups: DriftMasterGroup[], layout: DriftLayout): DriftMasterGroup[] {
  if (layout === "bridge") return [];
  return groups.filter((group) => normalizeDriftLayout(group.layout) === layout);
}
const EMPTY_ISIN = (): DriftIsinRule => ({
  kind: "single",
  isin: "",
  target_percentage: 0,
});

const EMPTY_SELECT_ALL = (): DriftIsinRule => ({
  kind: "all",
  isin: DRIFT_ISIN_ALL,
  name: "Select All",
  target_percentage: 0,
});

const EMPTY_EXCEPT = (): DriftIsinRule => ({
  kind: "except",
  isin: "",
  target_percentage: 0,
});

const EMPTY_DRIFT_LAYOUT_PARENTS: DriftLayoutParent[] = [];

function isSelectAllCode(code?: string | null): boolean {
  return String(code ?? "").trim().toLowerCase() === DRIFT_ISIN_ALL;
}

function roundPct2(value: number): number {
  return Math.round(value * 100) / 100;
}

function ruleIsinCode(rule: DriftIsinRule): string {
  return String(rule.isin ?? "").trim();
}

function validateIsinRules(rules: DriftIsinRule[], currentLayout: DriftLayout): string[] {
  const errors: string[] = [];
  const byLayout = new Map<DriftLayout, { rule: DriftIsinRule; index: number }[]>();

  rules.forEach((rule, index) => {
    const layout = resolveIsinRuleLayout(rule, currentLayout);
    const list = byLayout.get(layout) ?? [];
    list.push({ rule, index });
    byLayout.set(layout, list);
  });

  for (const [layout, rows] of byLayout) {
    const seen = new Map<string, number>();
    let allCount = 0;
    const layoutLabel =
      layout === currentLayout
        ? ""
        : ` (${layout === "macro" ? "Macro" : layout === "bridge" ? "Bridge" : "Default"} layout)`;

    rows.forEach(({ rule, index }, layoutIndex) => {
      const pct = Number(rule.target_percentage);
      if (Number.isNaN(pct) || pct < 0 || pct > 100) {
        errors.push(`ISIN row ${index + 1}${layoutLabel}: percentage must be between 0% and 100%.`);
      }

      if (rule.kind === "all" || isSelectAllCode(rule.isin)) {
        allCount += 1;
        return;
      }

      const code = ruleIsinCode(rule);
      if (!code) {
        if (rule.kind === "except") {
          errors.push(`Except row ${layoutIndex + 1}${layoutLabel}: choose an ISIN.`);
        }
        return;
      }
      if (seen.has(code)) {
        errors.push(`ISIN ${code} appears more than once${layoutLabel}.`);
      } else {
        seen.set(code, index);
      }
    });

    if (allCount > 1) {
      errors.push(`Only one Select All rule is allowed${layoutLabel}.`);
    }
  }

  return errors;
}

function normalizeIsinKind(raw: unknown, isin: string): DriftIsinKind {
  const mode = String(raw ?? "").trim().toLowerCase();
  if (isSelectAllCode(isin) || mode === "all") return "all";
  if (mode === "except") return "except";
  return "single";
}

function normalizeDriftLayout(value?: string | null): DriftLayout {
  const mode = String(value ?? "").trim().toLowerCase();
  if (mode === "macro" || mode === "bridge") return mode;
  return "default";
}

function layoutParentTitle(layout: DriftLayout): string {
  return layout === "bridge" ? "Bridge" : layout === "macro" ? "Macro" : "Asset Class";
}

function isStructureAssetClass(assetClass?: string | null): boolean {
  const clean = String(assetClass ?? "")
    .replace(/<[^>]*>/g, " ")
    .replace(/&nbsp;/gi, " ")
    .replace(/\s+/g, " ")
    .trim()
    .toLowerCase();
  return clean.includes("structure");
}

/**
 * Layout this rule belongs to. Empty new rows follow the tenant layout;
 * previously saved Default rows (content, no Macro/Bridge parent) stay Default.
 */
function resolveRuleLayout(
  rule: DriftRule,
  currentLayout: DriftLayout,
  parentOptions: DriftLayoutParent[] = [],
): DriftLayout {
  const stored = String(rule.layout_parent_type ?? "").trim().toLowerCase();
  if (stored === "macro" || stored === "bridge" || stored === "default") {
    return stored;
  }

  const parentId = String(rule.layout_parent_id ?? "").trim();
  if (
    parentId
    && (currentLayout === "macro" || currentLayout === "bridge")
    && findLayoutParent(parentOptions, parentId)
  ) {
    // Bridge/Macro parent picked on the current layout — not a Default leftover.
    return currentLayout;
  }

  if (ruleHasDimensionContent(rule) || parentId) {
    return "default";
  }
  return currentLayout;
}

function ruleMatchesCurrentLayout(
  rule: DriftRule,
  currentLayout: DriftLayout,
  parentOptions: DriftLayoutParent[] = [],
): boolean {
  return resolveRuleLayout(rule, currentLayout, parentOptions) === currentLayout;
}

function resolveIsinRuleLayout(
  rule: DriftIsinRule,
  currentLayout: DriftLayout,
): DriftLayout {
  const stored = String(rule.layout_parent_type ?? "").trim().toLowerCase();
  if (stored === "macro" || stored === "bridge" || stored === "default") {
    return stored;
  }
  if (String(rule.layout_parent_id ?? "").trim()) {
    // Parent id without type still belongs to a stamped layout when current is macro/bridge.
    return currentLayout === "macro" || currentLayout === "bridge" ? currentLayout : "default";
  }
  const hasContent =
    Boolean(ruleIsinCode(rule))
    || rule.kind === "all"
    || rule.kind === "except"
    || Number(rule.target_percentage) > 0;
  // Legacy unstamped rows with content belong to Default (hidden on Macro/Bridge).
  if (hasContent) return "default";
  return currentLayout;
}

function isinMatchesCurrentLayout(rule: DriftIsinRule, currentLayout: DriftLayout): boolean {
  return resolveIsinRuleLayout(rule, currentLayout) === currentLayout;
}

function emptyIsinForLayout(layout: DriftLayout, kind: DriftIsinKind = "single"): DriftIsinRule {
  const base =
    kind === "all" ? EMPTY_SELECT_ALL() : kind === "except" ? EMPTY_EXCEPT() : EMPTY_ISIN();
  return {
    ...base,
    layout_parent_type: layout,
    layout_parent_id: null,
  };
}

function emptyRuleForLayout(layout: DriftLayout, advanced = false): DriftRule {
  const base: DriftRule = {
    dimensions: {},
    target_percentage: 0,
    layout_parent_type: layout,
    layout_parent_id: null,
  };
  return advanced ? { ...base, operator: "lt" } : base;
}

function assetClassOptionsForRule(data: DriftSettingsData, rule: DriftRule): string[] {
  const held = data.dimensions.asset_class ?? [];
  const currentLayout = normalizeDriftLayout(data.defaultLayout);
  const parents = data.layoutParents ?? [];
  const ruleLayout = resolveRuleLayout(rule, currentLayout, parents);
  if (ruleLayout === "default") return held;

  const parentId = String(rule.layout_parent_id ?? "").trim();
  const parent = findLayoutParent(data.layoutParents ?? [], parentId);
  if (!parent) {
    // Other-layout / orphaned parent: keep the saved Asset Class visible.
    const saved = String(rule.dimensions.asset_class ?? "").trim();
    return saved ? [saved, ...held.filter((item) => item.toLowerCase() !== saved.toLowerCase())] : held;
  }

  const linked = parent.assetClasses ?? [];
  if (linked.length === 0) return held;
  const seen = new Set<string>();
  const out: string[] = [];
  const linkedKeys = new Set(linked.map((item) => item.toLowerCase()));
  for (const value of [...linked, ...held.filter((item) => linkedKeys.has(item.toLowerCase()))]) {
    const text = String(value ?? "").trim();
    const key = text.toLowerCase();
    if (!text || seen.has(key)) continue;
    seen.add(key);
    out.push(text);
  }
  return out;
}

function ruleHasDimensionContent(rule: DriftRule): boolean {
  const dims = rule.dimensions ?? {};
  return (
    Object.values(dims).some((value) => String(value ?? "").trim() !== "")
    || Number(rule.target_percentage) > 0
    || Boolean(String(rule.layout_parent_id ?? "").trim())
  );
}

function catalogParentsFromFilter(
  options: Array<{ value: string; label: string; underliers?: string[] }>,
): DriftLayoutParent[] {
  return options.map((item) => ({
    id: item.value,
    label: item.label || item.value,
    assetClasses: item.underliers ?? [],
  }));
}

/** Case-insensitive id/label match — same as DriftSettings::findLayoutParentOption. */
function findLayoutParent(
  parents: readonly DriftLayoutParent[],
  parentId: string | null | undefined,
): DriftLayoutParent | undefined {
  const needle = String(parentId ?? "").trim().toLowerCase();
  if (!needle) return undefined;
  return parents.find((item) => {
    const id = String(item.id ?? "").trim().toLowerCase();
    const label = String(item.label ?? "").trim().toLowerCase();
    return id === needle || label === needle;
  });
}

function withDriftLayout(
  data: DriftSettingsData,
  fallbackLayout: string,
  catalogParents: DriftLayoutParent[] = [],
): DriftSettingsData {
  // Prefer the tenant Layout chip over API defaultLayout (ACL can soft-fail to "default").
  const layout = normalizeDriftLayout(fallbackLayout || data.defaultLayout);
  const fromApi = data.layoutParents ?? [];
  return {
    ...data,
    defaultLayout: layout,
    layoutParents: fromApi.length > 0 ? fromApi : catalogParents,
  };
}

function validateDimensionRules(rules: DriftRule[], data: DriftSettingsData): string[] {
  const layout = normalizeDriftLayout(data.defaultLayout);
  const parents = data.layoutParents ?? [];
  const errors: string[] = [];
  let visibleIndex = 0;
  rules.forEach((rule) => {
    if (!ruleHasDimensionContent(rule)) return;
    // Other-layout rules stay as saved (read-only in UI) — do not re-validate against current layout.
    if (!ruleMatchesCurrentLayout(rule, layout, parents)) return;
    visibleIndex += 1;
    const ruleNo = visibleIndex;

    const assetClass = String(rule.dimensions.asset_class ?? "").trim();
    // Macro: only the Macro dropdown is required (Asset Class is optional).
    if (layout === "macro") {
      if (parents.length === 0) return;
      const parentId = String(rule.layout_parent_id ?? "").trim();
      if (!parentId) {
        errors.push(`Rule ${ruleNo}: Macro is required.`);
      }
      return;
    }

    if (!assetClass) {
      errors.push(`Rule ${ruleNo}: Asset Class is required.`);
    }
    if (layout !== "bridge") return;
    if (parents.length === 0) return;

    const label = layoutParentTitle(layout);
    const parentId = String(rule.layout_parent_id ?? "").trim();
    if (!parentId) {
      errors.push(`Rule ${ruleNo}: ${label} is required.`);
      return;
    }
    const parent = findLayoutParent(parents, parentId);
    // Orphaned Bridge id (renamed/removed catalog entry): keep as-is — matches backend.
    if (!parent) {
      return;
    }
    if (
      assetClass
      && (parent.assetClasses ?? []).length > 0
      && !parent.assetClasses.some((item) => item.toLowerCase() === assetClass.toLowerCase())
    ) {
      errors.push(`Rule ${ruleNo}: Asset Class must belong to the selected ${label}.`);
    }
  });

  visibleIndex = 0;
  rules.forEach((rule) => {
    if (!ruleHasDimensionContent(rule)) return;
    if (!ruleMatchesCurrentLayout(rule, layout, parents)) return;
    visibleIndex += 1;
    if (!isStructureAssetClass(rule.dimensions.asset_class)) return;
    if (rule.issuer_percentage == null) return;
    const pct = Number(rule.issuer_percentage);
    if (Number.isNaN(pct) || pct < 0 || pct > 100) {
      errors.push(`Rule ${visibleIndex}: Issuer % must be between 0% and 100%.`);
    }
  });

  const layoutRules = rules.filter(
    (rule) => ruleHasDimensionContent(rule) && ruleMatchesCurrentLayout(rule, layout, parents),
  );

  const seenSignatures = new Map<string, number>();
  let sigIndex = 0;
  for (const rule of layoutRules) {
    sigIndex += 1;
    const signature = ruleEntrySignature(rule);
    // Empty parent+dims only happens for blank starter rows — skip those.
    if (signature === "|") continue;
    const first = seenSignatures.get(signature);
    if (first != null) {
      const parentLabel = layoutParentTitle(layout);
      errors.push(
        `Rule ${sigIndex}: duplicate of Rule ${first} — same ${
          layout === "default" ? "dimensions" : parentLabel + " / dimensions"
        } entry is not allowed.`,
      );
      continue;
    }
    seenSignatures.set(signature, sigIndex);
  }

  for (const [key, label] of DIMENSIONS) {
    // Macro is the parent. AC+Type is excluded from Asset Class only when another
    // rule under the same Macro shares that Asset Class (then it counts in Type).
    const dimensionRules = layoutRules.filter((rule) =>
      ruleCountsTowardDimension(rule, key, layoutRules),
    );
    if (layout === "macro" || layout === "bridge") {
      const parentLabel = layoutParentTitle(layout);
      const totalsByParent = new Map<string, { label: string; total: number }>();
      for (const rule of dimensionRules) {
        const parentId = String(rule.layout_parent_id ?? "").trim();
        const mapKey = parentId.toLowerCase() || "__none__";
        const meta = parentId ? findLayoutParent(parents, parentId) : undefined;
        const display = cleanLabel(meta?.label || parentId || parentLabel);
        const prev = totalsByParent.get(mapKey) ?? { label: display, total: 0 };
        prev.total += Number(rule.target_percentage) || 0;
        totalsByParent.set(mapKey, prev);
      }
      for (const { label: macroName, total } of totalsByParent.values()) {
        const rounded = roundPct2(total);
        if (rounded > 100) {
          errors.push(
            `${parentLabel} "${macroName}" ${label} targets total ${rounded}% (maximum is 100%). Lower one or more ${label} rules under that ${parentLabel}.`,
          );
        }
      }
    } else {
      const total = roundPct2(
        dimensionRules.reduce((sum, rule) => sum + (Number(rule.target_percentage) || 0), 0),
      );
      if (total > 100) {
        errors.push(
          `${label} targets total ${total}% (maximum is 100%). Lower one or more ${label} rules.`,
        );
      }
    }
  }

  if (layout === "macro" || layout === "bridge") {
    const parentLabel = layoutParentTitle(layout);
    const parentSlices = layoutParentChartSlices(layoutRules, parents);
    const parentTotal = roundPct2(parentSlices.reduce((sum, slice) => sum + slice.value, 0));
    if (parentTotal > 100) {
      errors.push(
        `${parentLabel} targets total ${parentTotal}% (maximum is 100%). Lower one or more ${parentLabel} rules.`,
      );
    }
  }

  return errors;
}

function normalizeIsinEntry(item: { code: string; name: string } | string): { code: string; name: string } {
  if (typeof item === "string") {
    const code = item.trim();
    return { code, name: code };
  }
  const code = String(item.code ?? "").trim();
  const name = String(item.name ?? "").trim() || code;
  return { code, name };
}

function isinLabelLookup(data: DriftSettingsData): Map<string, string> {
  const map = new Map<string, string>();
  for (const item of [...(data.masterIsinList ?? []), ...(data.isinList ?? [])]) {
    const entry = normalizeIsinEntry(item);
    if (!entry.code) continue;
    // Prefer a real name over a code-as-name when both catalogs have the key.
    const existing = map.get(entry.code);
    if (!existing || existing === entry.code) {
      map.set(entry.code, entry.name);
    }
  }
  return map;
}

function enrichIsinRules(rules: DriftIsinRule[], data: DriftSettingsData): DriftIsinRule[] {
  const labels = isinLabelLookup(data);
  return rules.map((rule) => {
    const kind = rule.kind === "all" || rule.kind === "except" || rule.kind === "single"
      ? rule.kind
      : normalizeIsinKind(undefined, rule.isin ?? "");
    if (kind === "all") {
      return {
        ...rule,
        kind: "all" as const,
        isin: DRIFT_ISIN_ALL,
        name: "Select All",
        target_percentage: Number(rule.target_percentage ?? 0),
      };
    }
    const code = ruleIsinCode(rule);
    return {
      ...rule,
      kind,
      isin: code,
      name: rule.name || (code ? labels.get(code) || code : ""),
      target_percentage: Number(rule.target_percentage ?? 0),
    };
  });
}

/** Stable signature so inherited rows can be removed without wiping manual ones. */
function driftRuleSignature(rule: DriftRule): string {
  const dims = Object.keys(rule.dimensions ?? {})
    .sort()
    .map((key) => `${key}=${String(rule.dimensions[key as DriftDimensionKey] ?? "").trim()}`)
    .join("|");
  const parent = String(rule.layout_parent_id ?? "").trim();
  return `${dims}|p=${parent}|pct=${Number(rule.target_percentage ?? 0)}|m=${rule.margin ?? ""}|op=${rule.operator ?? ""}`;
}

function driftIsinSignature(rule: DriftIsinRule): string {
  return `kind=${rule.kind}|isin=${ruleIsinCode(rule)}|pct=${Number(rule.target_percentage ?? 0)}|m=${rule.margin ?? ""}|op=${rule.operator ?? ""}`;
}

/** Stamp layout so inherited / legacy ISIN rows stay visible on Macro/Bridge. */
function stampIsinRulesForLayout(
  rules: DriftIsinRule[],
  layout: DriftLayout,
): DriftIsinRule[] {
  return rules.map((rule) => {
    const stored = String(rule.layout_parent_type ?? "").trim().toLowerCase();
    if (stored === "macro" || stored === "bridge" || stored === "default") {
      return rule;
    }
    return {
      ...rule,
      layout_parent_type: layout,
      layout_parent_id: rule.layout_parent_id ?? null,
    };
  });
}

/** Stamp layout so inherited / legacy dimension rules stay visible on Macro/Bridge. */
function stampDimensionRulesForLayout(
  rules: DriftRule[],
  layout: DriftLayout,
): DriftRule[] {
  return rules.map((rule) => {
    const stored = String(rule.layout_parent_type ?? "").trim().toLowerCase();
    if (stored === "macro" || stored === "bridge" || stored === "default") {
      return rule;
    }
    return {
      ...rule,
      layout_parent_type: layout,
      layout_parent_id: rule.layout_parent_id ?? null,
    };
  });
}

/**
 * Append inherited ISIN rules without duplicating Select All or the same ISIN code.
 * When both sides have Select All, `preferAll` chooses which one to keep.
 */
function appendInheritedIsinRules(
  base: DriftIsinRule[],
  incoming: DriftIsinRule[],
  preferAll: "keep-base" | "keep-incoming" = "keep-base",
): { next: DriftIsinRule[]; appended: DriftIsinRule[]; displacedAll: DriftIsinRule | null } {
  const incomingAll = incoming.find((rule) => rule.kind === "all") ?? null;
  const baseAll = base.find((rule) => rule.kind === "all") ?? null;
  let working = [...base];
  let displacedAll: DriftIsinRule | null = null;

  if (incomingAll && baseAll && preferAll === "keep-incoming") {
    displacedAll = baseAll;
    working = working.filter((rule) => rule.kind !== "all");
  }

  const appended: DriftIsinRule[] = [];
  let hasAll = working.some((rule) => rule.kind === "all");
  const taken = new Set(
    working
      .filter((rule) => rule.kind !== "all")
      .map(ruleIsinCode)
      .filter(Boolean),
  );

  for (const rule of incoming) {
    if (rule.kind === "all") {
      if (hasAll) continue;
      hasAll = true;
      appended.push(rule);
      continue;
    }
    const code = ruleIsinCode(rule);
    if (code && taken.has(code)) continue;
    if (code) taken.add(code);
    appended.push(rule);
  }

  return { next: [...working, ...appended], appended, displacedAll };
}

function colorForDriftLabel(label: string, index: number) {
  return resolveAssetClassColor(label, index);
}
const cleanLabel = (value: string) => value
  .replace(/<[^>]*>/g, " ")
  .replace(/&nbsp;/gi, " ")
  .replace(/&amp;/gi, "&")
  .replace(/\s+/g, " ")
  .trim();

/**
 * Whether a rule counts toward a dimension total under its Macro.
 *
 * Asset Class + Asset Type is excluded from the Asset Class total only when another
 * rule under the same Macro shares that Asset Class — then it counts in Asset Type.
 * Other dimensions use most-specific (leaf) dimension in DIMENSIONS order.
 */
function ruleCountsTowardDimension(
  rule: DriftRule,
  dimension: DriftDimensionKey,
  peers: DriftRule[],
): boolean {
  const dimVal = (r: DriftRule, key: DriftDimensionKey) =>
    String(r.dimensions[key] ?? "").trim();
  if (dimVal(rule, dimension) === "") return false;

  if (dimension === "asset_class") {
    const assetClass = dimVal(rule, "asset_class").toLowerCase();
    const hasType = dimVal(rule, "asset_type") !== "";
    if (hasType) {
      const parentId = String(rule.layout_parent_id ?? "").trim().toLowerCase();
      const sameAcPeer = peers.some((peer) => {
        if (peer === rule) return false;
        if (String(peer.layout_parent_id ?? "").trim().toLowerCase() !== parentId) return false;
        return dimVal(peer, "asset_class").toLowerCase() === assetClass;
      });
      if (sameAcPeer) return false;
    }
    return true;
  }

  if (dimension === "asset_type") {
    if (dimVal(rule, "asset_type") === "") return false;
    // Still leaf vs industry/sector/… so Type+Industry counts in Industry.
    const typeIdx = DIMENSIONS.findIndex(([key]) => key === "asset_type");
    for (let i = typeIdx + 1; i < DIMENSIONS.length; i += 1) {
      if (dimVal(rule, DIMENSIONS[i][0]) !== "") return false;
    }
    return true;
  }

  const idx = DIMENSIONS.findIndex(([key]) => key === dimension);
  if (idx < 0) return false;
  for (let i = idx + 1; i < DIMENSIONS.length; i += 1) {
    if (dimVal(rule, DIMENSIONS[i][0]) !== "") return false;
  }
  return true;
}

function chartRulesForDimension(rules: DriftRule[], dimension: DriftDimensionKey) {
  return rules.filter((rule) => ruleCountsTowardDimension(rule, dimension, rules));
}

function dimensionValueCount(rule: DriftRule): number {
  return Object.values(rule.dimensions ?? {}).filter((value) => String(value ?? "").trim() !== "").length;
}

/** Parent + dimensions fingerprint — duplicates are not allowed. */
function ruleEntrySignature(rule: DriftRule): string {
  const parent = String(rule.layout_parent_id ?? "").trim().toLowerCase();
  const dims = Object.entries(rule.dimensions ?? {})
    .filter(([, value]) => String(value ?? "").trim() !== "")
    .map(([key, value]) => `${key}=${String(value).trim().toLowerCase()}`)
    .sort()
    .join("&");
  return `${parent}|${dims}`;
}

/**
 * Macro / Bridge chart slices: prefer parent-only targets; when none exist for a
 * parent, roll up single Asset Class rules under that parent (Bridge-style rows).
 */
function layoutParentChartSlices(
  rules: DriftRule[],
  parents: DriftLayoutParent[],
): Array<{ key: string; label: string; value: number; color: string }> {
  type Acc = { label: string; parentOnly: number | null; childSum: number };
  const byParent = new Map<string, Acc>();

  for (const rule of rules) {
    const parentId = String(rule.layout_parent_id ?? "").trim();
    if (!parentId) continue;

    const dims = dimensionValueCount(rule);
    const isParentOnly = dims === 0;
    const isAssetClassOnly = dims === 1 && String(rule.dimensions.asset_class ?? "").trim() !== "";
    if (!isParentOnly && !isAssetClassOnly) continue;

    const key = parentId.toLowerCase();
    const meta = findLayoutParent(parents, parentId);
    const label = cleanLabel(meta?.label || parentId);
    const entry = byParent.get(key) ?? { label, parentOnly: null, childSum: 0 };
    const pct = Number(rule.target_percentage) || 0;

    if (isParentOnly) {
      entry.parentOnly = (entry.parentOnly ?? 0) + pct;
    } else {
      entry.childSum += pct;
    }

    byParent.set(key, entry);
  }

  return [...byParent.entries()].map(([key, entry], index) => {
    const value = entry.parentOnly != null ? entry.parentOnly : entry.childSum;
    return {
      key,
      label: entry.label,
      value: roundPct2(value),
      color: colorForDriftLabel(entry.label, index),
    };
  });
}

function Donut({
  slices,
  emptyLabel = "0%",
  /** When set, center label uses this instead of summing all slices (e.g. max per-Macro total). */
  centerTotal,
  warnOver100 = true,
}: {
  slices: Array<{ value: number; color: string }>;
  emptyLabel?: string;
  centerTotal?: number | null;
  warnOver100?: boolean;
}) {
  const positive = slices.filter((slice) => Number.isFinite(slice.value) && slice.value > 0);
  const summed = positive.reduce((sum, slice) => sum + slice.value, 0);
  const total = typeof centerTotal === "number" && Number.isFinite(centerTotal) ? centerTotal : summed;
  const gradient = positive.reduce<{ cursor: number; stops: string[] }>(
    (result, slice) => {
      const end = result.cursor + Math.min(slice.value, Math.max(0, 100 - result.cursor));
      return {
        cursor: end,
        stops: [...result.stops, `${slice.color} ${result.cursor}% ${end}%`],
      };
    },
    { cursor: 0, stops: [] },
  );
  const stops = gradient.cursor < 100
    ? [...gradient.stops, `var(--muted) ${gradient.cursor}% 100%`]
    : gradient.stops;

  return (
    <div
      className="relative size-24 shrink-0 rounded-full"
      style={{ background: `conic-gradient(${stops.join(", ") || "var(--muted) 0 100%"})` }}
    >
      <div className="absolute inset-[11px] grid place-items-center rounded-full bg-card shadow-[inset_0_0_0_1px_var(--border)]">
        <span
          className={`font-semibold text-sm tabular-nums ${
            warnOver100 && total > 100 ? "text-destructive" : ""
          }`}
        >
          {slices.length ? `${Number(total.toFixed(2))}%` : emptyLabel}
        </span>
      </div>
    </div>
  );
}

const CHART_LEGEND_PREVIEW = 3;

type ChartLegendItem = {
  key: string;
  label: string;
  valueLabel: string;
  color: string;
};

/** Preview up to 3 rows; “Show more” + hover lists the full set. */
function ChartLegendList({
  items,
  emptyText,
}: {
  items: ChartLegendItem[];
  emptyText: string;
}) {
  if (!items.length) {
    return <p className="text-center text-muted-foreground text-xs">{emptyText}</p>;
  }

  const preview = items.slice(0, CHART_LEGEND_PREVIEW);
  const hasMore = items.length > CHART_LEGEND_PREVIEW;

  const rows = (list: ChartLegendItem[]) =>
    list.map((item) => (
      <div key={item.key} className="flex min-w-0 items-center gap-1 text-[10px]">
        <span className="size-2 shrink-0 rounded-full" style={{ backgroundColor: item.color }} />
        <span className="truncate" title={item.label}>
          {item.label}
        </span>
        <span className="ml-auto shrink-0 tabular-nums">{item.valueLabel}</span>
      </div>
    ));

  return (
    <div className="mt-2 w-full space-y-1 text-left">
      {rows(preview)}
      {hasMore ? (
        <HoverCard openDelay={100} closeDelay={80}>
          <HoverCardTrigger asChild>
            <button
              type="button"
              className="mt-0.5 w-full rounded-md px-1 py-0.5 text-left text-[10px] font-medium text-primary transition-colors hover:bg-primary/10"
            >
              Show more (+{items.length - CHART_LEGEND_PREVIEW})
            </button>
          </HoverCardTrigger>
          <HoverCardContent align="start" className="w-64 p-3">
            <p className="mb-2 font-medium text-[11px] text-muted-foreground uppercase tracking-wide">
              All {items.length} entries
            </p>
            <div className="max-h-64 space-y-1.5 overflow-y-auto">{rows(items)}</div>
          </HoverCardContent>
        </HoverCard>
      ) : null}
    </div>
  );
}

function ruleChartLabel(
  rule: DriftRule,
  dimensionLabel: string,
  layout: DriftLayout,
  parents: DriftLayoutParent[],
): string {
  const base = cleanLabel(dimensionLabel);
  if (layout !== "macro" && layout !== "bridge") return base;
  const parentId = String(rule.layout_parent_id ?? "").trim();
  if (!parentId) return base;
  const meta = findLayoutParent(parents, parentId);
  const parentName = cleanLabel(meta?.label || parentId);
  return base ? `${parentName} › ${base}` : parentName;
}

function TotalCards({
  rules,
  data,
  advanced = false,
}: {
  rules: DriftRule[];
  data: DriftSettingsData;
  advanced?: boolean;
}) {
  const currentLayout = normalizeDriftLayout(data.defaultLayout);
  const parents = data.layoutParents ?? [];
  const layoutRules = rules.filter((rule) => ruleMatchesCurrentLayout(rule, currentLayout, parents));
  const showLayoutParentChart = currentLayout === "macro" || currentLayout === "bridge";
  const layoutParentLabel = layoutParentTitle(currentLayout);
  const layoutParentSlices = !showLayoutParentChart
    ? []
    : advanced
      ? layoutRules
          .filter((rule) => String(rule.layout_parent_id ?? "").trim())
          .map((rule, index) => {
            const parentId = String(rule.layout_parent_id ?? "").trim();
            const meta = findLayoutParent(parents, parentId);
            const label = cleanLabel(meta?.label || parentId);
            return {
              key: `${parentId}-${index}`,
              label,
              value: Number(rule.target_percentage) || 0,
              color: colorForDriftLabel(label, index),
              operator: rule.operator === "gt" ? ">" : "<",
            };
          })
      : layoutParentChartSlices(layoutRules, parents).map((slice) => ({
          ...slice,
          operator: "",
        }));
  const hasLayoutParentData = parents.length > 0;
  const gridColsClass = showLayoutParentChart
    ? "grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-7"
    : "grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6";

  return (
    <div className={gridColsClass}>
      {showLayoutParentChart ? (
        <div className="flex min-h-48 flex-col items-center rounded-xl border bg-card p-4 text-center transition-shadow hover:shadow-sm">
          <Donut
            slices={layoutParentSlices.map((slice) => ({
              value: slice.value,
              color: slice.color,
            }))}
            emptyLabel={hasLayoutParentData ? "0%" : "N/A"}
          />
          <p className="mt-3 font-medium text-[11px] uppercase tracking-wide text-muted-foreground">
            {layoutParentLabel}
          </p>
          <ChartLegendList
            items={layoutParentSlices.map((slice) => ({
              key: slice.key,
              label: slice.label,
              valueLabel: `${advanced ? slice.operator : ""}${slice.value}%`,
              color: slice.color,
            }))}
            emptyText={hasLayoutParentData ? "No rules" : "No data"}
          />
        </div>
      ) : null}
      {DIMENSIONS.map(([key, label]) => {
        const dimensionRules = chartRulesForDimension(layoutRules, key);
        const hasDimensionData = (data.dimensions[key] ?? []).length > 0;
        const chartTitle =
          currentLayout === "macro" || currentLayout === "bridge"
            ? `${layoutParentTitle(currentLayout)} ${label}`
            : label;
        const legendItems: ChartLegendItem[] = dimensionRules.map((rule, index) => {
          const raw = cleanLabel(rule.dimensions[key] ?? "");
          const display = ruleChartLabel(rule, raw, currentLayout, parents);
          return {
            key: `${rule.layout_parent_id ?? ""}-${raw}-${index}`,
            label: display,
            valueLabel: `${advanced ? (rule.operator === "gt" ? ">" : "<") : ""}${rule.target_percentage}%`,
            color: colorForDriftLabel(raw || display, index),
          };
        });
        // Macro/Bridge: center % is max per-Macro total for this leaf dimension.
        let centerTotal: number | null = null;
        if (currentLayout === "macro" || currentLayout === "bridge") {
          const byParent = new Map<string, number>();
          for (const rule of dimensionRules) {
            const pid = String(rule.layout_parent_id ?? "").trim().toLowerCase() || "__none__";
            byParent.set(pid, (byParent.get(pid) ?? 0) + (Number(rule.target_percentage) || 0));
          }
          centerTotal = byParent.size
            ? Math.max(...[...byParent.values()].map((v) => roundPct2(v)))
            : 0;
        }
        return (
          <div key={key} className="flex min-h-48 flex-col items-center rounded-xl border bg-card p-4 text-center transition-shadow hover:shadow-sm">
            <Donut
              slices={dimensionRules.map((rule, index) => ({
                value: Number(rule.target_percentage || 0),
                color: colorForDriftLabel(cleanLabel(rule.dimensions[key] ?? ""), index),
              }))}
              emptyLabel={hasDimensionData ? "0%" : "N/A"}
              centerTotal={centerTotal}
            />
            <p className="mt-3 font-medium text-[11px] uppercase tracking-wide text-muted-foreground">
              {chartTitle}
            </p>
            <ChartLegendList
              items={legendItems}
              emptyText={hasDimensionData ? "No rules" : "No data"}
            />
          </div>
        );
      })}
    </div>
  );
}

function SearchableSelect({
  value,
  onChange,
  options,
  placeholder = "Select...",
  searchPlaceholder = "Search...",
  disabled = false,
  allowCustomValue = false,
  disabledValues = [],
}: {
  value: string;
  onChange: (value: string) => void;
  options: Array<{ value: string; label: string }>;
  placeholder?: string;
  searchPlaceholder?: string;
  disabled?: boolean;
  /** Let the typed text be committed as-is, for values not held in the portfolio yet. */
  allowCustomValue?: boolean;
  /** Option values that cannot be newly selected (still shown, grayed out). */
  disabledValues?: string[];
}) {
  const [open, setOpen] = React.useState(false);
  const [search, setSearch] = React.useState("");
  const blocked = React.useMemo(
    () => new Set(disabledValues.filter((code) => code && code !== value)),
    [disabledValues, value],
  );

  const selectedOption = React.useMemo(
    () => options.find((opt) => opt.value === value),
    [options, value]
  );

  const filteredOptions = React.useMemo(() => {
    const q = search.trim().toLowerCase();
    if (!q) return options;
    return options.filter((opt) =>
      opt.label.toLowerCase().includes(q) || opt.value.toLowerCase().includes(q)
    );
  }, [options, search]);

  const customValue = search.trim();
  const canCreate =
    allowCustomValue &&
    customValue.length > 0 &&
    !options.some((opt) => opt.value.toLowerCase() === customValue.toLowerCase()) &&
    !blocked.has(customValue);

  const commitCustomValue = () => {
    if (!canCreate) return;
    onChange(customValue);
    setOpen(false);
    setSearch("");
  };

  return (
    <Popover open={open} onOpenChange={(next) => { setOpen(next); if (!next) setSearch(""); }}>
      <PopoverTrigger asChild>
        <Button
          variant="outline"
          role="combobox"
          aria-expanded={open}
          disabled={disabled}
          className="h-9 w-full justify-between bg-background px-3 font-normal text-sm hover:bg-accent/50 text-left"
        >
          <span className="truncate">
            {selectedOption?.label
              ?? (value && value !== "__none__" && value !== "__any__" ? value : placeholder)}
          </span>
          <Search className="ml-2 size-3.5 shrink-0 opacity-40" />
        </Button>
      </PopoverTrigger>
      <PopoverContent className="w-[var(--radix-popover-trigger-width)] min-w-[260px] max-w-[420px] p-0 shadow-lg" align="start">
        <div className="p-2 border-b border-border/70 bg-muted/20">
          <div className="relative flex items-center">
            <Search className="absolute left-2.5 size-3.5 text-muted-foreground pointer-events-none" />
            <Input
              placeholder={searchPlaceholder}
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === "Enter" && canCreate) {
                  e.preventDefault();
                  commitCustomValue();
                }
              }}
              className="h-8 pl-8 pr-7 text-xs bg-background"
              autoFocus
            />
            {search ? (
              <button
                type="button"
                onClick={() => setSearch("")}
                className="absolute right-2 text-muted-foreground hover:text-foreground p-0.5 rounded-sm"
              >
                <X className="size-3" />
              </button>
            ) : null}
          </div>
        </div>
        <div className="max-h-60 overflow-y-auto p-1 space-y-0.5">
          {filteredOptions.map((opt) => {
            const isSelected = value === opt.value;
            const isBlocked = blocked.has(opt.value);
            return (
              <button
                key={opt.value}
                type="button"
                disabled={isBlocked}
                className={cn(
                  "flex w-full items-center justify-between rounded-md px-2.5 py-1.5 text-xs text-left transition-colors",
                  isBlocked
                    ? "cursor-not-allowed opacity-45"
                    : "hover:bg-accent hover:text-accent-foreground",
                  isSelected && !isBlocked && "bg-accent/70 font-medium text-foreground"
                )}
                onClick={() => {
                  if (isBlocked) return;
                  onChange(opt.value);
                  setOpen(false);
                  setSearch("");
                }}
              >
                <span className="truncate pr-2" title={opt.label}>
                  {opt.label}
                  {isBlocked ? " (already selected)" : ""}
                </span>
                {isSelected && !isBlocked && <Check className="size-3.5 text-primary shrink-0 ml-auto" />}
              </button>
            );
          })}
          {canCreate && (
            <button
              type="button"
              className="flex w-full items-center gap-1.5 rounded-md px-2.5 py-1.5 text-xs text-left transition-colors hover:bg-accent hover:text-accent-foreground"
              onClick={commitCustomValue}
            >
              <Plus className="size-3.5 shrink-0 text-primary" />
              <span className="truncate">
                Use &ldquo;<span className="font-medium">{customValue}</span>&rdquo;
              </span>
            </button>
          )}
          {filteredOptions.length === 0 && !canCreate && (
            <div className="py-4 text-center text-xs text-muted-foreground">
              No matching options found
            </div>
          )}
        </div>
      </PopoverContent>
    </Popover>
  );
}

/**
 * Customer's Asset Type suggestions (always the full held list — not filtered by
 * Asset Class). Prefer the backend `default` list, which drops instrument-level
 * Structure / Accu / Decu titles. Falls back to the flat dimension list when a
 * backend without the map answers.
 */
function assetTypeSuggestions(
  data: DriftSettingsData,
  _selectedAssetClass?: string | null,
): string[] {
  const allAssetTypes = data.dimensions.asset_type ?? [];
  const assetTypeMap = data.assetTypeMap;
  if (!assetTypeMap) return allAssetTypes;
  return assetTypeMap.default ?? allAssetTypes;
}

function RuleRow({
  rule,
  index,
  data,
  advanced,
  readOnly,
  allowRemove = true,
  onChange,
  onRemove,
}: {
  rule: DriftRule;
  index: number;
  data: DriftSettingsData;
  advanced?: boolean;
  readOnly?: boolean;
  allowRemove?: boolean;
  onChange: (updatedRule: DriftRule) => void;
  onRemove: () => void;
}) {
  const currentLayout = normalizeDriftLayout(data.defaultLayout);
  const parentOptions = data.layoutParents ?? [];
  const ruleLayout = resolveRuleLayout(rule, currentLayout, parentOptions);
  const layoutLocked = ruleHasDimensionContent(rule) && ruleLayout !== currentLayout;
  const rowReadOnly = Boolean(readOnly || layoutLocked);
  const showLayoutParent = ruleLayout === "macro" || ruleLayout === "bridge";
  const parentTitle = layoutParentTitle(ruleLayout);
  const parentId = String(rule.layout_parent_id ?? "").trim();
  const stampedParentType: DriftLayout = currentLayout;
  const parentSelectOptions = (() => {
    const options = parentOptions.map((item) => ({
      value: item.id,
      label: item.label || item.id,
    }));
    // Keep a saved orphan Macro/Bridge id visible so the rule can be edited/reassigned.
    if (parentId && !findLayoutParent(parentOptions, parentId)) {
      options.unshift({ value: parentId, label: `${parentId} (not in catalog)` });
    }
    return options;
  })();

  return (
    <div
      className={cn(
        "rounded-xl border bg-card p-4 space-y-3 shadow-xs",
        layoutLocked && "border-dashed opacity-90",
      )}
    >
      <div className="flex items-center justify-between gap-3">
        <div className="min-w-0">
          <span className="flex items-center gap-2 font-semibold text-sm text-foreground">
            <span className="flex size-6 items-center justify-center rounded-full bg-primary/10 font-semibold text-primary text-xs tabular-nums">
              {index + 1}
            </span>
            {advanced ? "Advanced Rule" : "Rule"}
          </span>
          {layoutLocked ? (
            <p className="mt-1 text-muted-foreground text-xs">
              Saved under {ruleLayout === "default" ? "Default" : parentTitle} layout — view only
              while Layout is {currentLayout === "default" ? "Default" : layoutParentTitle(currentLayout)}.
            </p>
          ) : null}
        </div>
        {!readOnly && allowRemove && (
          <Button
            variant="ghost"
            size="icon-sm"
            onClick={onRemove}
            title={layoutLocked ? "Remove this saved rule" : "Delete rule"}
          >
            <Trash2 className="size-4 text-destructive hover:text-destructive/80" />
          </Button>
        )}
      </div>
      <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
        {showLayoutParent && (
          <SearchableSelect
            disabled={rowReadOnly}
            value={rule.layout_parent_id || "__none__"}
            options={[
              { value: "__none__", label: `Select ${parentTitle}` },
              ...parentSelectOptions,
            ]}
            placeholder={`Select ${parentTitle}`}
            searchPlaceholder={`Search ${parentTitle.toLowerCase()}…`}
            onChange={(value) => {
              const nextParentId = value === "__none__" ? undefined : value;
              const dimensions = { ...rule.dimensions };
              const nextParent = findLayoutParent(parentOptions, nextParentId);
              const allowed = new Set((nextParent?.assetClasses ?? []).map((item) => item.toLowerCase()));
              if (
                dimensions.asset_class
                && (
                  !nextParentId
                  || (allowed.size > 0 && !allowed.has(dimensions.asset_class.toLowerCase()))
                )
              ) {
                delete dimensions.asset_class;
              }
              onChange({
                ...rule,
                layout_parent_id: nextParentId ?? null,
                layout_parent_type: stampedParentType,
                dimensions,
              });
            }}
          />
        )}
        {DIMENSIONS.map(([key, label]) => {
          const currentValue = rule.dimensions[key];
          const availableOptions = key === "asset_type"
            ? assetTypeSuggestions(data, rule.dimensions.asset_class)
            : key === "asset_class"
              ? assetClassOptionsForRule(data, rule)
              : data.dimensions[key] ?? [];

          const options = currentValue && !availableOptions.includes(currentValue)
            ? [currentValue, ...availableOptions]
            : availableOptions;

          const isAssetClass = key === "asset_class";
          const assetClassLocked = isAssetClass && showLayoutParent && !rule.layout_parent_id && !layoutLocked;
          const dimensionOptions = isAssetClass
            ? [
                { value: "__none__", label: assetClassLocked ? `Select ${parentTitle} first` : "Select Asset Class" },
                ...options.map((val) => ({
                  value: val,
                  label: cleanLabel(val) || val,
                })),
              ]
            : [
                { value: "__any__", label: `Any ${label}` },
                ...options.map((val) => ({
                  value: val,
                  label: cleanLabel(val) || val,
                })),
              ];

          return (
            <SearchableSelect
              key={key}
              disabled={rowReadOnly || assetClassLocked}
              value={isAssetClass ? (currentValue || "__none__") : (currentValue || "__any__")}
              options={dimensionOptions}
              placeholder={isAssetClass ? "Select Asset Class" : `Any ${label}`}
              searchPlaceholder={`Search or type ${label.toLowerCase()}…`}
              allowCustomValue={!isAssetClass || !showLayoutParent}
              onChange={(value) => {
                const dimensions = { ...rule.dimensions };
                if (isAssetClass) {
                  if (value === "__none__" || value === "__any__") {
                    delete dimensions.asset_class;
                  } else {
                    dimensions.asset_class = value;
                  }
                } else if (value === "__any__") {
                  delete dimensions[key];
                } else {
                  dimensions[key] = value;
                }

                onChange({
                  ...rule,
                  dimensions,
                  layout_parent_type: stampedParentType,
                  ...(isAssetClass && !isStructureAssetClass(dimensions.asset_class)
                    ? { issuer: null, issuer_percentage: null }
                    : {}),
                });
              }}
            />
          );
        })}
        {isStructureAssetClass(rule.dimensions.asset_class) && (
          <>
            <SearchableSelect
              disabled={rowReadOnly}
              value={rule.issuer || "__none__"}
              options={[
                { value: "__none__", label: "Select Issuer" },
                ...(() => {
                  const catalog = data.issuers ?? [];
                  const current = String(rule.issuer ?? "").trim();
                  const options = catalog.map((val) => ({ value: val, label: val }));
                  if (current && !catalog.some((item) => item.toLowerCase() === current.toLowerCase())) {
                    options.unshift({ value: current, label: current });
                  }
                  return options;
                })(),
              ]}
              placeholder="Select Issuer"
              searchPlaceholder="Search or type issuer…"
              allowCustomValue
              onChange={(value) => {
                onChange({
                  ...rule,
                  issuer: value === "__none__" ? null : value,
                });
              }}
            />
            <div className="relative w-full">
              <Input
                disabled={rowReadOnly}
                type="number"
                min="0"
                max="100"
                step="0.01"
                className="h-9 w-full bg-background pr-7"
                value={
                  rule.issuer_percentage === 0 || rule.issuer_percentage
                    ? String(rule.issuer_percentage)
                    : ""
                }
                onChange={(event) =>
                  onChange({
                    ...rule,
                    issuer_percentage:
                      event.target.value === "" ? null : Number(event.target.value),
                  })
                }
                placeholder="Issuer %"
              />
              <span className="pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">%</span>
            </div>
          </>
        )}
        {advanced && (
          <Select
            disabled={rowReadOnly}
            value={rule.operator ?? "lt"}
            onValueChange={(operator) =>
              onChange({ ...rule, operator: operator as "lt" | "gt" })
            }
          >
            <SelectTrigger className="w-full h-9 bg-background justify-between">
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="lt">Below target (&lt;)</SelectItem>
              <SelectItem value="gt">Above target (&gt;)</SelectItem>
            </SelectContent>
          </Select>
        )}
        <div className="relative w-full">
          <Input
            disabled={rowReadOnly}
            type="number"
            min="0"
            max="100"
            step="0.01"
            className="h-9 w-full bg-background pr-7"
            value={rule.target_percentage === 0 || rule.target_percentage ? String(rule.target_percentage) : ""}
            onChange={(event) =>
              onChange({
                ...rule,
                target_percentage: event.target.value === "" ? 0 : Number(event.target.value),
              })
            }
            placeholder="Target %"
          />
          <span className="pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">%</span>
        </div>
        <MarginFields
          margin={rule.margin}
          readOnly={rowReadOnly}
          onChange={(margin) => onChange({ ...rule, margin })}
        />
      </div>
    </div>
  );
}

function RuleEditor({
  title,
  rules,
  data,
  advanced,
  readOnly = false,
  protectFirst = false,
  onChange,
}: {
  title: string;
  rules: DriftRule[];
  data: DriftSettingsData;
  advanced?: boolean;
  readOnly?: boolean;
  /** Hide delete on the first row (keep at least one starter rule). */
  protectFirst?: boolean;
  onChange: (rules: DriftRule[]) => void;
}) {
  const currentLayout = normalizeDriftLayout(data.defaultLayout);
  const parents = data.layoutParents ?? EMPTY_DRIFT_LAYOUT_PARENTS;
  /** Other-layout rules stay in state/save, but are hidden from this view. */
  const hiddenRules = React.useMemo(
    () => rules.filter((rule) => !ruleMatchesCurrentLayout(rule, currentLayout, parents)),
    [rules, currentLayout, parents],
  );
  const visibleRules = React.useMemo(
    () => rules.filter((rule) => ruleMatchesCurrentLayout(rule, currentLayout, parents)),
    [rules, currentLayout, parents],
  );

  function commitVisible(nextVisible: DriftRule[]) {
    onChange([...hiddenRules, ...nextVisible]);
  }

  return (
    <section className="space-y-3 rounded-xl border bg-card p-5">
      <div className="flex items-center justify-between">
        <div>
          <div className="flex items-center gap-2">
            <h3 className="font-semibold text-base">{title}</h3>
            <Badge variant="secondary" className="tabular-nums">{visibleRules.length}</Badge>
          </div>
          <p className="text-muted-foreground text-xs">
            Each unique dimension combination defines a portfolio target.
            {currentLayout !== "default"
              ? ` Showing ${layoutParentTitle(currentLayout)} layout rules only.`
              : " Showing Default layout rules only."}
          </p>
        </div>
        {!readOnly && (
          <Button
            variant="outline"
            size="sm"
            className="gap-2"
            onClick={() =>
              commitVisible([
                ...visibleRules,
                emptyRuleForLayout(currentLayout, advanced),
              ])
            }
          >
            <Plus className="size-4" />
            Add rule
          </Button>
        )}
      </div>
      {!visibleRules.length && (
        <p className="rounded-lg bg-muted/50 p-5 text-center text-muted-foreground text-sm">
          No {currentLayout === "default" ? "Default" : layoutParentTitle(currentLayout)} layout rules defined.
        </p>
      )}
      {visibleRules.map((rule, index) => (
        <RuleRow
          key={`rule-${currentLayout}-${index}`}
          rule={rule}
          index={index}
          data={data}
          advanced={advanced}
          readOnly={readOnly}
          allowRemove={!protectFirst || index > 0}
          onChange={(updatedRule) =>
            commitVisible(visibleRules.map((item, i) => (i === index ? updatedRule : item)))
          }
          onRemove={() =>
            commitVisible(visibleRules.filter((_, i) => i !== index))
          }
        />
      ))}
    </section>
  );
}

function isinCatalog(data: DriftSettingsData, extraCode?: string, extraName?: string) {
  const labels = isinLabelLookup(data);
  const seen = new Set<string>();
  const list: Array<{ code: string; name: string }> = [];
  for (const item of data.isinList ?? []) {
    const entry = normalizeIsinEntry(item);
    if (!entry.code || isSelectAllCode(entry.code) || seen.has(entry.code)) continue;
    seen.add(entry.code);
    list.push({ code: entry.code, name: labels.get(entry.code) || entry.name || entry.code });
  }
  const extra = String(extraCode ?? "").trim();
  if (extra && !isSelectAllCode(extra) && !seen.has(extra)) {
    list.unshift({ code: extra, name: extraName || labels.get(extra) || extra });
  }
  return list;
}

/** Dropdown label: `CODE (Name)` when name differs; otherwise just the code. */
function formatIsinOptionLabel(code: string, name?: string | null): string {
  const cleanCode = String(code ?? "").trim();
  const cleanName = String(name ?? "").trim();
  if (!cleanCode) return cleanName;
  if (!cleanName || cleanName === cleanCode) return cleanCode;
  return `${cleanCode} (${cleanName})`;
}

function PctInput({
  value,
  readOnly,
  onChange,
  placeholder = "Target %",
}: {
  value: number;
  readOnly?: boolean;
  onChange: (next: number) => void;
  placeholder?: string;
}) {
  return (
    <div className="relative w-[140px]">
      <Input
        disabled={readOnly}
        type="number"
        min="0"
        max="100"
        step="0.01"
        className="h-9 w-full bg-background pr-7"
        value={value === 0 || value ? String(value) : ""}
        onChange={(event) => {
          const raw = event.target.value;
          if (raw === "") {
            onChange(0);
            return;
          }
          const next = Math.min(100, Math.max(0, Number(raw)));
          onChange(Number.isNaN(next) ? 0 : next);
        }}
        onBlur={() => onChange(roundPct2(Number(value) || 0))}
        placeholder={placeholder}
      />
      <span className="pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">%</span>
    </div>
  );
}

function IsinRow({
  rule,
  data,
  advanced,
  readOnly,
  allowRemove = true,
  offerSelectAll = false,
  takenIsins = [],
  openIsin,
  positions,
  positionsLoading,
  togglePositions,
  onChange,
  onRemove,
  onPickSelectAll,
}: {
  rule: DriftIsinRule;
  data: DriftSettingsData;
  advanced?: boolean;
  readOnly?: boolean;
  allowRemove?: boolean;
  /** Show Select All at the top of the ISIN dropdown. */
  offerSelectAll?: boolean;
  takenIsins?: string[];
  openIsin: string;
  positions: DriftPositionsData["positions"] | null;
  positionsLoading: boolean;
  togglePositions: (isin: string) => Promise<void>;
  onChange: (updatedRule: DriftIsinRule) => void;
  onRemove: () => void;
  onPickSelectAll?: () => void;
}) {
  const labels = React.useMemo(() => isinLabelLookup(data), [data]);
  const code = ruleIsinCode(rule);
  const catalog = React.useMemo(
    () => isinCatalog(data, code, rule.name),
    [data, code, rule.name],
  );

  const options = [
    { value: "__none__", label: "Select ISIN" },
    ...(offerSelectAll
      ? [{ value: "__select_all__", label: "Select All (each ISIN %)" }]
      : []),
    ...catalog.map((item) => ({
      value: item.code,
      label: formatIsinOptionLabel(item.code, item.name),
    })),
  ];

  return (
    <div className="space-y-3 rounded-xl border bg-card p-4 shadow-xs">
      <div className="flex flex-wrap items-center gap-3">
        <div className="min-w-[220px] flex-1">
          <SearchableSelect
            disabled={readOnly}
            value={code || "__none__"}
            disabledValues={takenIsins}
            options={options}
            placeholder="Select ISIN"
            searchPlaceholder="Search customer ISINs…"
            onChange={(next) => {
              if (next === "__select_all__") {
                onPickSelectAll?.();
                return;
              }
              if (takenIsins.includes(next)) return;
              if (next === "__none__") {
                onChange({ ...rule, kind: "single", isin: "", name: "" });
                return;
              }
              onChange({
                ...rule,
                kind: "single",
                isin: next,
                name: labels.get(next) || catalog.find((item) => item.code === next)?.name || next,
              });
            }}
          />
        </div>
        {advanced && (
          <div className="w-[150px]">
            <Select
              disabled={readOnly}
              value={rule.operator ?? "lt"}
              onValueChange={(operator) =>
                onChange({ ...rule, operator: operator as "lt" | "gt" })
              }
            >
              <SelectTrigger className="h-9 w-full justify-between bg-background">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="lt">Below target (&lt;)</SelectItem>
                <SelectItem value="gt">Above target (&gt;)</SelectItem>
              </SelectContent>
            </Select>
          </div>
        )}
        <PctInput
          value={Number(rule.target_percentage) || 0}
          readOnly={readOnly}
          onChange={(target_percentage) => onChange({ ...rule, target_percentage })}
        />
        <MarginFields
          margin={rule.margin}
          readOnly={readOnly}
          onChange={(margin) => onChange({ ...rule, margin })}
        />
        {code ? (
          <Button
            type="button"
            variant="outline"
            size="sm"
            className="h-9"
            onClick={() => void togglePositions(code)}
          >
            {openIsin === code ? "Hide" : "Positions"}
          </Button>
        ) : null}
        {!readOnly && allowRemove && (
          <Button
            type="button"
            variant="ghost"
            size="icon-sm"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              onRemove();
            }}
            title="Delete rule"
          >
            <Trash2 className="size-4 text-destructive hover:text-destructive/80" />
          </Button>
        )}
      </div>
      {Boolean(code) && openIsin === code && (
        <div className="mt-1 overflow-x-auto rounded-lg bg-muted/40 p-3">
          {positionsLoading ? (
            <p className="text-muted-foreground text-sm">Loading positions…</p>
          ) : positions?.rows.length ? (
            <>
              <p className="mb-2 font-medium text-sm">
                Total {formatQuantity(positions.total)} {positions.currency}
              </p>
              <table className="w-full text-left text-xs">
                <thead>
                  <tr>
                    {["Name", "ISIN", "Bank", "Qty", "Price", "Value"].map((heading) => (
                      <th key={heading} className="border-b p-2 font-medium">{heading}</th>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {positions.rows.map((position, positionIndex) => (
                    <tr key={`${position.ticker}-${position.bank_name}-${positionIndex}`}>
                      <td className="p-2">{position.name}</td>
                      <td className="p-2 font-mono">{position.isin || position.ticker}</td>
                      <td className="p-2">{position.bank_name}</td>
                      <td className="p-2">{position.qty}</td>
                      <td className="p-2">{position.price}</td>
                      <td className="p-2">{formatQuantity(position.value)}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </>
          ) : (
            <p className="text-muted-foreground text-sm">No positions found.</p>
          )}
        </div>
      )}
    </div>
  );
}

function SelectAllCard({
  selectAll,
  excepts,
  data,
  advanced,
  readOnly,
  takenIsins,
  takenByExcept,
  onChangeSelectAll,
  onChangeExcepts,
  onRemoveSelectAll,
}: {
  selectAll: DriftIsinRule;
  excepts: DriftIsinRule[];
  data: DriftSettingsData;
  advanced?: boolean;
  readOnly?: boolean;
  takenIsins: string[];
  takenByExcept: string[][];
  onChangeSelectAll: (rule: DriftIsinRule) => void;
  onChangeExcepts: (rules: DriftIsinRule[]) => void;
  onRemoveSelectAll: () => void;
}) {
  const labels = React.useMemo(() => isinLabelLookup(data), [data]);
  const exceptEnabled = excepts.length > 0;
  const exceptLayout = normalizeDriftLayout(
    selectAll.layout_parent_type ?? data.defaultLayout,
  );

  function exceptRow(partial?: Partial<DriftIsinRule>): DriftIsinRule {
    return {
      ...emptyIsinForLayout(exceptLayout, "except"),
      ...(advanced ? { operator: selectAll.operator } : {}),
      ...partial,
      // Always keep Except under the same layout as Select All.
      layout_parent_type: exceptLayout,
      layout_parent_id: partial?.layout_parent_id ?? null,
    };
  }

  function setExceptEnabled(enabled: boolean) {
    if (!enabled) {
      onChangeExcepts([]);
      return;
    }
    if (excepts.length === 0) {
      onChangeExcepts([exceptRow()]);
    }
  }

  return (
    <div className="space-y-3 rounded-xl border bg-card p-4 shadow-xs">
      <div className="flex flex-wrap items-center gap-3">
        <label className="flex h-9 items-center gap-2 rounded-md border bg-background px-2.5 text-sm">
          <Checkbox checked disabled />
          <span className="font-medium">Select All</span>
          <span className="text-muted-foreground text-xs">(each ISIN %)</span>
        </label>
        {advanced && (
          <div className="w-[150px]">
            <Select
              disabled={readOnly}
              value={selectAll.operator ?? "lt"}
              onValueChange={(operator) =>
                onChangeSelectAll({ ...selectAll, operator: operator as "lt" | "gt" })
              }
            >
              <SelectTrigger className="h-9 w-full justify-between bg-background">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="lt">Below target (&lt;)</SelectItem>
                <SelectItem value="gt">Above target (&gt;)</SelectItem>
              </SelectContent>
            </Select>
          </div>
        )}
        <PctInput
          value={Number(selectAll.target_percentage) || 0}
          readOnly={readOnly}
          onChange={(target_percentage) => onChangeSelectAll({ ...selectAll, target_percentage })}
          placeholder="Each %"
        />
        <MarginFields
          margin={selectAll.margin}
          readOnly={readOnly}
          onChange={(margin) => onChangeSelectAll({ ...selectAll, margin })}
        />
        <label className="flex h-9 items-center gap-2 rounded-md border bg-background px-2.5 text-sm">
          <Checkbox
            disabled={readOnly}
            checked={exceptEnabled}
            onCheckedChange={(checked) => setExceptEnabled(checked === true)}
          />
          <span>Except</span>
        </label>
        {!readOnly && (
          <Button
            type="button"
            variant="ghost"
            size="icon-sm"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              onRemoveSelectAll();
            }}
            title="Remove Select All"
          >
            <Trash2 className="size-4 text-destructive hover:text-destructive/80" />
          </Button>
        )}
      </div>

      {exceptEnabled && (
        <div className="space-y-2 border-t pt-3">
          <p className="text-muted-foreground text-xs">Excepted ISINs (own % each)</p>
          {excepts.map((except, index) => {
            const code = ruleIsinCode(except);
            const blockedCodes = new Set(takenByExcept[index] ?? takenIsins);
            const catalog = isinCatalog(data, code, except.name);
            // Also block other codes that share a display label with an already-selected except.
            const takenLabels = new Set(
              excepts
                .filter((_, i) => i !== index && ruleIsinCode(excepts[i]))
                .map((row) => (row.name || labels.get(ruleIsinCode(row)) || ruleIsinCode(row)).toLowerCase()),
            );
            for (const item of catalog) {
              if (item.code === code) continue;
              if (takenLabels.has((item.name || item.code).toLowerCase())) {
                blockedCodes.add(item.code);
              }
            }
            const blocked = [...blockedCodes];
            return (
              <div key={`except-${index}`} className="flex flex-wrap items-center gap-3">
                <div className="min-w-[220px] flex-1">
                  <SearchableSelect
                    disabled={readOnly}
                    value={code || "__none__"}
                    disabledValues={blocked}
                    options={[
                      { value: "__none__", label: "Select except ISIN" },
                      ...catalog.map((item) => ({
                        value: item.code,
                        label: formatIsinOptionLabel(item.code, item.name),
                      })),
                    ]}
                    placeholder="Select except ISIN"
                    searchPlaceholder="Search customer ISINs…"
                    onChange={(next) => {
                      if (blocked.includes(next)) return;
                      const updated = [...excepts];
                      if (next === "__none__") {
                        updated[index] = exceptRow({
                          ...except,
                          isin: "",
                          name: "",
                        });
                      } else {
                        updated[index] = exceptRow({
                          ...except,
                          kind: "except",
                          isin: next,
                          name: labels.get(next) || catalog.find((item) => item.code === next)?.name || next,
                        });
                      }
                      onChangeExcepts(updated);
                    }}
                  />
                </div>
                <PctInput
                  value={Number(except.target_percentage) || 0}
                  readOnly={readOnly}
                  onChange={(target_percentage) => {
                    const updated = [...excepts];
                    updated[index] = { ...except, target_percentage };
                    onChangeExcepts(updated);
                  }}
                />
                <MarginFields
                  margin={except.margin}
                  readOnly={readOnly}
                  onChange={(margin) => {
                    const updated = [...excepts];
                    updated[index] = { ...except, margin };
                    onChangeExcepts(updated);
                  }}
                />
                {!readOnly && (
                  <Button
                    type="button"
                    variant="ghost"
                    size="icon-sm"
                    onClick={(event) => {
                      event.preventDefault();
                      event.stopPropagation();
                      onChangeExcepts(excepts.filter((_, i) => i !== index));
                    }}
                    title="Remove except"
                  >
                    <Trash2 className="size-4 text-destructive hover:text-destructive/80" />
                  </Button>
                )}
              </div>
            );
          })}
          {!readOnly && (
            <Button
              variant="outline"
              size="sm"
              className="gap-2"
              onClick={() =>
                onChangeExcepts([
                  ...excepts,
                  exceptRow(),
                ])
              }
            >
              <Plus className="size-4" />
              Add except
            </Button>
          )}
        </div>
      )}
    </div>
  );
}

function IsinEditor({
  title,
  rules,
  data,
  advanced,
  tenant,
  readOnly = false,
  protectFirst = false,
  customerId,
  onChange,
}: {
  title: string;
  rules: DriftIsinRule[];
  data: DriftSettingsData;
  advanced?: boolean;
  tenant: string;
  readOnly?: boolean;
  protectFirst?: boolean;
  customerId?: number | null;
  onChange: (rules: DriftIsinRule[]) => void;
}) {
  const [openIsin, setOpenIsin] = React.useState("");
  const [positions, setPositions] = React.useState<DriftPositionsData["positions"] | null>(null);
  const [positionsLoading, setPositionsLoading] = React.useState(false);
  const [pendingSelectAll, setPendingSelectAll] = React.useState(false);
  /** Single-row index that triggered Select All (removed when confirmed). */
  const pendingSourceRef = React.useRef<number | null>(null);

  const currentLayout = normalizeDriftLayout(data.defaultLayout);
  const hiddenIsinRules = React.useMemo(
    () => rules.filter((rule) => !isinMatchesCurrentLayout(rule, currentLayout)),
    [rules, currentLayout],
  );
  const visibleIsinRules = React.useMemo(
    () => rules.filter((rule) => isinMatchesCurrentLayout(rule, currentLayout)),
    [rules, currentLayout],
  );

  function commitVisible(nextVisible: DriftIsinRule[]) {
    onChange([...hiddenIsinRules, ...nextVisible]);
  }

  const selectAllRaw = visibleIsinRules.find((rule) => rule.kind === "all") ?? null;
  const selectAll = selectAllRaw
    ? {
        ...selectAllRaw,
        layout_parent_type: normalizeDriftLayout(
          selectAllRaw.layout_parent_type ?? currentLayout,
        ),
      }
    : null;
  const excepts = visibleIsinRules
    .filter((rule) => rule.kind === "except")
    .map((rule) => ({
      ...rule,
      layout_parent_type: normalizeDriftLayout(
        rule.layout_parent_type ?? selectAll?.layout_parent_type ?? currentLayout,
      ),
    }));
  const singles = visibleIsinRules.filter((rule) => rule.kind === "single");
  const filledSingles = singles.filter((rule) => ruleIsinCode(rule));

  const takenForSelectAll = React.useMemo(() => {
    const codes = new Set<string>();
    for (const rule of singles) {
      const code = ruleIsinCode(rule);
      if (code) codes.add(code);
    }
    return [...codes];
  }, [singles]);
  const takenByExcept = React.useMemo(
    () =>
      excepts.map((_, index) => [
        ...takenForSelectAll,
        ...excepts.flatMap((other, otherIndex) =>
          otherIndex === index || !ruleIsinCode(other) ? [] : [ruleIsinCode(other)],
        ),
      ]),
    [excepts, takenForSelectAll],
  );
  const takenBySingle = React.useMemo(() => {
    const exceptCodes = excepts.map(ruleIsinCode).filter(Boolean);
    return singles.map((rule, index) => [
      ...exceptCodes,
      ...singles.flatMap((other, otherIndex) =>
        otherIndex === index || !ruleIsinCode(other) ? [] : [ruleIsinCode(other)],
      ),
    ]);
  }, [singles, excepts]);

  async function togglePositions(isin: string) {
    if (openIsin === isin) {
      setOpenIsin("");
      setPositions(null);
      return;
    }
    setOpenIsin(isin);
    setPositions(null);
    setPositionsLoading(true);
    try {
      const result = await loadDriftPositions(tenant, isin, customerId);
      setPositions(result.positions);
    } catch (loadError) {
      toastApiError(loadError, "Positions could not be loaded.");
    } finally {
      setPositionsLoading(false);
    }
  }

  function removeSingleAt(index: number) {
    let seen = 0;
    commitVisible(
      visibleIsinRules.filter((item) => {
        if (item.kind !== "single") return true;
        const drop = seen === index;
        seen += 1;
        return !drop;
      }),
    );
  }

  function updateSingleAt(index: number, updatedRule: DriftIsinRule) {
    let seen = 0;
    commitVisible(
      visibleIsinRules.map((item) => {
        if (item.kind !== "single") return item;
        const match = seen === index;
        seen += 1;
        return match ? updatedRule : item;
      }),
    );
  }

  function enableSelectAll(mode: "all" | "except") {
    const base = {
      ...emptyIsinForLayout(currentLayout, "all"),
      ...(advanced ? { operator: "lt" as const } : {}),
    };
    const sourceIndex = pendingSourceRef.current;
    const withoutSource = singles.filter((_, index) => index !== sourceIndex);
    const filled = withoutSource.filter((rule) => ruleIsinCode(rule));
    const source =
      sourceIndex != null && sourceIndex >= 0 && sourceIndex < singles.length
        ? singles[sourceIndex]
        : null;
    const sourceFilled = source && ruleIsinCode(source) ? [source] : [];
    const allFilled = [...filled, ...sourceFilled];

    if (mode === "except" && allFilled.length) {
      commitVisible([
        base,
        ...allFilled.map((rule) => ({
          ...rule,
          kind: "except" as const,
          layout_parent_type: currentLayout,
          layout_parent_id: rule.layout_parent_id ?? null,
        })),
        ...withoutSource.filter((rule) => !ruleIsinCode(rule)),
      ]);
    } else {
      commitVisible([base, ...withoutSource.filter((rule) => !ruleIsinCode(rule))]);
    }
    pendingSourceRef.current = null;
    setPendingSelectAll(false);
  }

  function requestSelectAllFromRow(sourceIndex: number) {
    if (selectAll) return;
    pendingSourceRef.current = sourceIndex;
    const othersFilled = singles.filter(
      (rule, index) => index !== sourceIndex && Boolean(ruleIsinCode(rule)),
    );
    const sourceFilled = Boolean(ruleIsinCode(singles[sourceIndex]));
    if (othersFilled.length > 0 || sourceFilled) {
      setPendingSelectAll(true);
      return;
    }
    enableSelectAll("all");
  }

  function replaceSelectAllBlock(nextAll: DriftIsinRule | null, nextExcepts: DriftIsinRule[]) {
    commitVisible([
      ...(nextAll ? [nextAll] : []),
      ...nextExcepts,
      ...singles,
    ]);
  }

  return (
    <section className="space-y-3 rounded-xl border bg-card p-4">
      <div className="flex items-center justify-between gap-3">
        <div>
          <h3 className="font-semibold text-base">{title}</h3>
          <p className="text-muted-foreground text-xs">
            Pick an ISIN or Select All from the dropdown. Use Except for carve-outs.
            {currentLayout !== "default"
              ? ` Showing ${layoutParentTitle(currentLayout)} layout ISIN rules only.`
              : " Showing Default layout ISIN rules only."}
          </p>
        </div>
        <div className="flex items-center gap-2">
          {!readOnly && (
            <Button
              type="button"
              variant="outline"
              size="sm"
              className="gap-2"
              onClick={() =>
                commitVisible([
                  ...visibleIsinRules,
                  {
                    ...emptyIsinForLayout(currentLayout),
                    ...(advanced ? { operator: "lt" as const } : {}),
                  },
                ])
              }
            >
              <Plus className="size-4" />
              Add ISIN
            </Button>
          )}
        </div>
      </div>

      {selectAll && (
        <SelectAllCard
          selectAll={selectAll}
          excepts={excepts}
          data={data}
          advanced={advanced}
          readOnly={readOnly}
          takenIsins={takenForSelectAll}
          takenByExcept={takenByExcept}
          onChangeSelectAll={(next) => replaceSelectAllBlock(next, excepts)}
          onChangeExcepts={(next) => replaceSelectAllBlock(selectAll, next)}
          onRemoveSelectAll={() => replaceSelectAllBlock(null, [])}
        />
      )}

      {singles.map((rule, index) => (
        <IsinRow
          key={`isin-single-${index}`}
          rule={rule}
          data={data}
          advanced={advanced}
          readOnly={readOnly}
          allowRemove={!readOnly && (!protectFirst || index > 0 || Boolean(selectAll) || singles.length > 1)}
          offerSelectAll={!selectAll && !readOnly}
          takenIsins={takenBySingle[index] ?? []}
          openIsin={openIsin}
          positions={positions}
          positionsLoading={positionsLoading}
          togglePositions={togglePositions}
          onChange={(updatedRule) => updateSingleAt(index, updatedRule)}
          onRemove={() => removeSingleAt(index)}
          onPickSelectAll={() => requestSelectAllFromRow(index)}
        />
      ))}

      {!visibleIsinRules.length && (
        <p className="rounded-lg bg-muted/50 p-5 text-center text-muted-foreground text-sm">
          No {currentLayout === "default" ? "Default" : layoutParentTitle(currentLayout)} layout ISIN
          rules defined. Click Add ISIN to start.
        </p>
      )}

      <AlertDialog
        open={pendingSelectAll}
        onOpenChange={(open) => {
          setPendingSelectAll(open);
          if (!open) pendingSourceRef.current = null;
        }}
      >
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Existing ISIN rules</AlertDialogTitle>
            <AlertDialogDescription>
              You already have {filledSingles.length} ISIN rule{filledSingles.length === 1 ? "" : "s"}.
              Include them in Select All, or move them to Except?
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter className="flex-col gap-2 sm:flex-row">
            <AlertDialogCancel>Cancel</AlertDialogCancel>
            <AlertDialogAction type="button" onClick={() => enableSelectAll("all")}>
              Add in All
            </AlertDialogAction>
            <AlertDialogAction type="button" onClick={() => enableSelectAll("except")}>
              Add in Except
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </section>
  );
}

function apiRules(rules: DriftRule[]) {
  return rules.map((rule) => ({
    ...rule.dimensions,
    layout_parent_id: rule.layout_parent_id ?? null,
    layout_parent_type: rule.layout_parent_type ?? null,
    issuer: rule.issuer ?? null,
    issuer_percentage: rule.issuer_percentage ?? null,
    target_percentage: rule.target_percentage,
    margin: rule.margin ?? null,
    margin_enabled: rule.margin != null,
    ...(rule.operator ? { operator: rule.operator } : {}),
  }));
}

function apiIsinRules(rules: DriftIsinRule[]) {
  const rows: Array<Record<string, unknown>> = [];
  for (const rule of rules) {
    if (rule.kind === "all") {
      rows.push({
        isin: DRIFT_ISIN_ALL,
        name: "Select All",
        target_percentage: roundPct2(Number(rule.target_percentage) || 0),
        margin: rule.margin ?? null,
        margin_enabled: rule.margin != null,
        isin_group_mode: "all",
        isin_group_id: null,
        isin_group_input_pct: null,
        layout_parent_id: rule.layout_parent_id ?? null,
        layout_parent_type: rule.layout_parent_type ?? null,
        ...(rule.operator ? { operator: rule.operator } : {}),
      });
      continue;
    }
    const code = ruleIsinCode(rule);
    if (!code) continue;
    rows.push({
      isin: code,
      ...(rule.name?.trim() ? { name: rule.name.trim() } : {}),
      target_percentage: roundPct2(Number(rule.target_percentage) || 0),
      margin: rule.margin ?? null,
      margin_enabled: rule.margin != null,
      isin_group_mode: rule.kind === "except" ? "except" : null,
      isin_group_id: null,
      isin_group_input_pct: null,
      layout_parent_id: rule.layout_parent_id ?? null,
      layout_parent_type: rule.layout_parent_type ?? null,
      ...(rule.operator ? { operator: rule.operator } : {}),
    });
  }
  return rows;
}

function hydrateMargin(row: Record<string, unknown>): number | null {
  if (row.margin_enabled === false || row.margin == null || row.margin === "") {
    return null;
  }
  return Number(row.margin);
}

function hydrateRulesFromApi(raw: unknown): DriftRule[] {
  if (!Array.isArray(raw)) return [];
  return raw.map((item) => {
    const row = (item ?? {}) as Record<string, unknown>;
    const layoutParentId = typeof row.layout_parent_id === "string" ? row.layout_parent_id.trim() : "";
    const layoutParentType = typeof row.layout_parent_type === "string"
      ? row.layout_parent_type.trim().toLowerCase()
      : "";
    const issuer = typeof row.issuer === "string" ? row.issuer.trim() : "";
    const issuerPct = row.issuer_percentage == null || row.issuer_percentage === ""
      ? null
      : Number(row.issuer_percentage);
    const parentFields = {
      ...(layoutParentId ? { layout_parent_id: layoutParentId } : {}),
      ...(layoutParentType === "macro" || layoutParentType === "bridge" || layoutParentType === "default"
        ? { layout_parent_type: layoutParentType as DriftLayout }
        : {}),
      ...(issuer ? { issuer } : {}),
      ...(issuerPct != null && !Number.isNaN(issuerPct) ? { issuer_percentage: issuerPct } : {}),
    };
    if (row.dimensions && typeof row.dimensions === "object") {
      return {
        dimensions: row.dimensions as DriftRule["dimensions"],
        target_percentage: Number(row.target_percentage ?? 0),
        margin: hydrateMargin(row),
        ...(typeof row.operator === "string" ? { operator: row.operator as DriftRule["operator"] } : {}),
        ...parentFields,
      };
    }
    const dimensions: DriftRule["dimensions"] = {};
    for (const [key] of DIMENSIONS) {
      const value = typeof row[key] === "string" ? row[key].trim() : "";
      if (value) dimensions[key] = value;
    }
    return {
      dimensions,
      target_percentage: Number(row.target_percentage ?? 0),
      margin: hydrateMargin(row),
      ...(typeof row.operator === "string" ? { operator: row.operator as DriftRule["operator"] } : {}),
      ...parentFields,
    };
  });
}

function hydrateIsinRulesFromApi(raw: unknown): DriftIsinRule[] {
  if (!Array.isArray(raw)) return [];
  const out: DriftIsinRule[] = [];
  // Legacy multi-select groups (each/total) flatten to singles.
  const legacyGroups = new Map<string, Array<Record<string, unknown>>>();

  for (const item of raw) {
    const row = (item ?? {}) as Record<string, unknown>;
    const isin = String(row.isin ?? "").trim();
    if (!isin) continue;
    const mode = String(row.isin_group_mode ?? "").trim().toLowerCase();
    const groupId = typeof row.isin_group_id === "string" ? row.isin_group_id.trim() : "";
    const pct = roundPct2(Number(row.target_percentage ?? 0));
    const margin = hydrateMargin(row);
    const operator = typeof row.operator === "string"
      ? (row.operator as DriftIsinRule["operator"])
      : undefined;
    const name = typeof row.name === "string" && row.name.trim() ? row.name.trim() : isin;
    const layoutParentId = typeof row.layout_parent_id === "string" ? row.layout_parent_id.trim() : "";
    const layoutParentType = typeof row.layout_parent_type === "string"
      ? row.layout_parent_type.trim().toLowerCase()
      : "";
    const layoutFields = {
      ...(layoutParentId ? { layout_parent_id: layoutParentId } : { layout_parent_id: null }),
      ...(layoutParentType === "macro" || layoutParentType === "bridge" || layoutParentType === "default"
        ? { layout_parent_type: layoutParentType as DriftLayout }
        : { layout_parent_type: null }),
    };

    if (isSelectAllCode(isin) || mode === "all") {
      out.push({
        kind: "all",
        isin: DRIFT_ISIN_ALL,
        name: "Select All",
        target_percentage: pct,
        margin,
        ...layoutFields,
        ...(operator ? { operator } : {}),
      });
      continue;
    }

    if (mode === "except") {
      out.push({
        kind: "except",
        isin,
        name,
        target_percentage: pct,
        margin,
        ...layoutFields,
        ...(operator ? { operator } : {}),
      });
      continue;
    }

    if (groupId && (mode === "each" || mode === "total" || mode === "")) {
      const bucket = legacyGroups.get(groupId) ?? [];
      bucket.push(row);
      legacyGroups.set(groupId, bucket);
      continue;
    }

    out.push({
      kind: "single",
      isin,
      name,
      target_percentage: pct,
      margin,
      ...layoutFields,
      ...(operator ? { operator } : {}),
    });
  }

  for (const rows of legacyGroups.values()) {
    for (const row of rows) {
      const isin = String(row.isin ?? "").trim();
      if (!isin || isSelectAllCode(isin)) continue;
      const name = typeof row.name === "string" && row.name.trim() ? row.name.trim() : isin;
      const operator = typeof row.operator === "string"
        ? (row.operator as DriftIsinRule["operator"])
        : undefined;
      const layoutParentId = typeof row.layout_parent_id === "string" ? row.layout_parent_id.trim() : "";
      const layoutParentType = typeof row.layout_parent_type === "string"
        ? row.layout_parent_type.trim().toLowerCase()
        : "";
      out.push({
        kind: "single",
        isin,
        name,
        target_percentage: roundPct2(Number(row.target_percentage ?? 0)),
        margin: hydrateMargin(row),
        ...(layoutParentId ? { layout_parent_id: layoutParentId } : { layout_parent_id: null }),
        ...(layoutParentType === "macro" || layoutParentType === "bridge" || layoutParentType === "default"
          ? { layout_parent_type: layoutParentType as DriftLayout }
          : { layout_parent_type: null }),
        ...(operator ? { operator } : {}),
      });
    }
  }

  return out;
}

function MarginFields({
  margin,
  readOnly,
  onChange,
}: {
  margin: number | null | undefined;
  readOnly?: boolean;
  onChange: (margin: number | null) => void;
}) {
  const enabled = margin != null;
  return (
    <div className="flex flex-wrap items-center gap-3 sm:col-span-2">
      <label className="inline-flex items-center gap-2 text-sm">
        <Checkbox
          disabled={readOnly}
          checked={enabled}
          onCheckedChange={(value) => onChange(value === true ? 0 : null)}
        />
        <span>Use margin (±%)</span>
      </label>
      {enabled ? (
        <div className="relative w-[120px]">
          <Input
            disabled={readOnly}
            type="number"
            min="0"
            max="100"
            step="0.01"
            className="h-9 w-full bg-background pr-7"
            value={margin === 0 || margin ? String(margin) : ""}
            onChange={(event) => {
              const raw = event.target.value;
              onChange(raw === "" ? 0 : Number(raw));
            }}
            placeholder="Margin"
          />
          <span className="pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">%</span>
        </div>
      ) : null}
    </div>
  );
}

function IsinAllocationSummary({
  rules,
  data,
}: {
  rules: DriftIsinRule[];
  data: DriftSettingsData;
}) {
  const currentLayout = normalizeDriftLayout(data.defaultLayout);
  const layoutRules = React.useMemo(
    () => rules.filter((rule) => isinMatchesCurrentLayout(rule, currentLayout)),
    [rules, currentLayout],
  );
  const labels = React.useMemo(() => isinLabelLookup(data), [data]);
  const labeledRules = React.useMemo(() => {
    const rows: Array<{ key: string; label: string; pct: number; color: string }> = [];
    let colorIndex = 0;
    layoutRules.forEach((rule, ruleIndex) => {
      if (rule.kind === "all") {
        rows.push({
          key: `all-${ruleIndex}`,
          label: "Select All",
          pct: roundPct2(Number(rule.target_percentage) || 0),
          color: colorForDriftLabel("Select All", colorIndex),
        });
        colorIndex += 1;
        return;
      }
      const code = ruleIsinCode(rule);
      if (!code) return;
      const label = rule.name || labels.get(code) || code;
      rows.push({
        key: `${rule.kind}:${code}:${ruleIndex}`,
        label: rule.kind === "except" ? `Except: ${label}` : label,
        pct: roundPct2(Number(rule.target_percentage) || 0),
        color: colorForDriftLabel(label, colorIndex),
      });
      colorIndex += 1;
    });
    return rows;
  }, [labels, layoutRules]);

  return (
    <section className="space-y-3 border-t pt-5">
      <div className="flex items-baseline gap-2">
        <h3 className="font-semibold">Allocate by ISIN</h3>
        <span className="text-muted-foreground text-xs">
          {labeledRules.length} rule{labeledRules.length === 1 ? "" : "s"}
        </span>
      </div>
      <div className="flex items-stretch gap-3">
        <HoverCard openDelay={120} closeDelay={80}>
          <HoverCardTrigger asChild>
            <div className="flex w-[148px] shrink-0 cursor-default flex-col items-center justify-center rounded-xl border bg-card p-3 text-center">
              <Donut
                slices={labeledRules.map((item) => ({
                  value: item.pct,
                  color: item.color,
                }))}
              />
              <p className="mt-2 font-medium text-[11px] uppercase tracking-wide text-muted-foreground">
                Total
              </p>
            </div>
          </HoverCardTrigger>
          <HoverCardContent align="start" className="w-72 p-3">
            {labeledRules.length ? (
              <ul className="max-h-64 space-y-1.5 overflow-y-auto">
                {labeledRules.map((item) => (
                  <li key={`hover-${item.key}`} className="flex min-w-0 items-center gap-2 text-xs">
                    <span
                      className="size-2 shrink-0 rounded-full"
                      style={{ backgroundColor: item.color }}
                    />
                    <span className="min-w-0 flex-1 truncate" title={item.label}>{item.label}</span>
                    <span className="shrink-0 font-medium tabular-nums">{item.pct}%</span>
                  </li>
                ))}
              </ul>
            ) : (
              <p className="text-muted-foreground text-xs">No ISIN rules</p>
            )}
          </HoverCardContent>
        </HoverCard>

        <div className="min-w-0 flex-1 overflow-x-auto pb-1">
          <div className="flex w-max gap-3">
            {labeledRules.map((item) => (
              <div
                key={`isin-${item.key}`}
                className="flex h-[148px] w-[148px] shrink-0 flex-col items-center justify-center rounded-xl border bg-card p-3 text-center"
              >
                <Donut slices={[{ value: item.pct, color: item.color }]} />
                <HoverCard openDelay={150} closeDelay={60}>
                  <HoverCardTrigger asChild>
                    <p className="mt-2 w-full cursor-default truncate font-medium text-[11px] uppercase tracking-wide text-muted-foreground">
                      {item.label}
                    </p>
                  </HoverCardTrigger>
                  <HoverCardContent side="top" className="w-auto max-w-xs px-3 py-1.5 text-xs">
                    {item.label}
                  </HoverCardContent>
                </HoverCard>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}

function RulesOverview({ rules }: { rules: DriftRule[] }) {
  if (!rules.length) {
    return (
      <div className="flex flex-col items-center gap-2 rounded-xl border border-dashed bg-muted/20 py-12 text-center">
        <ListChecks className="size-6 text-muted-foreground" />
        <p className="text-muted-foreground text-sm">No target allocations defined.</p>
      </div>
    );
  }
  const total = rules.reduce((sum, rule) => sum + (Number(rule.target_percentage) || 0), 0);
  return (
    <div className="space-y-3">
      <div className="flex flex-wrap items-center gap-2 text-muted-foreground text-xs">
        <Badge variant="secondary" className="tabular-nums">
          {rules.length} rule{rules.length === 1 ? "" : "s"}
        </Badge>
        <span>Combined target {Number(total.toFixed(2))}%</span>
      </div>
      {rules.map((rule, index) => {
        const color = colorForDriftLabel(
          cleanLabel(Object.values(rule.dimensions).filter(Boolean).join(" / ")),
          index,
        );
        const pct = Number(rule.target_percentage) || 0;
        const details = [
          rule.layout_parent_id
            ? `${rule.layout_parent_type === "bridge" ? "Bridge" : "Macro"}: ${cleanLabel(rule.layout_parent_id)}`
            : null,
          rule.issuer
            ? `Issuer: ${rule.issuer}${rule.issuer_percentage != null ? ` (${rule.issuer_percentage}%)` : ""}`
            : null,
          ...Object.entries(rule.dimensions).map(
            ([key, value]) => `${DIMENSIONS.find(([dimension]) => dimension === key)?.[1] ?? key}: ${cleanLabel(value)}`,
          ),
        ].filter(Boolean);
        return (
          <div key={index} className="space-y-3 rounded-xl border bg-card p-4 transition-shadow hover:shadow-sm">
            <div className="flex items-start gap-3">
              <span
                className="mt-1 h-8 w-1 shrink-0 rounded-full"
                style={{ backgroundColor: color }}
              />
              <div className="min-w-0 flex-1 space-y-1.5">
                <p className="font-medium text-sm">Rule {index + 1}</p>
                <div className="flex flex-wrap gap-1.5">
                  {details.length ? (
                    details.map((detail) => (
                      <Badge key={detail} variant="secondary" className="font-normal">
                        {detail}
                      </Badge>
                    ))
                  ) : (
                    <span className="text-muted-foreground text-xs">No filters, applies to everything</span>
                  )}
                </div>
              </div>
              <span className="font-semibold text-xl tabular-nums">{rule.target_percentage}%</span>
            </div>
            <div className="h-1.5 overflow-hidden rounded-full bg-muted">
              <div className="h-full rounded-full" style={{ width: `${Math.min(100, Math.max(0, pct))}%`, backgroundColor: color }} />
            </div>
          </div>
        );
      })}
    </div>
  );
}

function actionBadgeClass(action: string): string {
  if (action === "add") return "bg-blue-500/15 text-blue-700 dark:text-blue-300";
  if (action === "reduce") return "bg-amber-500/15 text-amber-700 dark:text-amber-300";
  return "bg-emerald-500/15 text-emerald-700 dark:text-emerald-300";
}

function driftTextClass(drift: number): string {
  if (drift > 0) return "text-amber-600 dark:text-amber-400";
  if (drift < 0) return "text-blue-600 dark:text-blue-400";
  return "text-emerald-600 dark:text-emerald-400";
}

function AllocationRows({
  title,
  rows,
  currency,
}: {
  title: string;
  rows: DriftAllocationComparison[];
  currency: string;
}) {
  if (!rows.length) return null;
  return (
    <section className="overflow-hidden rounded-xl border bg-card">
      <div className="flex items-center gap-2 border-b px-4 py-3">
        <h3 className="font-semibold">{title}</h3>
        <Badge variant="secondary" className="tabular-nums">{rows.length}</Badge>
      </div>
      <div className="overflow-x-auto">
        <table className="w-full min-w-[780px] text-left text-sm">
          <thead className="bg-muted/40 text-muted-foreground text-xs uppercase tracking-wide">
            <tr>
              {["Target", "Ideal vs Actual", "Drift", "Actual Value", "Target Value", "Action"].map((heading) => (
                <th key={heading} className="px-4 py-2.5 font-medium">{heading}</th>
              ))}
            </tr>
          </thead>
          <tbody className="divide-y">
            {rows.map((row, index) => {
              const ideal = Math.min(100, Math.max(0, Number(row.ideal_pct) || 0));
              const actual = Math.min(100, Math.max(0, Number(row.actual_pct) || 0));
              return (
                <tr key={`${row.rule_label}-${index}`} className="transition-colors hover:bg-muted/30">
                  <td className="max-w-72 px-4 py-3 font-medium">{row.rule_label}</td>
                  <td className="min-w-48 px-4 py-3">
                    <div className="space-y-1.5">
                      <div className="relative h-1.5 overflow-hidden rounded-full bg-muted">
                        <div className="h-full rounded-full bg-primary" style={{ width: `${actual}%` }} />
                        <div
                          className="absolute inset-y-[-2px] w-0.5 rounded-full bg-foreground/70"
                          style={{ left: `calc(${ideal}% - 1px)` }}
                          title={`Ideal ${row.ideal_pct}%`}
                        />
                      </div>
                      <p className="text-muted-foreground text-xs tabular-nums">
                        Actual {row.actual_pct}% · Ideal {row.ideal_pct}%
                      </p>
                    </div>
                  </td>
                  <td className={cn("px-4 py-3 font-semibold tabular-nums", driftTextClass(row.drift))}>
                    {row.drift > 0 ? "+" : ""}{row.drift}%
                  </td>
                  <td className="px-4 py-3 tabular-nums">{formatQuantity(Number(row.actual_value))} {currency}</td>
                  <td className="px-4 py-3 tabular-nums">{formatQuantity(Number(row.target_value))} {currency}</td>
                  <td className="px-4 py-3">
                    <span className={cn("rounded-full px-2.5 py-1 font-medium text-xs", actionBadgeClass(row.reallocate_action))}>
                      {row.reallocate_action === "add" ? "Add" : row.reallocate_action === "reduce" ? "Reduce" : "On target"}
                    </span>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </section>
  );
}

function ActualVsIdeal({ data }: { data: DriftSettingsData }) {
  if (!data.selectedCustomerId) {
    return <p className="rounded-xl border border-dashed bg-muted/20 p-6 text-center text-muted-foreground text-sm">Select a customer above to see their Actual vs Ideal Allocation report.</p>;
  }
  const report = data.driftReportData;
  if (report?.error) {
    return <p className="rounded-xl border border-destructive/30 bg-destructive/5 p-4 text-destructive text-sm">Report could not be loaded: {report.error}</p>;
  }
  const comparison = report?.comparison ?? [];
  const isinComparison = report?.isinComparison ?? [];
  if (!comparison.length && !isinComparison.length) {
    return <p className="rounded-xl border border-dashed bg-muted/20 p-6 text-center text-muted-foreground text-sm">No target rules or portfolio data. Define targets and save to generate the report.</p>;
  }
  const allRows = [...comparison, ...isinComparison];
  const needsAction = allRows.filter((row) => row.reallocate_action === "add" || row.reallocate_action === "reduce").length;

  return (
    <div className="space-y-4">
      <div className="grid gap-3 sm:grid-cols-3">
        <div className="rounded-xl border bg-card p-4">
          <p className="text-muted-foreground text-xs uppercase tracking-wide">Total portfolio value</p>
          <p className="mt-1 font-semibold text-xl tabular-nums">{formatQuantity(Number(report?.totalPortfolioValue ?? 0))} {report?.reportingCode ?? ""}</p>
        </div>
        <div className="rounded-xl border bg-card p-4">
          <p className="text-muted-foreground text-xs uppercase tracking-wide">Targets tracked</p>
          <p className="mt-1 font-semibold text-xl tabular-nums">{allRows.length}</p>
        </div>
        <div className="rounded-xl border bg-card p-4">
          <p className="text-muted-foreground text-xs uppercase tracking-wide">Need rebalancing</p>
          <p className={cn("mt-1 font-semibold text-xl tabular-nums", needsAction > 0 && "text-amber-600 dark:text-amber-400")}>{needsAction}</p>
        </div>
      </div>
      <AllocationRows title="Target allocation" rows={comparison} currency={report?.reportingCode ?? ""} />
      <AllocationRows title="ISIN allocation" rows={isinComparison} currency={report?.reportingCode ?? ""} />
    </div>
  );
}

export function SettingsPanelDrift({ tenant }: { tenant: string }) {
  const { layout: tenantLayout } = useDefaultLayout(tenant);
  const searchParams = useSearchParams();
  const initialCustomerId = React.useMemo(() => {
    const raw = searchParams.get("customer_id");
    if (!raw) return null;
    const id = Number(raw);
    return Number.isInteger(id) && id > 0 ? id : null;
  }, [searchParams]);
  const [data, setData] = React.useState<DriftSettingsData | null>(null);
  const [rules, setRules] = React.useState<DriftRule[]>([]);
  const [isinRules, setIsinRules] = React.useState<DriftIsinRule[]>([]);
  const [advanced, setAdvanced] = React.useState<DriftRule[]>([]);
  const [advancedIsin, setAdvancedIsin] = React.useState<DriftIsinRule[]>([]);
  const [activeTab, setActiveTab] = React.useState("charts");
  const [customerSearch, setCustomerSearch] = React.useState("");
  const [customerDropdownOpen, setCustomerDropdownOpen] = React.useState(false);
  const [loading, setLoading] = React.useState(true);
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState<string | null>(null);
  const [inheritEnabled, setInheritEnabled] = React.useState(false);
  const [inheritGroupIds, setInheritGroupIds] = React.useState<string[]>([]);
  const [inheritPickerOpen, setInheritPickerOpen] = React.useState(false);
  const [pendingInheritSelectAll, setPendingInheritSelectAll] = React.useState<{
    groupIds: string[];
    previousGroupIds: string[];
    masterRules: DriftRule[];
    masterIsin: DriftIsinRule[];
    prevRuleSigs: Set<string>;
    prevIsinSigs: Set<string>;
    customerPct: number;
    masterPct: number;
  } | null>(null);
  const displacedCustomerAllRef = React.useRef<DriftIsinRule | null>(null);
  const resolvingInheritSelectAllRef = React.useRef(false);
  const inheritedRuleSigsRef = React.useRef<Set<string>>(new Set());
  const inheritedIsinSigsRef = React.useRef<Set<string>>(new Set());
  const [masterGroupName, setMasterGroupName] = React.useState("");
  const [masterGroups, setMasterGroups] = React.useState<DriftMasterGroup[]>([]);
  const [masterSelectedId, setMasterSelectedId] = React.useState("");
  const [masterCreateMode, setMasterCreateMode] = React.useState(true);
  const [masterDraftRules, setMasterDraftRules] = React.useState<DriftRule[]>([EMPTY_RULE()]);
  const [masterDraftIsinRules, setMasterDraftIsinRules] = React.useState<DriftIsinRule[]>([EMPTY_ISIN()]);
  const [masterSheetOpen, setMasterSheetOpen] = React.useState(false);
  const [headerActionsEl, setHeaderActionsEl] = React.useState<HTMLElement | null>(null);
  const previewLayoutForCatalog = normalizeDriftLayout(
    data?.defaultLayout || tenantLayout,
  );
  const { catalog: layoutCatalog } = useAssetClassFilterCatalog(previewLayoutForCatalog, [], {
    enabled: previewLayoutForCatalog === "macro" || previewLayoutForCatalog === "bridge",
  });
  const catalogParents = React.useMemo(
    () => catalogParentsFromFilter(layoutCatalog.options ?? []),
    [layoutCatalog],
  );

  const applyData = React.useCallback((result: DriftSettingsData) => {
    setData(result);
    setRules(hydrateRulesFromApi(result.rules ?? []));
    setIsinRules(enrichIsinRules(hydrateIsinRulesFromApi(result.isinRules ?? []), result));
    setAdvanced(hydrateRulesFromApi(result.advancedRules ?? []));
    setAdvancedIsin(enrichIsinRules(hydrateIsinRulesFromApi(result.advancedIsinRules ?? []), result));
    setInheritEnabled(false);
    setInheritGroupIds([]);
    setInheritPickerOpen(false);
    inheritedRuleSigsRef.current = new Set();
    inheritedIsinSigsRef.current = new Set();
    const groups = result.masterGroups ?? [];
    setMasterGroups(groups);
    const layout = normalizeDriftLayout(result.defaultLayout || tenantLayout);
    const matching = masterGroupsForLayout(groups, layout);
    if (matching.length === 0) {
      setMasterCreateMode(true);
      setMasterSelectedId("");
      setMasterDraftRules([emptyRuleForLayout(layout)]);
      setMasterDraftIsinRules([EMPTY_ISIN()]);
      setMasterSheetOpen(true);
    } else {
      setMasterCreateMode(false);
    }
    if (result.selectedCustomerId) {
      setCustomerSearch(result.customerList[String(result.selectedCustomerId)] ?? "");
    }
  }, [tenantLayout]);

  React.useEffect(() => {
    let cancelled = false;
    void loadDriftSettings(tenant, initialCustomerId).then((result) => {
      if (cancelled) return;
      applyData(result);
    }).catch((loadError) => { if (!cancelled) setError(errorMessageOr(loadError, "Drift Settings could not be loaded.")); })
      .finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [applyData, tenant, initialCustomerId]);

  async function selectCustomer(customerId: number | null) {
    setLoading(true);
    setError(null);
    try {
      const result = await loadDriftSettings(tenant, customerId);
      applyData(result);
      if (!customerId) setCustomerSearch("");
      setCustomerDropdownOpen(false);
    } catch (loadError) {
      setError(errorMessageOr(loadError, "Customer allocation could not be loaded."));
    } finally {
      setLoading(false);
    }
  }

  async function saveStandard() {
    if (!data) return;
    const editorData = withDriftLayout(data, tenantLayout, catalogParents);
    const errors = [
      ...validateDimensionRules(rules, editorData),
      ...validateIsinRules(isinRules, normalizeDriftLayout(editorData.defaultLayout)),
    ];
    if (errors.length) {
      toast.error(errors[0], errors.length > 1 ? { description: errors.slice(1).join("\n") } : undefined);
      return;
    }
    setBusy(true);
    try {
      const result = await saveDriftSettings(tenant, {
        operation: "standard",
        rules: apiRules(rules),
        isinRules: apiIsinRules(isinRules),
        customerId: data.selectedCustomerId ?? undefined,
      });
      applyData(result);
      toast.success("Drift Settings saved");
    } catch (saveError) { toastApiError(saveError, "Drift Settings could not be saved."); }
    finally { setBusy(false); }
  }

  async function saveAdvanced() {
    if (!data) return;
    const editorData = withDriftLayout(data, tenantLayout, catalogParents);
    const errors = [
      ...validateDimensionRules(advanced, editorData),
      ...validateIsinRules(advancedIsin, normalizeDriftLayout(editorData.defaultLayout)),
    ];
    if (errors.length) {
      toast.error(errors[0], errors.length > 1 ? { description: errors.slice(1).join("\n") } : undefined);
      return;
    }
    setBusy(true);
    try {
      const result = await saveDriftSettings(tenant, {
        operation: "advanced",
        rules: apiRules(advanced),
        isinRules: apiIsinRules(advancedIsin),
        customerId: data?.selectedCustomerId ?? undefined,
      });
      applyData(result);
      toast.success("Advanced Drift Settings saved");
    } catch (saveError) { toastApiError(saveError, "Advanced settings could not be saved."); }
    finally { setBusy(false); }
  }

  async function saveMasterGroup() {
    const layout = normalizeDriftLayout(data?.defaultLayout || tenantLayout);
    if (layout === "bridge") {
      toast.error(MASTER_GROUP_BRIDGE_DENIED);
      return;
    }
    const name = masterGroupName.trim();
    if (!name) {
      toast.error("Enter a master group name.");
      return;
    }
    if (data) {
      const errors = [
        ...validateDimensionRules(masterDraftRules, withDriftLayout(data, tenantLayout, catalogParents)),
        ...validateIsinRules(masterDraftIsinRules, layout),
      ];
      if (errors.length) {
        toast.error(errors[0], errors.length > 1 ? { description: errors.slice(1).join("\n") } : undefined);
        return;
      }
    }
    setBusy(true);
    try {
      const result = await saveDriftSettings(tenant, {
        operation: "master_group_save",
        name,
        layout,
        rules: apiRules(masterDraftRules),
        isinRules: apiIsinRules(masterDraftIsinRules),
      }) as DriftSettingsData & {
        masterGroups?: DriftMasterGroup[];
        masterGroup?: { id: number; name: string };
      };
      const groups = result.masterGroups ?? [];
      setMasterGroups(groups);
      setMasterGroupName("");
      setMasterCreateMode(false);
      const savedId = result.masterGroup?.id;
      if (savedId) {
        setMasterSelectedId(String(savedId));
      }
      toast.success("Master group saved");
    } catch (saveError) {
      toastApiError(saveError, "Master group could not be saved.");
    } finally {
      setBusy(false);
    }
  }

  async function deleteMasterGroup(groupId: number) {
    setBusy(true);
    try {
      const result = await saveDriftSettings(tenant, {
        operation: "master_group_delete",
        masterGroupId: groupId,
      }) as DriftSettingsData & { masterGroups?: DriftMasterGroup[] };
      const groups = result.masterGroups ?? [];
      setMasterGroups(groups);
      setInheritGroupIds((prev) => {
        const next = prev.filter((id) => id !== String(groupId));
        if (next.length === 0) setInheritEnabled(false);
        return next;
      });
      if (masterSelectedId === String(groupId)) {
        setMasterSelectedId("");
        setMasterDraftRules([]);
        setMasterDraftIsinRules([]);
      }
      const layout = normalizeDriftLayout(data?.defaultLayout || tenantLayout);
      if (masterGroupsForLayout(groups, layout).length === 0) {
        setMasterCreateMode(true);
        setMasterDraftRules([emptyRuleForLayout(layout)]);
        setMasterDraftIsinRules([EMPTY_ISIN()]);
        setMasterSheetOpen(true);
      }
      toast.success("Master group deleted");
    } catch (saveError) {
      toastApiError(saveError, "Master group could not be deleted.");
    } finally {
      setBusy(false);
    }
  }

  async function fetchMasterGroup(groupId: number) {
    const result = await saveDriftSettings(tenant, {
      operation: "master_group_get",
      masterGroupId: groupId,
    }) as {
      masterGroup?: { rules?: unknown; isin_rules?: unknown; name?: string; layout?: string };
      masterGroups?: DriftMasterGroup[];
    };
    const group = result.masterGroup;
    if (!group) throw new Error("Master group not found.");
    if (result.masterGroups) setMasterGroups(result.masterGroups);
    const catalog = data ?? ({} as DriftSettingsData);
    const groupLayout = normalizeDriftLayout(
      group.layout || catalog.defaultLayout || tenantLayout,
    );
    return {
      rules: stampDimensionRulesForLayout(hydrateRulesFromApi(group.rules), groupLayout),
      isinRules: stampIsinRulesForLayout(
        enrichIsinRules(hydrateIsinRulesFromApi(group.isin_rules), catalog),
        groupLayout,
      ),
      layout: groupLayout,
    };
  }

  async function loadMasterGroupForView(groupId: number) {
    setBusy(true);
    try {
      const loaded = await fetchMasterGroup(groupId);
      setMasterDraftRules(loaded.rules);
      setMasterDraftIsinRules(loaded.isinRules);
    } catch (loadError) {
      toastApiError(loadError, "Master group could not be loaded.");
    } finally {
      setBusy(false);
    }
  }

  function commitInheritedIsin(
    base: DriftIsinRule[],
    masterIsin: DriftIsinRule[],
    preferAll: "keep-base" | "keep-incoming",
  ) {
    const layout = normalizeDriftLayout(data?.defaultLayout || tenantLayout);
    const stampedMaster = stampIsinRulesForLayout(masterIsin, layout);
    const { next, appended, displacedAll } = appendInheritedIsinRules(
      base,
      stampedMaster,
      preferAll,
    );
    if (displacedAll) {
      displacedCustomerAllRef.current = displacedAll;
    }
    inheritedIsinSigsRef.current = new Set(appended.map(driftIsinSignature));
    setIsinRules(next);
  }

  function applyInheritedChartsAndIsin(
    masterRules: DriftRule[],
    masterIsin: DriftIsinRule[],
    prevRuleSigs: Set<string>,
    prevIsinSigs: Set<string>,
    preferAll: "keep-base" | "keep-incoming",
  ) {
    const layout = normalizeDriftLayout(data?.defaultLayout || tenantLayout);
    const stampedRules = stampDimensionRulesForLayout(masterRules, layout);
    inheritedRuleSigsRef.current = new Set(stampedRules.map(driftRuleSignature));
    setRules((prev) => [
      ...prev.filter((rule) => !prevRuleSigs.has(driftRuleSignature(rule))),
      ...stampedRules,
    ]);
    const base = isinRules.filter((rule) => !prevIsinSigs.has(driftIsinSignature(rule)));
    commitInheritedIsin(base, masterIsin, preferAll);
  }

  async function applyMasterGroups(groupIds: string[], previousGroupIds: string[] = inheritGroupIds) {
    if (groupIds.length === 0) {
      const prevRuleSigs = inheritedRuleSigsRef.current;
      const prevIsinSigs = inheritedIsinSigsRef.current;
      const displaced = displacedCustomerAllRef.current;
      inheritedRuleSigsRef.current = new Set();
      inheritedIsinSigsRef.current = new Set();
      displacedCustomerAllRef.current = null;
      setRules((prev) => prev.filter((rule) => !prevRuleSigs.has(driftRuleSignature(rule))));
      setIsinRules((prev) => {
        const withoutInherited = prev.filter((rule) => !prevIsinSigs.has(driftIsinSignature(rule)));
        if (displaced && !withoutInherited.some((rule) => rule.kind === "all")) {
          return [displaced, ...withoutInherited];
        }
        return withoutInherited;
      });
      return;
    }
    setBusy(true);
    try {
      const loaded = await Promise.all(groupIds.map((id) => fetchMasterGroup(Number(id))));
      const masterRules = loaded.flatMap((item) => item.rules);
      const masterIsin = loaded.flatMap((item) => item.isinRules);
      const prevRuleSigs = inheritedRuleSigsRef.current;
      const prevIsinSigs = inheritedIsinSigsRef.current;

      const base = isinRules.filter((rule) => !prevIsinSigs.has(driftIsinSignature(rule)));
      const customerAll = base.find((rule) => rule.kind === "all");
      const masterAll = masterIsin.find((rule) => rule.kind === "all");
      if (customerAll && masterAll) {
        setPendingInheritSelectAll({
          groupIds,
          previousGroupIds,
          masterRules,
          masterIsin,
          prevRuleSigs,
          prevIsinSigs,
          customerPct: roundPct2(Number(customerAll.target_percentage) || 0),
          masterPct: roundPct2(Number(masterAll.target_percentage) || 0),
        });
        return;
      }

      applyInheritedChartsAndIsin(masterRules, masterIsin, prevRuleSigs, prevIsinSigs, "keep-base");
      toast.success(
        groupIds.length === 1
          ? "Master group applied to form — save to persist for this customer"
          : `${groupIds.length} master groups applied to form — save to persist for this customer`,
      );
    } catch (loadError) {
      toastApiError(loadError, "Master group could not be loaded.");
      setInheritGroupIds(previousGroupIds);
    } finally {
      setBusy(false);
    }
  }

  function resolveInheritSelectAll(choice: "customer" | "master") {
    const pending = pendingInheritSelectAll;
    if (!pending) return;
    resolvingInheritSelectAllRef.current = true;
    applyInheritedChartsAndIsin(
      pending.masterRules,
      pending.masterIsin,
      pending.prevRuleSigs,
      pending.prevIsinSigs,
      choice === "master" ? "keep-incoming" : "keep-base",
    );
    setPendingInheritSelectAll(null);
    toast.success(
      choice === "master"
        ? "Using master Select All — your Select All was removed. Save to persist."
        : "Keeping your Select All — master Select All was skipped. Save to persist.",
    );
    queueMicrotask(() => {
      resolvingInheritSelectAllRef.current = false;
    });
  }

  function cancelInheritSelectAll() {
    if (resolvingInheritSelectAllRef.current) return;
    const pending = pendingInheritSelectAll;
    if (!pending) return;
    setInheritGroupIds(pending.previousGroupIds);
    setPendingInheritSelectAll(null);
  }

  function toggleInheritGroup(groupId: string, checked: boolean) {
    const previousGroupIds = inheritGroupIds;
    const next = checked
      ? [...inheritGroupIds, groupId]
      : inheritGroupIds.filter((id) => id !== groupId);
    setInheritGroupIds(next);
    void applyMasterGroups(next, previousGroupIds);
  }

  function disableInherit() {
    const ruleSigs = inheritedRuleSigsRef.current;
    const isinSigs = inheritedIsinSigsRef.current;
    const displaced = displacedCustomerAllRef.current;
    setInheritEnabled(false);
    setInheritGroupIds([]);
    setInheritPickerOpen(false);
    setPendingInheritSelectAll(null);
    inheritedRuleSigsRef.current = new Set();
    inheritedIsinSigsRef.current = new Set();
    displacedCustomerAllRef.current = null;
    setRules((prev) => prev.filter((rule) => !ruleSigs.has(driftRuleSignature(rule))));
    setIsinRules((prev) => {
      const withoutInherited = prev.filter((rule) => !isinSigs.has(driftIsinSignature(rule)));
      if (displaced && !withoutInherited.some((rule) => rule.kind === "all")) {
        return [displaced, ...withoutInherited];
      }
      return withoutInherited;
    });
  }

  function startMasterCreate() {
    const layout = normalizeDriftLayout(data?.defaultLayout || tenantLayout);
    setMasterCreateMode(true);
    setMasterSelectedId("");
    setMasterGroupName("");
    setMasterDraftRules([emptyRuleForLayout(layout)]);
    setMasterDraftIsinRules([EMPTY_ISIN()]);
    setMasterSheetOpen(true);
  }

  React.useEffect(() => {
    // The portal target only exists in the DOM after mount.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setHeaderActionsEl(document.getElementById("settings-header-actions"));
  }, []);

  if (loading) return <p className="py-12 text-center text-muted-foreground text-sm">Loading Drift Settings…</p>;
  if (error || !data) return <p className="rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-destructive text-sm">{error}</p>;

  const editorData = withDriftLayout(data, tenantLayout, catalogParents);
  const customerOptions = Object.entries(editorData.customerList ?? {});
  const showCustomerPicker = customerOptions.length > 0;
  const filteredCustomers = customerOptions.filter(([, name]) =>
    name.toLowerCase().includes(customerSearch.trim().toLowerCase()),
  );
  const showStandardRules = activeTab === "charts" || activeTab === "overview";
  const showReportTab = showCustomerPicker && Boolean(editorData.selectedCustomerId);
  const canManageMasterGroups = Boolean(editorData.canManageMasterGroups);
  const previewLayout = normalizeDriftLayout(editorData.defaultLayout || tenantLayout);
  const canCreateMasterForLayout = previewLayout !== "bridge";
  const layoutMasterGroups = masterGroupsForLayout(masterGroups, previewLayout);
  const showInherit = canManageMasterGroups && !editorData.isReadOnly && canCreateMasterForLayout;
  const showMasterCreate = canManageMasterGroups && canCreateMasterForLayout && (layoutMasterGroups.length === 0 || masterCreateMode);
  const showMasterPicker = canManageMasterGroups && canCreateMasterForLayout && layoutMasterGroups.length > 0 && !masterCreateMode;
  const masterEditorData: DriftSettingsData = {
    ...editorData,
    dimensions: editorData.masterDimensions ?? editorData.dimensions,
    isinList: editorData.masterIsinList ?? editorData.isinList,
    assetTypeMap: editorData.masterAssetTypeMap ?? editorData.assetTypeMap,
  };

  const masterSheetDescription =
    previewLayout === "bridge"
      ? "Bridge rules are created on the customer side"
      : previewLayout === "macro"
        ? "Reusable Macro-layout templates for this tenant (filtered by current Layout)"
        : "Reusable Default-layout templates for this tenant (filtered by current Layout)";

  return (
    <div className="space-y-5">
      {canManageMasterGroups && headerActionsEl
        ? createPortal(
            <Button
              type="button"
              variant="outline"
              size="sm"
              className="gap-1.5"
              onClick={() => setMasterSheetOpen(true)}
            >
              <Layers className="size-3.5" />
              Master Rules
            </Button>,
            headerActionsEl,
          )
        : null}

      {canManageMasterGroups ? (
        <Sheet open={masterSheetOpen} onOpenChange={setMasterSheetOpen}>
          <SheetContent
            side="right"
            showCloseButton={false}
            className="flex h-dvh w-full flex-col gap-0 overflow-hidden bg-muted/30 p-0 data-[side=right]:sm:max-w-3xl"
          >
            <div className="shrink-0 space-y-2 border-b border-border/60 px-4 pt-4 pb-3">
              <p className="text-muted-foreground text-[10px] font-semibold tracking-widest uppercase">
                Drift Settings
              </p>
              <div className="flex items-center justify-between gap-2">
                <SheetTitle className="flex min-w-0 items-center gap-2 text-left text-base font-semibold tracking-tight">
                  <span className="flex size-8 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground shadow-md shadow-primary/25">
                    <Layers className="size-3.5" />
                  </span>
                  <span className="truncate">Master Rule Groups</span>
                </SheetTitle>
                <Button
                  type="button"
                  variant="ghost"
                  size="icon-sm"
                  className="rounded-lg"
                  onClick={() => setMasterSheetOpen(false)}
                  aria-label="Close master rules"
                >
                  <X className="size-4" />
                </Button>
              </div>
              <SheetDescription className="text-muted-foreground text-xs leading-relaxed">
                {masterSheetDescription}
              </SheetDescription>
            </div>

            <div className="min-h-0 flex-1 space-y-4 overflow-y-auto px-4 py-4">
              {!canCreateMasterForLayout ? (
                <p className="rounded-md border border-border/80 bg-muted/40 px-3 py-2 text-muted-foreground text-sm">
                  {MASTER_GROUP_BRIDGE_DENIED}
                </p>
              ) : null}
              {showMasterPicker && (
                <div className="flex flex-wrap items-center gap-2">
                  <Select
                    value={masterSelectedId || "__select__"}
                    onValueChange={(value) => {
                      if (value === "__select__") {
                        const layout = normalizeDriftLayout(editorData.defaultLayout || tenantLayout);
                        setMasterSelectedId("");
                        setMasterDraftRules([emptyRuleForLayout(layout)]);
                        setMasterDraftIsinRules([EMPTY_ISIN()]);
                        return;
                      }
                      setMasterSelectedId(value);
                      setMasterCreateMode(false);
                      void loadMasterGroupForView(Number(value));
                    }}
                  >
                    <SelectTrigger className="w-[240px] bg-background">
                      <SelectValue placeholder="Select" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="__select__">Select</SelectItem>
                      {layoutMasterGroups.map((group) => (
                        <SelectItem key={group.id} value={String(group.id)}>
                          {group.name}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                  <Button type="button" size="sm" variant="outline" className="gap-1.5" disabled={busy} onClick={startMasterCreate}>
                    <Plus className="size-3.5" />
                    Create
                  </Button>
                  {masterSelectedId ? (
                    <Button
                      type="button"
                      size="sm"
                      variant="ghost"
                      className="text-destructive"
                      disabled={busy}
                      onClick={() => void deleteMasterGroup(Number(masterSelectedId))}
                    >
                      <Trash2 className="size-3.5" />
                    </Button>
                  ) : null}
                </div>
              )}

              {showMasterCreate && (
                <div className="flex flex-wrap gap-2">
                  <Input
                    value={masterGroupName}
                    onChange={(event) => setMasterGroupName(event.target.value)}
                    placeholder="Group name"
                    className="max-w-xs"
                  />
                  <Button type="button" size="sm" disabled={busy} onClick={() => void saveMasterGroup()}>
                    {busy ? "Saving…" : "Save/Create as Master"}
                  </Button>
                  {layoutMasterGroups.length > 0 ? (
                    <Button
                      type="button"
                      size="sm"
                      variant="ghost"
                      disabled={busy}
                      onClick={() => {
                        setMasterCreateMode(false);
                        setMasterGroupName("");
                      }}
                    >
                      Cancel
                    </Button>
                  ) : null}
                </div>
              )}

              {(canCreateMasterForLayout && (showMasterCreate || masterSelectedId) && data) ? (
                <div className="space-y-4">
                  <RuleEditor
                    title="Master Rules"
                    rules={masterDraftRules}
                    data={masterEditorData}
                    readOnly={!showMasterCreate}
                    protectFirst={showMasterCreate}
                    onChange={setMasterDraftRules}
                  />
                  <IsinEditor
                    title="Master ISIN Rules"
                    rules={masterDraftIsinRules}
                    data={masterEditorData}
                    tenant={tenant}
                    readOnly={!showMasterCreate}
                    protectFirst={showMasterCreate}
                    onChange={setMasterDraftIsinRules}
                  />
                </div>
              ) : null}

              {showMasterPicker && !masterSelectedId ? (
                <p className="text-muted-foreground text-sm">Select a group to view its rules, or create a new one.</p>
              ) : null}
            </div>
          </SheetContent>
        </Sheet>
      ) : null}

      <div className="flex flex-wrap items-start justify-between gap-3">
        <div className="flex items-center gap-3">
          <span className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary">
            <Scale className="size-5" />
          </span>
          <div>
            <h2 className="font-semibold text-xl tracking-tight">Target Allocation Rules</h2>
            <p className="text-muted-foreground text-sm">
              {data.isReadOnly
                ? "Portfolio target allocation summary"
                : "Define your portfolio targets — each dimension total must not exceed 100%"}
            </p>
          </div>
        </div>
        {!data.isReadOnly ? (
          <Button type="button" variant="outline" size="sm" asChild className="gap-2">
            <Link href={`/customer/${tenant}/reports/portfolio-simulator`}>
              <FlaskConical className="size-3.5" />
              What-If Simulator
            </Link>
          </Button>
        ) : null}
      </div>

      {showCustomerPicker && (
        <div className="relative rounded-xl border bg-muted/30 p-3">
          <div className="flex items-center gap-3">
            <label className="flex shrink-0 items-center gap-2 font-medium text-sm"><span className="flex size-7 items-center justify-center rounded-lg bg-primary/10 text-primary"><User className="size-4" /></span>Customer</label>
            <div className="relative flex-1">
              <Search className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
              <Input
                value={customerSearch}
                onFocus={() => setCustomerDropdownOpen(true)}
                onBlur={() => window.setTimeout(() => setCustomerDropdownOpen(false), 150)}
                onChange={(event) => {
                  setCustomerSearch(event.target.value);
                  setCustomerDropdownOpen(true);
                }}
                placeholder="Search customers..."
                className="bg-background pr-9 pl-9"
              />
              {data.selectedCustomerId && (
                <button
                  type="button"
                  className="absolute top-1/2 right-3 -translate-y-1/2 text-muted-foreground hover:text-foreground"
                  onClick={() => void selectCustomer(null)}
                  aria-label="Clear customer"
                >
                  <X className="size-4" />
                </button>
              )}
              {customerDropdownOpen && (
                <div className="absolute z-30 mt-1 max-h-64 w-full overflow-y-auto rounded-lg border bg-popover p-1 shadow-lg">
                  {filteredCustomers.map(([id, name]) => (
                    <button
                      key={id}
                      type="button"
                      className="block w-full rounded-md px-3 py-2 text-left text-sm hover:bg-accent"
                      onMouseDown={(event) => event.preventDefault()}
                      onClick={() => void selectCustomer(Number(id))}
                    >
                      {name}
                    </button>
                  ))}
                  {!filteredCustomers.length && <p className="p-3 text-center text-muted-foreground text-sm">No customers found.</p>}
                </div>
              )}
            </div>
          </div>
        </div>
      )}

      {showInherit && (
        <div className="flex flex-wrap items-center gap-3 rounded-xl border bg-muted/30 p-3">
          <label className="inline-flex items-center gap-2 font-medium text-sm">
            <Checkbox
              checked={inheritEnabled}
              onCheckedChange={(checked) => {
                if (checked === true) {
                  inheritedRuleSigsRef.current = new Set();
                  inheritedIsinSigsRef.current = new Set();
                  setInheritEnabled(true);
                  return;
                }
                disableInherit();
              }}
            />
            Inherit from master group
          </label>
          {inheritEnabled && (
            <Popover open={inheritPickerOpen} onOpenChange={setInheritPickerOpen}>
              <PopoverTrigger asChild>
                <Button
                  type="button"
                  variant="outline"
                  className="h-9 w-[260px] justify-between font-normal"
                  disabled={busy || layoutMasterGroups.length === 0}
                >
                  <span className="truncate">
                    {inheritGroupIds.length === 0
                      ? "Select"
                      : inheritGroupIds.length === 1
                        ? (layoutMasterGroups.find((g) => String(g.id) === inheritGroupIds[0])?.name ?? "1 selected")
                        : `${inheritGroupIds.length} selected`}
                  </span>
                  <ChevronDown className="size-4 shrink-0 opacity-50" />
                </Button>
              </PopoverTrigger>
              <PopoverContent className="w-[260px] p-2" align="start">
                <div className="max-h-56 space-y-1 overflow-y-auto">
                  {layoutMasterGroups.map((group) => {
                    const id = String(group.id);
                    const selected = inheritGroupIds.includes(id);
                    return (
                      <label
                        key={group.id}
                        className="flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent"
                      >
                        <Checkbox
                          checked={selected}
                          disabled={busy || Boolean(pendingInheritSelectAll)}
                          onCheckedChange={(checked) => toggleInheritGroup(id, checked === true)}
                        />
                        <span className="truncate">{group.name}</span>
                      </label>
                    );
                  })}
                  {layoutMasterGroups.length === 0 && (
                    <p className="p-2 text-center text-muted-foreground text-xs">
                      {previewLayout === "macro" ? "No Macro master groups." : "No Default master groups."}
                    </p>
                  )}
                </div>
              </PopoverContent>
            </Popover>
          )}
        </div>
      )}

      <AlertDialog
        open={Boolean(pendingInheritSelectAll)}
        onOpenChange={(open) => {
          if (!open) cancelInheritSelectAll();
        }}
      >
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Only one Select All is allowed</AlertDialogTitle>
            <AlertDialogDescription>
              You already have Select All
              {pendingInheritSelectAll ? ` (${pendingInheritSelectAll.customerPct}%)` : ""}, and the
              master group also has Select All
              {pendingInheritSelectAll ? ` (${pendingInheritSelectAll.masterPct}%)` : ""}. Which one
              do you want to keep? The other will be removed.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter className="flex-col gap-2 sm:flex-row">
            <AlertDialogCancel type="button">Cancel</AlertDialogCancel>
            <AlertDialogAction type="button" onClick={() => resolveInheritSelectAll("customer")}>
              Keep customer&apos;s
            </AlertDialogAction>
            <AlertDialogAction type="button" onClick={() => resolveInheritSelectAll("master")}>
              Use master
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>

      <Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-4">
        <TabsList className={`grid h-11 w-full ${showReportTab ? "grid-cols-4" : "grid-cols-3"}`}>
          <TabsTrigger value="charts" className="gap-2"><BarChart3 className="size-4" />Charts</TabsTrigger>
          <TabsTrigger value="overview" className="gap-2"><ListChecks className="size-4" />Overview</TabsTrigger>
          <TabsTrigger value="advanced" className="gap-2"><Settings2 className="size-4" />Advanced</TabsTrigger>
          {showReportTab && <TabsTrigger value="report" className="gap-2"><Scale className="size-4" />Actual vs Ideal</TabsTrigger>}
        </TabsList>

        <TabsContent value="charts" className="space-y-5">
          <TotalCards rules={rules} data={editorData} />
          <IsinAllocationSummary rules={isinRules} data={editorData} />
        </TabsContent>
        <TabsContent value="overview" className="space-y-5">
          <RulesOverview rules={rules} />
          <IsinAllocationSummary rules={isinRules} data={editorData} />
        </TabsContent>
        <TabsContent value="advanced" className="space-y-5">
          <TotalCards rules={advanced} data={editorData} advanced />
          <RuleEditor title="Advanced Rules" rules={advanced} data={editorData} advanced readOnly={data.isReadOnly} onChange={setAdvanced} />
          <IsinEditor title="ISIN Advanced Rules" rules={advancedIsin} data={editorData} tenant={tenant} customerId={data.selectedCustomerId} advanced readOnly={data.isReadOnly} onChange={setAdvancedIsin} />
          {!data.isReadOnly && (
            <div className="flex justify-end">
              <Button className="gap-2" disabled={busy} onClick={() => void saveAdvanced()}>
                <Save className="size-4" />{busy ? "Saving…" : "Save Advanced Rules"}
              </Button>
            </div>
          )}
        </TabsContent>
        {showReportTab && <TabsContent value="report"><ActualVsIdeal data={data} /></TabsContent>}
      </Tabs>

      {showStandardRules && !data.isReadOnly && (
        <div className="space-y-4">
          <RuleEditor title="Rules" rules={rules} data={editorData} onChange={setRules} />
          <IsinEditor title="Allocate by ISIN" rules={isinRules} data={data} tenant={tenant} onChange={setIsinRules} />
          <div className="flex justify-end">
            <Button className="gap-2" disabled={busy} onClick={() => void saveStandard()}>
              <Save className="size-4" />{busy ? "Saving…" : "Save Settings"}
            </Button>
          </div>
        </div>
      )}
    </div>
  );
}
