import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "../ui/table";
import { Input } from "../ui/input";
import { Button } from "../ui/button";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "../ui/select";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "../ui/dialog";
import { Label } from "../ui/label";
import { Switch } from "../ui/switch";
import { Badge } from "../ui/badge";
import { ArrowLeft, Plus, Edit, MoreHorizontal, Trash2 } from "lucide-react";
import { toast } from "sonner";
import { skipCountForPage } from "../../lib/paginationQuery";
import { preventDialogDismissOnOverlay } from "../../lib/formDialogLayout";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import { SearchableSelect } from "../ui/searchable-select";
import { SearchableMultiSelect } from "../ui/searchable-multi-select";
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "../ui/pagination";
import { getLabels, getLabel, createLabel, batchCreateLabels, updateLabel, deleteLabel } from "../../services/labelService";
import type { LabelDto, LabelCreateInput, LabelBatchCreateItemInput, LabelUpdateInput } from "../../types/label";
import { getLocations } from "../../services/locationService";
import { getGroups } from "../../services/groupService";
import { getPartners } from "../../services/partnerService";
import { getLabelCategories } from "../../services/labelCategoryService";
import { getLabelTypes } from "../../services/labelTypeService";
import { getLabelTemplate, getLabelTemplates, updateLabelTemplate } from "../../services/labelTemplateService";
import { getProduct, getProducts } from "../../services/productService";
import { getProductCategories } from "../../services/productCategoryService";
import type { GroupListItem } from "../../types/group";
import type { PartnerListItem } from "../../types/partner";
import type { LocationDto } from "../../types/location";
import type { LabelCategoryDto } from "../../types/labelCategory";
import type { LabelTypeDto } from "../../types/labelType";
import type {
  LabelElement,
  LabelTemplate,
  LabelTemplateDto,
  LabelTemplateProductDefaultDto,
} from "../../types/labelTemplate";
import type { ProductDto } from "../../types/product";
import type { ProductCategoryDto } from "../../types/productCategory";
import type { ProductLocationLinkDto } from "../../types/productLocation";
import { getProductLocations } from "../../services/productLocationService";
import { NutritionManualEntryForm } from "./NutritionManualEntryForm";
import { LabelPreviewOnly } from "./LabelTemplateEditor/LabelCanvas";
import {
  appliedLocationToEditor,
  buildTemplateContentsFromElements,
  canonicalElementType,
  dataEntryColumnLabel,
  isDataEntryTableColumnElement,
  isDateTimeDataEntryField,
  labelElementsToApiPayload,
  normalizeTemplateBorder,
  sortTemplateElementsForDisplay,
} from "../../types/labelTemplate";
import {
  applyLiveDateTimePreviewToElements,
  LABEL_FORM_OFFSET_UNITS,
  normalizeLabelFormOffsetInput,
  offsetFieldUiStateFromStored,
  serializePrintInputOffset,
} from "../../lib/labelFormDatePreview";
import {
  listNutritionElements,
  listNutritionManualFieldSpecs,
  mergeNutritionManualIntoConfig,
  nutritionDefaultValuesJsonForSave,
  nutritionManualValuesFromTemplateConfig,
  parseNutritionManualFromDefaults,
  type NutritionManualFieldSpec,
} from "../../lib/nutritionManualEntry";
import {
  applyProductCodeValueToLabelElements,
  buildTemplateBarcodeQrDefaultsFromCodeValue,
  buildTemplateLabelBindingDefaults,
  isTemplateLabelNameElement,
  isTemplateLabelTypeElement,
  isTemplateSectionBarcodeOrQrElement,
} from "../../lib/productCodeValueTemplate";
import { useCategoryScopeAuth } from "../../hooks/useCategoryScopeAuth";
import {
  effectiveScopePartnerId,
  hydrateCategoryScopeFromLocationIds,
  locationsScopedForCategoryForm,
  regionNamesToGroupIds,
  regionOptionsForPartner,
  scopePartnerValidationMessage,
  buildSpecifiedLocationPayload,
} from "../../lib/categoryScopeForm";

function toDisplay(v: string | null | undefined): string {
  const s = (v ?? "").trim();
  return s ? s : "None";
}

/** 列表行：适用 Region 展示 */
function labelRowRegionText(item: LabelDto, groups: GroupListItem[]): string {
  const display = (item.region ?? "").trim();
  if (display) return display;
  const at = String(item.appliedRegionType ?? "").trim().toUpperCase();
  if (at === "ALL") return "All Regions";
  const ids = [
    ...new Set(
      [...(item.regionIds ?? []), ...(item.groupIds ?? [])]
        .map((x) => String(x).trim())
        .filter(Boolean),
    ),
  ];
  if (ids.length) {
    const names = ids
      .map((id) => (groups.find((g) => g.id === id)?.groupName ?? "").trim())
      .filter(Boolean);
    if (names.length) return [...new Set(names)].join(", ");
  }
  return "None";
}

function hydrateLabelRegionScopeFromDto(
  dto: Pick<LabelDto, "appliedRegionType" | "regionIds" | "groupIds" | "locationId">,
  groups: GroupListItem[],
  partners: PartnerListItem[],
  locations: LocationDto[],
): { partnerId: string; regionNames: string[] } {
  const mode = String(dto.appliedRegionType ?? "ALL").trim().toUpperCase();
  const groupIds = [
    ...new Set(
      [...(dto.regionIds ?? []), ...(dto.groupIds ?? [])]
        .map((x) => String(x).trim())
        .filter(Boolean),
    ),
  ];

  let partnerId = "";
  if (groupIds.length) {
    partnerId = (groups.find((g) => groupIds.includes(g.id))?.partnerId ?? "").trim();
  }
  if (!partnerId && (dto.locationId ?? "").trim()) {
    partnerId = hydrateCategoryScopeFromLocationIds(
      [String(dto.locationId).trim()],
      locations,
      partners,
      groups,
    ).partnerId;
  }

  if (mode === "ALL" && partnerId) {
    return {
      partnerId,
      regionNames: regionOptionsForPartner(groups, partnerId).map((o) => o.value),
    };
  }

  const regionNames = groupIds.filter(Boolean);
  return { partnerId, regionNames: [...new Set(regionNames)] };
}

function buildLabelRegionScopePayload(
  selectedRegionNames: string[],
  groups: GroupListItem[],
  partnerId: string,
): { appliedRegionType: "ALL" | "SPECIFIED"; regionIds: string[] } {
  const pid = partnerId.trim();
  const selected = selectedRegionNames.map((x) => x.trim()).filter(Boolean);
  if (!selected.length) {
    return { appliedRegionType: "SPECIFIED", regionIds: [] };
  }
  const allNames = regionOptionsForPartner(groups, pid).map((o) => o.value);
  const isAll =
    allNames.length > 0 &&
    allNames.length === selected.length &&
    allNames.every((n) => selected.includes(n));
  if (isAll) {
    return { appliedRegionType: "ALL", regionIds: [] };
  }
  const regionIds = regionNamesToGroupIds(selected, groups, pid);
  return { appliedRegionType: "SPECIFIED", regionIds };
}

function mergeUniqueById<T extends { id: string }>(lists: T[][]): T[] {
  const map = new Map<string, T>();
  for (const list of lists) {
    for (const item of list) {
      const id = (item.id ?? "").trim();
      if (id) map.set(id, item);
    }
  }
  return [...map.values()];
}

/** 列表行：产品列（接口可能返回 `products` 汇总字符串或 `productName` / productIds） */
function labelRowProductsText(item: LabelDto): string {
  const aggregated = (item.products ?? "").trim();
  if (aggregated) return aggregated;
  const pn = (item.productName ?? "").trim();
  if (pn) return pn;
  const n = item.productIds?.length ?? 0;
  if (n > 0) return `${n} product(s)`;
  return "None";
}

/** 与列表/产品页一致：按区域（Group）解析该组下门店 */
function locationsForRegionGroup(
  locations: LocationDto[],
  groups: GroupListItem[],
  regionGroupId: string,
): LocationDto[] {
  const id = regionGroupId.trim();
  if (!id) return [];
  const g = groups.find((x) => (x.id ?? "").trim() === id);
  if (!g) return [];
  const gn = (g.groupName ?? "").trim();
  const pn = (g.partnerName ?? "").trim();
  return locations.filter(
    (l) => (l.groupName ?? "").trim() === gn && (l.partner ?? "").trim() === pn,
  );
}

function locationsForRegionGroups(
  locations: LocationDto[],
  groups: GroupListItem[],
  regionGroupIds: string[],
): LocationDto[] {
  const seen = new Set<string>();
  const out: LocationDto[] = [];
  for (const gid of regionGroupIds.map((x) => x.trim()).filter(Boolean)) {
    for (const loc of locationsForRegionGroup(locations, groups, gid)) {
      if (!seen.has(loc.id)) {
        seen.add(loc.id);
        out.push(loc);
      }
    }
  }
  return out;
}

function buildProductLocationMap(rows: ProductLocationLinkDto[]): Map<string, string[]> {
  const map = new Map<string, string[]>();
  for (const row of rows) {
    const pid = (row.productId ?? "").trim();
    const lid = (row.locationId ?? "").trim();
    if (!pid || !lid) continue;
    if (!map.has(pid)) map.set(pid, []);
    const arr = map.get(pid)!;
    if (!arr.includes(lid)) arr.push(lid);
  }
  return map;
}

function mergeProductLocationOverlay(map: Map<string, string[]>, products: ProductDto[]): Map<string, string[]> {
  const next = new Map(map);
  for (const p of products) {
    if (Array.isArray(p.locationIds)) {
      next.set(p.id, [...new Set(p.locationIds.map((x) => String(x).trim()).filter(Boolean))]);
    }
  }
  return next;
}

function productCategoryMatchesRegion(c: ProductCategoryDto, allowedLocIds: Set<string>): boolean {
  const at = String(c.availabilityType ?? "ALL").trim().toUpperCase();
  if (at !== "SPECIFIED") return true;
  const lids = (c.locationIds ?? []).map((x) => String(x).trim()).filter(Boolean);
  return lids.some((lid) => allowedLocIds.has(lid));
}

function labelCategoryMatchesRegion(c: LabelCategoryDto, allowedLocIds: Set<string>): boolean {
  const at = String(c.availabilityType ?? "ALL").trim().toUpperCase();
  if (at !== "SPECIFIED") return true;
  const lids = (c.locationIds ?? []).map((x) => String(x).trim()).filter(Boolean);
  return lids.some((lid) => allowedLocIds.has(lid));
}

function labelTypeMatchesRegion(t: LabelTypeDto, allowedLocIds: Set<string>): boolean {
  const at = String(t.availabilityType ?? "ALL").trim().toUpperCase();
  if (at !== "SPECIFIED") return true;
  const lids = (t.locationIds ?? []).map((x) => String(x).trim()).filter(Boolean);
  return lids.some((lid) => allowedLocIds.has(lid));
}

type TemplateInputColumn =
  | { kind: "element"; el: LabelElement }
  | { kind: "nutrition"; parent: LabelElement; spec: NutritionManualFieldSpec };

function templateAppliesToRegion(tpl: LabelTemplateDto, allowedLocIds: Set<string>): boolean {
  const mode = appliedLocationToEditor(tpl);
  if (mode === "ALL") return true;
  const ids = (tpl.appliedLocationIds ?? []).map((x) => String(x).trim()).filter(Boolean);
  return ids.some((lid) => allowedLocIds.has(lid));
}

/** 详情 / 列表行 → 编辑表单（列表接口可能缺 ID 字段，需再以 GET 详情补全） */
function labelDtoToUpdateForm(d: LabelDto): LabelUpdateInput {
  const ids = d.productIds;
  const arr = Array.isArray(ids) ? ids.map((x) => String(x).trim()).filter(Boolean) : [];
  return {
    labelName: d.labelName ?? "",
    templateCode: d.templateCode ?? "",
    locationIds: (() => {
      const fromList = (d.locationIds ?? []).map((x) => String(x).trim()).filter(Boolean);
      if (fromList.length) return [...new Set(fromList)];
      const single = (d.locationId ?? "").trim();
      return single ? [single] : [];
    })(),
    labelCategoryId: d.labelCategoryId ?? "",
    labelTypeId: d.labelTypeId ?? "",
    /** 编辑表单仅支持单商品：多商品时取第一个 */
    productIds: arr.length ? [arr[0]] : [],
    labelInfoJson: d.labelInfoJson ?? null,
    state: d.state ?? true,
    appliedRegionType: d.appliedRegionType ?? undefined,
    regionIds: d.regionIds ?? undefined,
    groupIds: d.groupIds ?? undefined,
  };
}
function getDataEntryElements(template: LabelTemplateDto | null): LabelElement[] {
  if (!template) return [];
  return sortTemplateElementsForDisplay((template.elements ?? []) as LabelElement[]).filter(
    isDataEntryTableColumnElement,
  );
}

/** 新建/编辑标签弹窗「Template Input Data」：Label 可编辑列 + Template 条码/二维码只读列（不含 Label Name，由左侧 General Settings 统一录入） */
function getCreateLabelTemplateInputElements(template: LabelTemplateDto | null): LabelElement[] {
  if (!template) return [];
  const sorted = sortTemplateElementsForDisplay((template.elements ?? []) as LabelElement[]);
  const seen = new Set<string>();
  const out: LabelElement[] = [];
  for (const el of sorted) {
    if (isTemplateLabelNameElement(el)) continue;
    if (!isDataEntryTableColumnElement(el) && !isTemplateSectionBarcodeOrQrElement(el)) continue;
    if (seen.has(el.id)) continue;
    seen.add(el.id);
    out.push(el);
  }
  return out;
}

function buildCreateLabelPreviewTemplate(
  apiTpl: LabelTemplateDto | null,
  textValues: Record<string, string>,
  dateOffsets: Record<string, { unit: string; value: string }>,
  nutritionByElementId: Record<string, Record<string, string>>,
  productCodeValue?: string | null,
): LabelTemplate | null {
  if (!apiTpl) return null;
  const tmpl = dtoToEditorTemplate(apiTpl);
  const now = new Date();
  for (const el of tmpl.elements) {
    const cfg = { ...(el.config as Record<string, unknown>) };
    delete cfg.__previewFormatted;
    const id = el.id;
    const type = canonicalElementType(el.type);
    if (!isDataEntryTableColumnElement(el)) {
      el.config = cfg;
      continue;
    }
    if (isDateTimeDataEntryField(el)) {
      const pair = dateOffsets[id] ?? { unit: "Days", value: "" };
      const unit = pair.unit || "Days";
      const norm = normalizeLabelFormOffsetInput(pair.value);
      if (norm.kind === "invalid") {
        cfg.text = "";
      } else if (norm.kind === "zero") {
        cfg.text = serializePrintInputOffset(unit, "0");
      } else {
        cfg.text = serializePrintInputOffset(unit, norm.storeValue);
      }
    } else {
      const v = textValues[id] ?? "";
      if (type === "BARCODE" || type === "QRCODE") cfg.data = v;
      else if (type === "IMAGE") cfg.src = v;
      else if (type === "TEXT_STATIC" || type === "TEXT_PRODUCT" || type === "TEXT_PRICE") cfg.text = v;
      else cfg.text = v;
    }
    el.config = cfg;
  }
  tmpl.elements = applyLiveDateTimePreviewToElements(tmpl.elements, now);
  for (const el of tmpl.elements) {
    if (canonicalElementType(el.type) !== "NUTRITION") continue;
    const manual = nutritionByElementId[el.id] ?? {};
    const merged = mergeNutritionManualIntoConfig({ ...(el.config as Record<string, unknown>) }, manual);
    el.config = merged as LabelElement["config"];
  }
  tmpl.elements = applyProductCodeValueToLabelElements(tmpl.elements, productCodeValue);
  return tmpl;
}

function collectTemplateDefaultValuesForSave(
  latest: LabelTemplateDto,
  textValues: Record<string, string>,
  dateOffsets: Record<string, { unit: string; value: string }>,
  nutritionByElementId: Record<string, Record<string, string>>,
  productCodeValue?: string | null,
  labelBindings?: { labelName?: string | null; labelTypeName?: string | null },
): Record<string, string> {
  const out: Record<string, string> = {};
  for (const el of getDataEntryElements(latest)) {
    const id = el.id;
    if (isDateTimeDataEntryField(el)) {
      const pair = dateOffsets[id] ?? { unit: "Days", value: "" };
      const unit = pair.unit || "Days";
      const norm = normalizeLabelFormOffsetInput(pair.value);
      if (norm.kind === "invalid") {
        out[id] = "";
      } else if (norm.kind === "zero") {
        out[id] = serializePrintInputOffset(unit, "0");
      } else {
        out[id] = serializePrintInputOffset(unit, norm.storeValue);
      }
    } else {
      out[id] = String(textValues[id] ?? "");
    }
  }
  for (const nel of listNutritionElements((latest.elements ?? []) as LabelElement[])) {
    const manual = nutritionByElementId[nel.id];
    if (!manual) continue;
    const j = nutritionDefaultValuesJsonForSave(manual);
    if (j) out[nel.id] = j;
  }
  Object.assign(
    out,
    buildTemplateBarcodeQrDefaultsFromCodeValue(
      (latest.elements ?? []) as LabelElement[],
      productCodeValue,
    ),
  );
  Object.assign(
    out,
    buildTemplateLabelBindingDefaults((latest.elements ?? []) as LabelElement[], labelBindings ?? {}),
  );
  return out;
}

/** 编辑标签：从模板 templateProductDefaults 恢复当前产品+类型的录入与预览状态 */
function hydrateLabelFormInputFromProductDefaults(
  template: LabelTemplateDto,
  productId: string,
  labelTypeId: string,
): {
  textValues: Record<string, string>;
  dateOffsets: Record<string, { unit: string; value: string }>;
  nutritionByElementId: Record<string, Record<string, string>>;
} {
  const pid = productId.trim();
  const ltid = labelTypeId.trim();
  const key = `${pid}::${ltid}`;
  const row = (template.templateProductDefaults ?? []).find(
    (r) => `${String(r.productId ?? "").trim()}::${String(r.labelTypeId ?? "").trim()}` === key,
  );
  const stored = row?.defaultValues ?? {};
  const elements = getCreateLabelTemplateInputElements(template);
  const textValues: Record<string, string> = {};
  const dateOffsets: Record<string, { unit: string; value: string }> = {};
  const nutritionByElementId: Record<string, Record<string, string>> = {};

  for (const el of elements) {
    if (isDateTimeDataEntryField(el)) {
      dateOffsets[el.id] = offsetFieldUiStateFromStored(String(stored[el.id] ?? ""));
      textValues[el.id] = "";
    } else {
      textValues[el.id] = String(stored[el.id] ?? "");
    }
  }

  for (const nel of listNutritionElements((template.elements ?? []) as LabelElement[])) {
    const raw = stored[nel.id];
    if (typeof raw === "string" && raw.trim().startsWith("{")) {
      nutritionByElementId[nel.id] = parseNutritionManualFromDefaults(raw);
    } else {
      nutritionByElementId[nel.id] = nutritionManualValuesFromTemplateConfig(nel);
    }
  }

  return { textValues, dateOffsets, nutritionByElementId };
}

function mergeLabelBindingsIntoTextValues(
  textValues: Record<string, string>,
  elements: LabelElement[],
  bindings: { labelName?: string | null; labelTypeName?: string | null },
  options?: { onlyWhenEmpty?: boolean; storedValues?: Record<string, string> },
): Record<string, string> {
  const out = { ...textValues };
  const binds = buildTemplateLabelBindingDefaults(elements, bindings);
  for (const el of elements) {
    if (!isTemplateLabelNameElement(el) && !isTemplateLabelTypeElement(el)) continue;
    const bindVal = binds[el.id];
    if (!bindVal) continue;
    if (options?.onlyWhenEmpty) {
      const stored = String(options.storedValues?.[el.id] ?? out[el.id] ?? "").trim();
      if (stored) {
        out[el.id] = stored;
        continue;
      }
    }
    out[el.id] = bindVal;
  }
  return out;
}

function clearTemplateLabelNameFields(
  textValues: Record<string, string>,
  elements: LabelElement[],
): Record<string, string> {
  const out = { ...textValues };
  for (const el of elements) {
    if (isTemplateLabelNameElement(el)) out[el.id] = "";
  }
  return out;
}

function resolveLabelFormTemplateTextValues(
  template: LabelTemplateDto,
  productId: string,
  labelTypeId: string,
  labelTypeName: string,
  labelName?: string,
): Record<string, string> {
  const empty = emptyLabelFormTemplateInputState(template);
  const pid = productId.trim();
  const ltid = labelTypeId.trim();
  const elements = (template.elements ?? []) as LabelElement[];
  const trimmedLabelName = (labelName ?? "").trim();

  if (pid && ltid) {
    const hydrated = hydrateLabelFormInputFromProductDefaults(template, pid, ltid);
    let textValues = { ...empty.textValues, ...hydrated.textValues };
    textValues = clearTemplateLabelNameFields(textValues, elements);
    textValues = mergeLabelBindingsIntoTextValues(
      textValues,
      elements,
      { labelTypeName },
      { onlyWhenEmpty: true, storedValues: hydrated.textValues },
    );
    if (trimmedLabelName) {
      textValues = mergeLabelBindingsIntoTextValues(textValues, elements, {
        labelName: trimmedLabelName,
      });
    }
    return textValues;
  }

  let textValues = { ...empty.textValues };
  const bindings: { labelName?: string; labelTypeName?: string } = {};
  if (trimmedLabelName) bindings.labelName = trimmedLabelName;
  if (ltid && labelTypeName) bindings.labelTypeName = labelTypeName;
  if (Object.keys(bindings).length) {
    textValues = mergeLabelBindingsIntoTextValues(textValues, elements, bindings);
  }
  return textValues;
}

function emptyLabelFormTemplateInputState(template: LabelTemplateDto | null): {
  textValues: Record<string, string>;
  dateOffsets: Record<string, { unit: string; value: string }>;
  nutritionByElementId: Record<string, Record<string, string>>;
} {
  if (!template) {
    return { textValues: {}, dateOffsets: {}, nutritionByElementId: {} };
  }
  const textValues: Record<string, string> = {};
  const dateOffsets: Record<string, { unit: string; value: string }> = {};
  for (const el of getCreateLabelTemplateInputElements(template)) {
    textValues[el.id] = "";
    if (isDateTimeDataEntryField(el)) {
      dateOffsets[el.id] = { unit: "Days", value: "" };
    }
  }
  const nutritionByElementId: Record<string, Record<string, string>> = {};
  for (const n of listNutritionElements((template.elements ?? []) as LabelElement[])) {
    nutritionByElementId[n.id] = nutritionManualValuesFromTemplateConfig(n);
  }
  return { textValues, dateOffsets, nutritionByElementId };
}

function templateListCode(t: LabelTemplateDto): string {
  return (t.templateCode ?? t.id ?? "").trim();
}

function templateListLabel(t: LabelTemplateDto): string {
  return (t.templateName ?? t.name ?? "").trim() || "None";
}

function dtoToEditorTemplate(apiTemplate: LabelTemplateDto): LabelTemplate {
  return {
    id: apiTemplate.id,
    name: (apiTemplate.name ?? apiTemplate.templateName ?? "").trim() || "Unnamed template",
    labelType: (apiTemplate.labelType as any) ?? "PRICE",
    unit: (apiTemplate.unit as any) ?? "cm",
    width: apiTemplate.width ?? 6,
    height: apiTemplate.height ?? 4,
    appliedLocation: appliedLocationToEditor(apiTemplate),
    appliedLocationIds: [...(apiTemplate.appliedLocationIds ?? [])],
    showRuler: apiTemplate.showRuler ?? true,
    showGrid: apiTemplate.showGrid ?? true,
    border: normalizeTemplateBorder(apiTemplate.border),
    elements: (apiTemplate.elements ?? []).map((raw, idx) => {
      const el = raw as LabelElement;
      const en = (el.elementName ?? "").trim();
      return {
        ...el,
        elementName: en || `element${idx + 1}`,
      };
    }),
  };
}

function buildTemplateDefaultsMap(
  template: LabelTemplateDto,
): Map<string, LabelTemplateProductDefaultDto> {
  const map = new Map<string, LabelTemplateProductDefaultDto>();
  for (const row of template.templateProductDefaults ?? []) {
    const key = `${row.productId}::${row.labelTypeId}`;
    map.set(key, row);
  }
  return map;
}

/** 新建/编辑标签：先选 Company + 适用 Region 后再拉取下拉数据 */
export type LabelFormReferenceScopeIds = {
  partnerId: string;
  /** Region 展示名（`fl_group.GroupName`），至少一项 */
  regionNames: string[];
};

function clearLabelFormScopedOptions(setters: {
  setTemplates: (v: LabelTemplateDto[]) => void;
  setLocations: (v: LocationDto[]) => void;
  setCategories: (v: LabelCategoryDto[]) => void;
  setTypes: (v: LabelTypeDto[]) => void;
  setProducts: (v: ProductDto[]) => void;
  setProductCategories: (v: ProductCategoryDto[]) => void;
  setProductLocationMap: (v: Map<string, string[]>) => void;
}) {
  setters.setTemplates([]);
  setters.setLocations([]);
  setters.setCategories([]);
  setters.setTypes([]);
  setters.setProducts([]);
  setters.setProductCategories([]);
  setters.setProductLocationMap(new Map());
}

/**
 * @param scopeIds `null`：一次性加载全量；有 partnerId + groupIds 时按所选 Region 并集请求各下拉接口。
 */
function useLabelFormReferenceData(open: boolean, scopeIds: LabelFormReferenceScopeIds | null) {
  const [bootstrapLoading, setBootstrapLoading] = useState(false);
  const [scopedLoading, setScopedLoading] = useState(false);
  const [templates, setTemplates] = useState<LabelTemplateDto[]>([]);
  const [locations, setLocations] = useState<LocationDto[]>([]);
  const [groups, setGroups] = useState<GroupListItem[]>([]);
  const [partners, setPartners] = useState<PartnerListItem[]>([]);
  const [categories, setCategories] = useState<LabelCategoryDto[]>([]);
  const [types, setTypes] = useState<LabelTypeDto[]>([]);
  const [products, setProducts] = useState<ProductDto[]>([]);
  const [productCategories, setProductCategories] = useState<ProductCategoryDto[]>([]);
  const [productLocationMap, setProductLocationMap] = useState<Map<string, string[]>>(() => new Map());

  const scopeReady = !!(scopeIds?.partnerId?.trim() && (scopeIds?.regionNames?.length ?? 0) > 0);
  const loadAllOnOpen = scopeIds === null;

  useEffect(() => {
    if (!open) return;
    let cancelled = false;
    (async () => {
      setBootstrapLoading(true);
      try {
        const [grpRes, partnerRes] = await Promise.all([
          getGroups({ skipCount: 1, maxResultCount: 500 }),
          getPartners({ skipCount: 1, maxResultCount: 500 }),
        ]);
        if (cancelled) return;
        setGroups(grpRes.items ?? []);
        setPartners(partnerRes.items ?? []);
      } catch (e: unknown) {
        if (!cancelled) {
          toast.error("Failed to load company / region options", {
            description: e instanceof Error ? e.message : "Check network or sign-in.",
          });
          setGroups([]);
          setPartners([]);
        }
      } finally {
        if (!cancelled) setBootstrapLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open]);

  useEffect(() => {
    if (!open) return;
    const scopedSetters = {
      setTemplates,
      setLocations,
      setCategories,
      setTypes,
      setProducts,
      setProductCategories,
      setProductLocationMap,
    };

    if (!loadAllOnOpen && !scopeReady) {
      clearLabelFormScopedOptions(scopedSetters);
      return;
    }

    let cancelled = false;
    (async () => {
      setScopedLoading(true);
      try {
        if (loadAllOnOpen) {
          const [tplRes, locRes, catRes, typeRes, prodRes, menuCatRes, plRes] = await Promise.all([
          getLabelTemplates({ skipCount: 1, maxResultCount: 500 }),
          getLocations({ skipCount: 1, maxResultCount: 500 }),
          getLabelCategories({ skipCount: 1, maxResultCount: 500 }),
          getLabelTypes({ skipCount: 1, maxResultCount: 500 }),
          getProducts({ skipCount: 1, maxResultCount: 500 }),
          getProductCategories({ skipCount: 1, maxResultCount: 500, sorting: "OrderNum desc" }),
          getProductLocations({ skipCount: 1, maxResultCount: 2000 }),
        ]);
        if (cancelled) return;
        setTemplates(tplRes.items ?? []);
        setLocations(locRes.items ?? []);
        setCategories(catRes.items ?? []);
        setTypes(typeRes.items ?? []);
        setProducts(prodRes.items ?? []);
        setProductCategories(menuCatRes.items ?? []);
        const rawMap = buildProductLocationMap(plRes.items ?? []);
        setProductLocationMap(mergeProductLocationOverlay(rawMap, prodRes.items ?? []));
          return;
        }

        const partnerId = scopeIds!.partnerId.trim();
        const regionNames = scopeIds!.regionNames.map((x) => x.trim()).filter(Boolean);
        const groupIds = regionNamesToGroupIds(regionNames, groups, partnerId);
        const p = partners.find((x) => (x.id ?? "").trim() === partnerId);
        const partnerName = (p?.partnerName ?? "").trim();
        if (!partnerName || !regionNames.length || !groupIds.length) {
          clearLabelFormScopedOptions(scopedSetters);
          return;
        }

        const [tplRes, locRes, plRes] = await Promise.all([
          getLabelTemplates({ skipCount: 1, maxResultCount: 500 }),
          getLocations({ skipCount: 1, maxResultCount: 500, partner: partnerName }),
          getProductLocations({ skipCount: 1, maxResultCount: 2000 }),
        ]);
        const locAll = locRes.items ?? [];
        const locItems = locationsScopedForCategoryForm(
          locAll,
          partners,
          partnerId,
          regionNames,
          groups,
        );
        const allowedLocIds = new Set(locItems.map((l) => l.id).filter(Boolean));

        const [catLists, typeLists, prodLists, menuCatLists] = await Promise.all([
          Promise.all(
            groupIds.map((gid) =>
              getLabelCategories({ skipCount: 1, maxResultCount: 500, groupId: gid }),
            ),
          ),
          Promise.all(
            groupIds.map((gid) => getLabelTypes({ skipCount: 1, maxResultCount: 500, groupId: gid })),
          ),
          Promise.all(
            groupIds.map((gid) =>
              getProducts({ skipCount: 1, maxResultCount: 500, partnerId, groupId: gid }),
            ),
          ),
          Promise.all(
            groupIds.map((gid) =>
              getProductCategories({
                skipCount: 1,
                maxResultCount: 500,
                sorting: "OrderNum desc",
                groupId: gid,
              }),
            ),
          ),
        ]);
        if (cancelled) return;

        const tplItems = (tplRes.items ?? []).filter((t) =>
          templateAppliesToRegion(t, allowedLocIds),
        );
        setLocations(locItems);
        setTemplates(tplItems);
        setCategories(mergeUniqueById(catLists.map((r) => r.items ?? [])));
        setTypes(mergeUniqueById(typeLists.map((r) => r.items ?? [])));
        const prodItems = mergeUniqueById(prodLists.map((r) => r.items ?? []));
        setProducts(prodItems);
        setProductCategories(mergeUniqueById(menuCatLists.map((r) => r.items ?? [])));
        const rawMap = buildProductLocationMap(plRes.items ?? []);
        setProductLocationMap(mergeProductLocationOverlay(rawMap, prodItems));
      } catch (e: unknown) {
        if (!cancelled) {
          toast.error("Failed to load options", {
            description: e instanceof Error ? e.message : "Check network or sign-in.",
          });
          clearLabelFormScopedOptions(scopedSetters);
        }
      } finally {
        if (!cancelled) setScopedLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open, loadAllOnOpen, scopeReady, scopeIds?.partnerId, scopeIds?.regionNames?.join("\u0001"), groups, partners]);

  return {
    loading: bootstrapLoading || scopedLoading,
    bootstrapLoading,
    scopedLoading,
    templates,
    locations,
    groups,
    partners,
    categories,
    types,
    products,
    productCategories,
    productLocationMap,
  };
}

/** 先选商品分类，再在分类下搜索并单选商品（提交仍用 productIds: [id]） */
function ProductSingleSelectByCategoryField({
  productCatalogCategoryId,
  onProductCatalogCategoryIdChange,
  productId,
  onProductIdChange,
  products,
  productCategories,
  disabled,
}: {
  productCatalogCategoryId: string;
  onProductCatalogCategoryIdChange: (id: string) => void;
  productId: string;
  onProductIdChange: (id: string) => void;
  products: ProductDto[];
  productCategories: ProductCategoryDto[];
  /** 为 true 时表示未选区域等作用域，下拉禁用并显示统一提示 */
  disabled?: boolean;
}) {
  const catalogCategoryOptions = useMemo(
    () =>
      productCategories
        .map((c) => ({
          value: (c.id ?? "").trim(),
          label: toDisplay(c.categoryName ?? c.categoryCode ?? c.id),
        }))
        .filter((o) => o.value),
    [productCategories],
  );

  const filteredProducts = useMemo(() => {
    const cid = productCatalogCategoryId.trim();
    if (!cid) return [];
    return products.filter((p) => (p.categoryId ?? "").trim() === cid);
  }, [products, productCatalogCategoryId]);

  const productSelectOptions = useMemo(() => {
    const rows = filteredProducts.map((p) => {
      const name = (p.productName ?? p.productCode ?? "").trim() || p.id;
      return { value: p.id, label: `${name}` };
    });
    const pid = productId.trim();
    if (pid && !rows.some((r) => r.value === pid)) {
      const p = products.find((x) => x.id === pid);
      const name = (p?.productName ?? p?.productCode ?? "").trim() || pid;
      return [{ value: pid, label: `${name} (current)` }, ...rows];
    }
    return rows;
  }, [filteredProducts, productId, products]);

  const productOptionsForSelect = useMemo(
    () =>
      productSelectOptions.map((r) => ({
        value: r.value,
        label: r.label,
      })),
    [productSelectOptions],
  );

  return (
    <div className="space-y-3">
      <div className="space-y-2">
        <Label>Product category *</Label>
        <SearchableSelect
          value={productCatalogCategoryId}
          onValueChange={(v) => {
            onProductCatalogCategoryIdChange(v);
            onProductIdChange("");
          }}
          options={catalogCategoryOptions}
          placeholder={
            disabled ? "Select company and region first" : "Select product category first"
          }
          searchPlaceholder="Search product category…"
          emptyText={disabled ? "Select company and region." : "No product categories."}
          disabled={disabled}
        />
      </div>
      <div className="space-y-2">
        <Label>Product *</Label>
        <SearchableSelect
          value={productId}
          onValueChange={onProductIdChange}
          options={productOptionsForSelect}
          placeholder={
            disabled
              ? "Select company and region first"
              : productCatalogCategoryId.trim()
                ? "Select one product"
                : "Select category first"
          }
          searchPlaceholder="Search products…"
          emptyText={
            disabled
              ? "Select company and region."
              : productCatalogCategoryId.trim()
                ? "No products in this category."
                : "Select a product category."
          }
          disabled={disabled || !productCatalogCategoryId.trim()}
        />
      </div>
    </div>
  );
}

type LabelsListProps = {
  /** 大于 0 时打开「新增标签」弹窗，并由父级通过 onOpenCreateIntentConsumed 归零 */
  openCreateSeq?: number;
  onOpenCreateIntentConsumed?: () => void;
};

export function LabelsList({ openCreateSeq = 0, onOpenCreateIntentConsumed }: LabelsListProps = {}) {
  const [bulkAddTemplateCode, setBulkAddTemplateCode] = useState<string | null>(null);
  const [bulkAddContextHint, setBulkAddContextHint] = useState<string | undefined>(undefined);
  const [isBulkAddDialogOpen, setIsBulkAddDialogOpen] = useState(false);
  const [bulkLoadingTemplates, setBulkLoadingTemplates] = useState(false);
  const [bulkLoadingPreview, setBulkLoadingPreview] = useState(false);
  const [bulkTemplates, setBulkTemplates] = useState<LabelTemplateDto[]>([]);
  const [bulkTemplateCode, setBulkTemplateCode] = useState("");
  const [bulkPreviewTemplate, setBulkPreviewTemplate] = useState<LabelTemplate | null>(null);
  const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
  const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
  const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
  const [editingLabel, setEditingLabel] = useState<LabelDto | null>(null);
  const [deletingLabel, setDeletingLabel] = useState<LabelDto | null>(null);
  const [labels, setLabels] = useState<LabelDto[]>([]);
  const [loading, setLoading] = useState(false);
  const [total, setTotal] = useState(0);
  const [refreshSeq, setRefreshSeq] = useState(0);
  const [actionsOpenForId, setActionsOpenForId] = useState<string | null>(null);

  const scopeAuth = useCategoryScopeAuth();

  const [keyword, setKeyword] = useState("");
  const [companyFilter, setCompanyFilter] = useState<string>("all");
  const [regionFilter, setRegionFilter] = useState<string>("all");
  const [locationFilter, setLocationFilter] = useState<string>("all");
  const [labelCategoryFilter, setLabelCategoryFilter] = useState<string>("all");
  const [labelTypeFilter, setLabelTypeFilter] = useState<string>("all");
  const [templateFilter, setTemplateFilter] = useState<string>("all");
  const [stateFilter, setStateFilter] = useState<string>("all");

  const [filterLocations, setFilterLocations] = useState<LocationDto[]>([]);
  const [filterPartners, setFilterPartners] = useState<PartnerListItem[]>([]);
  const [filterGroups, setFilterGroups] = useState<GroupListItem[]>([]);
  const [filterLabelCategories, setFilterLabelCategories] = useState<LabelCategoryDto[]>([]);
  const [filterLabelTypes, setFilterLabelTypes] = useState<LabelTypeDto[]>([]);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const [locRes, grpRes, catRes, typeRes, partnerRes] = await Promise.all([
          getLocations({ skipCount: 1, maxResultCount: 500 }),
          getGroups({ skipCount: 1, maxResultCount: 500 }),
          getLabelCategories({ skipCount: 1, maxResultCount: 500 }),
          getLabelTypes({ skipCount: 1, maxResultCount: 500 }),
          getPartners({ skipCount: 1, maxResultCount: 500, state: true }),
        ]);
        if (cancelled) return;
        setFilterLocations(locRes.items ?? []);
        setFilterPartners(partnerRes.items ?? []);
        setFilterGroups(grpRes.items ?? []);
        setFilterLabelCategories(catRes.items ?? []);
        setFilterLabelTypes(typeRes.items ?? []);
      } catch (e: unknown) {
        if (!cancelled) {
          setFilterLocations([]);
          setFilterPartners([]);
          setFilterGroups([]);
          setFilterLabelCategories([]);
          setFilterLabelTypes([]);
          toast.error("Failed to load filters.", {
            description: e instanceof Error ? e.message : "Please try again.",
          });
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, []);

  const companySelectOptions = useMemo(
    () =>
      [...filterPartners]
        .filter((p) => (p.partnerName ?? "").trim())
        .sort((a, b) => (a.partnerName ?? "").localeCompare(b.partnerName ?? "", undefined, { sensitivity: "base" })),
    [filterPartners],
  );

  const regionSelectOptions = useMemo(() => {
    let list = filterGroups;
    if (companyFilter !== "all") {
      list = list.filter((g) => g.partnerId === companyFilter);
    }
    const m = new Map<string, GroupListItem>();
    for (const g of list) {
      const id = (g.id ?? "").trim();
      if (id && !m.has(id)) m.set(id, g);
    }
    return Array.from(m.values()).sort((a, b) =>
      (a.groupName ?? "").localeCompare(b.groupName ?? "", undefined, { sensitivity: "base" }),
    );
  }, [filterGroups, companyFilter]);

  const selectedToolbarPartner = useMemo(
    () => (companyFilter === "all" ? null : filterPartners.find((p) => p.id === companyFilter) ?? null),
    [companyFilter, filterPartners],
  );

  const selectedToolbarGroup = useMemo(
    () => (regionFilter === "all" ? null : filterGroups.find((g) => g.id === regionFilter) ?? null),
    [regionFilter, filterGroups],
  );

  const locationsForToolbarFilter = useMemo(() => {
    return filterLocations.filter((loc) => {
      if (selectedToolbarPartner) {
        const pn = (selectedToolbarPartner.partnerName ?? "").trim();
        if (pn && (loc.partner ?? "").trim() !== pn) return false;
      }
      if (selectedToolbarGroup) {
        const gn = (selectedToolbarGroup.groupName ?? "").trim();
        if (gn && (loc.groupName ?? "").trim() !== gn) return false;
      }
      return true;
    });
  }, [filterLocations, selectedToolbarPartner, selectedToolbarGroup]);

  const labelScopeQuery = useMemo(
    () => ({
      partnerId: companyFilter !== "all" ? companyFilter : undefined,
      groupId: regionFilter !== "all" ? regionFilter : undefined,
      locationId: locationFilter !== "all" ? locationFilter : undefined,
    }),
    [companyFilter, regionFilter, locationFilter],
  );

  useEffect(() => {
    setRegionFilter("all");
    setLocationFilter("all");
  }, [companyFilter]);

  useEffect(() => {
    setLocationFilter("all");
  }, [regionFilter]);

  useEffect(() => {
    if (companyFilter === "all") return;
    if (!companySelectOptions.some((p) => p.id === companyFilter)) setCompanyFilter("all");
  }, [companyFilter, companySelectOptions]);

  useEffect(() => {
    if (regionFilter === "all") return;
    if (!regionSelectOptions.some((g) => g.id === regionFilter)) setRegionFilter("all");
  }, [companyFilter, regionSelectOptions, regionFilter]);

  const categoryToolbarOptions = useMemo(() => {
    const m = new Map<string, LabelCategoryDto>();
    for (const c of filterLabelCategories) {
      const id = (c.id ?? "").trim();
      if (id && !m.has(id)) m.set(id, c);
    }
    return Array.from(m.values()).sort((a, b) => {
      const la = (a.categoryName ?? a.categoryCode ?? a.id ?? "").toString();
      const lb = (b.categoryName ?? b.categoryCode ?? b.id ?? "").toString();
      return la.localeCompare(lb, undefined, { sensitivity: "base" });
    });
  }, [filterLabelCategories]);

  const typeToolbarOptions = useMemo(() => {
    const m = new Map<string, LabelTypeDto>();
    for (const t of filterLabelTypes) {
      const id = (t.id ?? "").trim();
      if (id && !m.has(id)) m.set(id, t);
    }
    return Array.from(m.values()).sort((a, b) => {
      const la = (a.typeName ?? a.typeCode ?? a.id ?? "").toString();
      const lb = (b.typeName ?? b.typeCode ?? b.id ?? "").toString();
      return la.localeCompare(lb, undefined, { sensitivity: "base" });
    });
  }, [filterLabelTypes]);

  useEffect(() => {
    if (labelCategoryFilter === "all") return;
    const allowed = new Set(categoryToolbarOptions.map((c) => c.id));
    if (!allowed.has(labelCategoryFilter)) setLabelCategoryFilter("all");
  }, [categoryToolbarOptions, labelCategoryFilter]);

  useEffect(() => {
    if (labelTypeFilter === "all") return;
    const allowed = new Set(typeToolbarOptions.map((t) => t.id));
    if (!allowed.has(labelTypeFilter)) setLabelTypeFilter("all");
  }, [typeToolbarOptions, labelTypeFilter]);

  useEffect(() => {
    if (locationFilter === "all") return;
    const allowed = new Set(locationsForToolbarFilter.map((l) => l.id));
    if (!allowed.has(locationFilter)) setLocationFilter("all");
  }, [locationsForToolbarFilter, locationFilter]);

  const [pageIndex, setPageIndex] = useState(1);
  const [pageSize, setPageSize] = useState(10);

  useEffect(() => {
    if (openCreateSeq <= 0) return;
    setIsCreateDialogOpen(true);
    onOpenCreateIntentConsumed?.();
  }, [openCreateSeq, onOpenCreateIntentConsumed]);

  const abortRef = useRef<AbortController | null>(null);
  const keywordTimerRef = useRef<number | null>(null);
  const [debouncedKeyword, setDebouncedKeyword] = useState("");

  useEffect(() => {
    if (keywordTimerRef.current) window.clearTimeout(keywordTimerRef.current);
    keywordTimerRef.current = window.setTimeout(() => setDebouncedKeyword(keyword.trim()), 300);
    return () => {
      if (keywordTimerRef.current) window.clearTimeout(keywordTimerRef.current);
    };
  }, [keyword]);

  const totalPages = Math.max(1, Math.ceil(total / pageSize));

  useEffect(() => {
    setPageIndex(1);
  }, [debouncedKeyword, companyFilter, regionFilter, locationFilter, labelCategoryFilter, labelTypeFilter, templateFilter, stateFilter, pageSize]);

  useEffect(() => {
    const run = async () => {
      abortRef.current?.abort();
      const ac = new AbortController();
      abortRef.current = ac;

      setLoading(true);
      try {
        const skipCount = skipCountForPage(pageIndex);
        const res = await getLabels(
          {
            skipCount,
            maxResultCount: pageSize,
          keyword: debouncedKeyword || undefined,
          labelCategoryId: labelCategoryFilter !== "all" ? labelCategoryFilter : undefined,
          labelTypeId: labelTypeFilter !== "all" ? labelTypeFilter : undefined,
          templateCode: templateFilter !== "all" ? templateFilter : undefined,
          state: stateFilter === "all" ? undefined : stateFilter === "true",
            ...labelScopeQuery,
          },
          ac.signal,
        );

        setLabels(res.items ?? []);
        setTotal(res.totalCount ?? 0);
      } catch (e: any) {
        if (e?.name === "AbortError") return;
        toast.error("Failed to load labels.", {
          description: e?.message ? String(e.message) : "Please try again.",
        });
        setLabels([]);
        setTotal(0);
      } finally {
        setLoading(false);
      }
    };

    run();
    return () => abortRef.current?.abort();
  }, [
    debouncedKeyword,
    labelScopeQuery,
    labelCategoryFilter,
    labelTypeFilter,
    templateFilter,
    stateFilter,
    pageIndex,
    pageSize,
    refreshSeq,
  ]);

  const refreshList = () => setRefreshSeq((x) => x + 1);

  const openEdit = (label: LabelDto) => {
    setActionsOpenForId(null);
    setEditingLabel(label);
    setIsEditDialogOpen(true);
  };

  const openDelete = (label: LabelDto) => {
    setActionsOpenForId(null);
    setDeletingLabel(label);
    setIsDeleteDialogOpen(true);
  };

  useEffect(() => {
    if (!isBulkAddDialogOpen) return;
    let cancelled = false;
    (async () => {
      setBulkLoadingTemplates(true);
      try {
        const res = await getLabelTemplates({ skipCount: 1, maxResultCount: 500 });
        if (cancelled) return;
        const items = (res.items ?? []).filter((t) => templateListCode(t));
        setBulkTemplates(items);
      } catch (e: unknown) {
        if (cancelled) return;
        setBulkTemplates([]);
        toast.error("Failed to load templates.", {
          description: e instanceof Error ? e.message : "Please try again.",
        });
      } finally {
        if (!cancelled) setBulkLoadingTemplates(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [isBulkAddDialogOpen]);

  useEffect(() => {
    if (!isBulkAddDialogOpen || !bulkTemplateCode) {
      setBulkPreviewTemplate(null);
      return;
    }
    let cancelled = false;
    (async () => {
      setBulkLoadingPreview(true);
      try {
        const tpl = await getLabelTemplate(bulkTemplateCode);
        if (cancelled) return;
        setBulkPreviewTemplate(dtoToEditorTemplate(tpl));
      } catch (e: unknown) {
        if (cancelled) return;
        setBulkPreviewTemplate(null);
        toast.error("Failed to load template preview.", {
          description: e instanceof Error ? e.message : "Please try again.",
        });
      } finally {
        if (!cancelled) setBulkLoadingPreview(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [isBulkAddDialogOpen, bulkTemplateCode]);

  const bulkTemplateOptions = useMemo(
    () =>
      bulkTemplates.map((t) => ({
        value: templateListCode(t),
        label: templateListLabel(t),
      })),
    [bulkTemplates],
  );

  const handleBulkAddConfirm = () => {
    if (!bulkTemplateCode.trim()) {
      toast.error("Template required", {
        description: "Please select a template first.",
      });
      return;
    }
    const tpl = bulkTemplates.find((x) => templateListCode(x) === bulkTemplateCode);
    const title = tpl ? templateListLabel(tpl) : bulkTemplateCode;
    setBulkAddContextHint(`Template: ${title}`);
    setBulkAddTemplateCode(bulkTemplateCode);
    setIsBulkAddDialogOpen(false);
  };

  const closeBulkAddDialog = (open: boolean) => {
    setIsBulkAddDialogOpen(open);
    if (!open) {
      setBulkTemplateCode("");
      setBulkPreviewTemplate(null);
    }
  };

  const closeBulkAddPage = () => {
    setBulkAddTemplateCode(null);
    setBulkAddContextHint(undefined);
    refreshList();
  };

  if (bulkAddTemplateCode) {
    return (
      <div className="h-[calc(100vh-8rem)] min-h-[500px] flex flex-col pt-2">
        <LabelBulkAddPage
          templateCode={bulkAddTemplateCode}
          onBack={closeBulkAddPage}
          contextHint={bulkAddContextHint}
        />
      </div>
    );
  }

  return (
    <div className="h-full flex flex-col">
      <div className="pb-4">
        <div className="flex flex-col gap-4">
          <div className="flex flex-wrap items-center gap-3">
            <Input
              placeholder="Search"
              value={keyword}
              onChange={(e) => setKeyword(e.target.value)}
              style={{ height: 40, boxSizing: 'border-box' }}
              className="bg-white border border-gray-300 rounded-md w-40 shrink-0 placeholder:text-gray-500"
            />
            {scopeAuth.requireCompanySelection ? (
              <Select value={companyFilter} onValueChange={setCompanyFilter}>
                <SelectTrigger className="bg-white border border-gray-300 rounded-md w-[150px] shrink-0" style={{ height: 40, boxSizing: 'border-box' }}>
                  <SelectValue placeholder="Company" />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="all">All Companies</SelectItem>
                  {companySelectOptions.map((p) => (
                    <SelectItem key={p.id} value={p.id}>
                      {toDisplay(p.partnerName)}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            ) : null}
            <Select value={regionFilter} onValueChange={setRegionFilter}>
              <SelectTrigger className="bg-white border border-gray-300 rounded-md w-[150px] shrink-0" style={{ height: 40, boxSizing: 'border-box' }}>
                <SelectValue placeholder="Region" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All Region</SelectItem>
                {regionSelectOptions.map((g) => (
                  <SelectItem key={g.id} value={g.id}>
                    {toDisplay(g.groupName)}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            <Select value={locationFilter} onValueChange={setLocationFilter}>
              <SelectTrigger className="bg-white border border-gray-300 rounded-md w-[150px] shrink-0" style={{ height: 40, boxSizing: 'border-box' }}>
                <SelectValue placeholder="Location" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All Locations</SelectItem>
                {locationsForToolbarFilter.map((loc) => (
                  <SelectItem key={loc.id} value={loc.id}>
                    {(() => {
                      const code = (loc.locationCode ?? "").trim();
                      const name = (loc.locationName ?? "").trim();
                      if (code && name) return `${code} - ${name}`;
                      return name || code || loc.id;
                    })()}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            <Select value={labelCategoryFilter} onValueChange={setLabelCategoryFilter}>
              <SelectTrigger className="bg-white border border-gray-300 rounded-md w-[150px] shrink-0" style={{ height: 40, boxSizing: 'border-box' }}>
                <SelectValue placeholder="Category" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All Categories</SelectItem>
                {categoryToolbarOptions.map((c) => (
                  <SelectItem key={c.id} value={c.id}>
                    {toDisplay(c.categoryName ?? c.categoryCode ?? c.id)}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            <Select value={labelTypeFilter} onValueChange={setLabelTypeFilter}>
              <SelectTrigger className="bg-white border border-gray-300 rounded-md w-[150px] shrink-0" style={{ height: 40, boxSizing: 'border-box' }}>
                <SelectValue placeholder="Type" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All Types</SelectItem>
                {typeToolbarOptions.map((t) => (
                  <SelectItem key={t.id} value={t.id}>
                    {toDisplay(t.typeName ?? t.typeCode ?? t.id)}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            <Select value={stateFilter} onValueChange={setStateFilter}>
              <SelectTrigger className="bg-white border border-gray-300 rounded-md w-[150px] shrink-0" style={{ height: 40, boxSizing: 'border-box' }}>
                <SelectValue placeholder="Status" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">Status</SelectItem>
                <SelectItem value="true">Active</SelectItem>
                <SelectItem value="false">Inactive</SelectItem>
              </SelectContent>
            </Select>
            <div className="flex-1" />
            <Button
              variant="outline"
              className="h-10 px-6 shrink-0 font-semibold border"
              style={{ backgroundColor: "#16a34a", color: "#ffffff", borderColor: "#16a34a" }}
              onClick={() => setIsBulkAddDialogOpen(true)}
            >
              Bulk Add
            </Button>
            <Button
              className="bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-md h-10 px-6 shrink-0"
              onClick={() => setIsCreateDialogOpen(true)}
            >
              New Label <Plus className="ml-1 h-4 w-4" />
            </Button>
          </div>
        </div>
      </div>

      <div className="flex-1 overflow-auto pt-6">
        <div className="rounded-md border bg-white shadow-sm">
          <Table>
            <TableHeader>
              <TableRow className="bg-gray-50 hover:bg-gray-50">
                <TableHead className="font-bold text-gray-900 min-w-[120px]">Label Name</TableHead>
                <TableHead className="font-bold text-gray-900 min-w-[120px]">Product</TableHead>
                <TableHead className="font-bold text-gray-900 min-w-[120px]">Region</TableHead>
                <TableHead className="font-bold text-gray-900 min-w-[100px]">Location</TableHead>
                <TableHead className="font-bold text-gray-900 min-w-[110px]">Label Category</TableHead>
                <TableHead className="font-bold text-gray-900 min-w-[120px]">Product Category</TableHead>
                <TableHead className="font-bold text-gray-900 min-w-[110px]">Label Type</TableHead>
                <TableHead className="font-bold text-gray-900 min-w-[110px]">Template</TableHead>
                <TableHead className="font-bold text-gray-900 w-[92px]">Status</TableHead>
                <TableHead className="font-bold text-gray-900 min-w-[140px]">Last Edited</TableHead>
                <TableHead className="font-bold text-gray-900 text-center w-[88px]">Actions</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {loading ? (
                <TableRow>
                  <TableCell colSpan={11} className="text-center text-sm text-gray-500 py-10">
                    Loading...
                  </TableCell>
                </TableRow>
              ) : labels.length === 0 ? (
                <TableRow>
                  <TableCell colSpan={11} className="text-center text-sm text-gray-500 py-10">
                    No results.
                  </TableCell>
                </TableRow>
              ) : (
                labels.map((item) => (
                  <TableRow key={item.id} className="hover:bg-gray-50">
                    <TableCell className="font-medium text-gray-900 whitespace-nowrap">{toDisplay(item.labelName)}</TableCell>
                    <TableCell
                      className="font-medium text-gray-900 whitespace-nowrap max-w-[220px] truncate"
                      title={labelRowProductsText(item)}
                    >
                      {labelRowProductsText(item)}
                    </TableCell>
                    <TableCell
                      className="font-medium text-gray-900 whitespace-nowrap max-w-[180px] truncate"
                      title={labelRowRegionText(item, filterGroups)}
                    >
                      {labelRowRegionText(item, filterGroups)}
                    </TableCell>
                    <TableCell className="font-medium text-gray-900 whitespace-nowrap">
                      {toDisplay(item.locationName ?? item.locationId)}
                    </TableCell>
                    <TableCell className="font-medium text-gray-900 whitespace-nowrap">
                      {toDisplay(item.labelCategoryName ?? item.labelCategoryId)}
                    </TableCell>
                    <TableCell className="font-medium text-gray-900 whitespace-nowrap">
                      {toDisplay(item.productCategoryName)}
                    </TableCell>
                    <TableCell className="font-medium text-gray-900 whitespace-nowrap">
                      {toDisplay(item.labelTypeName ?? item.labelTypeId)}
                    </TableCell>
                    <TableCell
                      className="font-medium text-gray-900 whitespace-nowrap max-w-[200px] truncate"
                      title={toDisplay(item.templateName ?? item.templateCode)}
                    >
                      {toDisplay(item.templateName ?? item.templateCode)}
                    </TableCell>
                    <TableCell className="whitespace-nowrap">
                      <Badge
                        className={`font-medium ${item.state === true ? "bg-green-600" : "bg-gray-400"}`}
                      >
                        {item.state === true ? "Active" : "Inactive"}
                      </Badge>
                    </TableCell>
                    <TableCell className="font-medium text-gray-900 whitespace-nowrap">
                      {toDisplay(item.lastEdited)}
                    </TableCell>
                    <TableCell className="text-center">
                      <Popover
                        open={actionsOpenForId === item.id}
                        onOpenChange={(open) => setActionsOpenForId(open ? item.id : null)}
                      >
                        <PopoverTrigger asChild>
                          <Button
                            type="button"
                            variant="ghost"
                            size="icon"
                            className="h-8 w-8"
                            aria-label="Row actions"
                          >
                            <MoreHorizontal className="h-4 w-4 text-gray-500" />
                          </Button>
                        </PopoverTrigger>
                        <PopoverContent align="end" className="w-48 p-1">
                          <Button
                            type="button"
                            variant="ghost"
                            className="w-full justify-start gap-2 h-9 px-2 font-normal"
                            onClick={() => openEdit(item)}
                          >
                            <Edit className="w-4 h-4" />
                            Edit
                          </Button>
                          <Button
                            type="button"
                            variant="ghost"
                            className="w-full justify-start gap-2 h-9 px-2 font-normal text-red-600 hover:text-red-700 hover:bg-red-50"
                            onClick={() => openDelete(item)}
                          >
                            <Trash2 className="w-4 h-4 shrink-0" />
                            Delete
                          </Button>
                        </PopoverContent>
                      </Popover>
                    </TableCell>
                  </TableRow>
                ))
              )}
            </TableBody>
          </Table>
        </div>
      </div>

      <div className="pt-4">
        <div className="flex items-center justify-between text-sm text-gray-600">
          <div>
            Showing {total === 0 ? 0 : (pageIndex - 1) * pageSize + 1}-
            {Math.min(pageIndex * pageSize, total)} of {total}
          </div>
          <div className="flex items-center gap-3">
            <Select value={String(pageSize)} onValueChange={(v) => setPageSize(Number(v))}>
              <SelectTrigger className="w-[110px] h-9 rounded-md border border-gray-300 bg-white text-gray-900">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                {[10, 20, 50].map((n) => (
                  <SelectItem key={n} value={String(n)}>
                    {n} / page
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            <Pagination className="mx-0 w-auto justify-end">
              <PaginationContent>
                <PaginationItem>
                  <PaginationPrevious
                    href="#"
                    size="default"
                    onClick={(e) => {
                      e.preventDefault();
                      setPageIndex((p) => Math.max(1, p - 1));
                    }}
                    aria-disabled={pageIndex <= 1}
                    className={pageIndex <= 1 ? "pointer-events-none opacity-50" : ""}
                  />
                </PaginationItem>
                <PaginationItem>
                  <PaginationLink
                    href="#"
                    isActive
                    size="default"
                    onClick={(e) => e.preventDefault()}
                  >
                    Page {pageIndex} / {totalPages}
                  </PaginationLink>
                </PaginationItem>
                <PaginationItem>
                  <PaginationNext
                    href="#"
                    size="default"
                    onClick={(e) => {
                      e.preventDefault();
                      setPageIndex((p) => Math.min(totalPages, p + 1));
                    }}
                    aria-disabled={pageIndex >= totalPages}
                    className={pageIndex >= totalPages ? "pointer-events-none opacity-50" : ""}
                  />
                </PaginationItem>
              </PaginationContent>
            </Pagination>
          </div>
        </div>
      </div>

      <CreateLabelDialog
        open={isCreateDialogOpen}
        onOpenChange={setIsCreateDialogOpen}
        onCreated={() => {
          setPageIndex(1);
          refreshList();
        }}
      />

      <EditLabelDialog
        open={isEditDialogOpen}
        label={editingLabel}
        onOpenChange={(open) => {
          setIsEditDialogOpen(open);
          if (!open) setEditingLabel(null);
        }}
        onUpdated={refreshList}
      />

      <DeleteLabelDialog
        open={isDeleteDialogOpen}
        label={deletingLabel}
        onOpenChange={(open) => {
          setIsDeleteDialogOpen(open);
          if (!open) setDeletingLabel(null);
        }}
        onDeleted={refreshList}
      />

      <Dialog open={isBulkAddDialogOpen} onOpenChange={closeBulkAddDialog}>
        <DialogContent
          className="sm:max-w-none"
          style={{ width: "min(960px, calc(100vw - 3rem))", maxWidth: "min(960px, calc(100vw - 3rem))" }}
        >
          <DialogHeader>
            <DialogTitle>Select a Template to Bulk Add</DialogTitle>
          </DialogHeader>
          <div className="grid grid-cols-1 gap-6 py-2 md:grid-cols-[minmax(240px,320px)_1fr]">
            <div className="space-y-2 min-w-0">
              <Label>Template</Label>
              <SearchableSelect
                value={bulkTemplateCode}
                onValueChange={setBulkTemplateCode}
                options={bulkTemplateOptions}
                placeholder={bulkLoadingTemplates ? "Loading templates…" : "Select template"}
                searchPlaceholder="Search template…"
                emptyText="No templates found."
                disabled={bulkLoadingTemplates}
              />
            </div>
            <div className="flex min-h-[420px] min-w-0 items-center justify-center overflow-auto rounded-xl border border-gray-200 bg-gray-50 p-4">
              {bulkLoadingPreview ? (
                <div className="text-sm text-gray-500">Loading preview…</div>
              ) : bulkPreviewTemplate ? (
                <div className="flex w-full min-w-0 items-center justify-center">
                  <LabelPreviewOnly template={bulkPreviewTemplate} maxWidth={520} previewAsPrinted />
                </div>
              ) : (
                <div className="text-sm text-gray-500">Select a template to preview.</div>
              )}
            </div>
          </div>
          <DialogFooter>
            <Button variant="outline" onClick={() => closeBulkAddDialog(false)}>
              Cancel
            </Button>
            <Button
              variant="outline"
              className="min-w-24 font-semibold border"
              style={{ backgroundColor: "#16a34a", color: "#ffffff", borderColor: "#16a34a" }}
              disabled={!bulkTemplateCode.trim() || bulkLoadingPreview}
              onClick={handleBulkAddConfirm}
            >
              Bulk Add
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}

type BulkAddRow = {
  id: string;
  partnerId: string;
  regionIds: string[];
  productCategoryId: string;
  productId: string;
  labelName: string;
  locationIds: string[];
  labelCategoryId: string;
  labelTypeId: string;
  state: boolean;
  templateDataValues: Record<string, string>;
  templateDateOffsets: Record<string, { unit: string; value: string }>;
  nutritionByElementId: Record<string, Record<string, string>>;
};

function createEmptyBulkAddRow(scopeAuth: ReturnType<typeof useCategoryScopeAuth>): BulkAddRow {
  return {
    id: `row-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
    partnerId: scopeAuth.requireCompanySelection ? "" : scopeAuth.fixedPartnerId.trim(),
    regionIds: [],
    productCategoryId: "",
    productId: "",
    labelName: "",
    locationIds: [],
    labelCategoryId: "",
    labelTypeId: "",
    state: true,
    templateDataValues: {},
    templateDateOffsets: {},
    nutritionByElementId: {},
  };
}

function LabelBulkAddPage({
  templateCode,
  onBack,
  contextHint,
}: {
  templateCode: string;
  onBack: () => void;
  contextHint?: string;
}) {
  const scopeAuth = useCategoryScopeAuth();
  const {
    loading,
    templates,
    locations,
    groups,
    partners,
    categories,
    types,
    products,
    productCategories,
    productLocationMap,
  } = useLabelFormReferenceData(true, null);
  const [rows, setRows] = useState<BulkAddRow[]>(() => [createEmptyBulkAddRow(scopeAuth)]);
  const [saving, setSaving] = useState(false);
  const [bulkTemplateDetail, setBulkTemplateDetail] = useState<LabelTemplateDto | null>(null);
  const [bulkTemplateDetailLoading, setBulkTemplateDetailLoading] = useState(false);

  useEffect(() => {
    if (scopeAuth.requireCompanySelection) return;
    const fixed = scopeAuth.fixedPartnerId.trim();
    if (!fixed) return;
    setRows((prev) =>
      prev.map((row) => (row.partnerId === fixed ? row : { ...row, partnerId: fixed })),
    );
  }, [scopeAuth.requireCompanySelection, scopeAuth.fixedPartnerId]);

  useEffect(() => {
    const code = templateCode.trim();
    if (!code) {
      setBulkTemplateDetail(null);
      return;
    }
    let cancelled = false;
    (async () => {
      setBulkTemplateDetailLoading(true);
      try {
        const tpl = await getLabelTemplate(code);
        if (!cancelled) setBulkTemplateDetail(tpl);
      } catch (e: any) {
        if (!cancelled) {
          setBulkTemplateDetail(null);
          toast.error("Failed to load template fields.", {
            description: e?.message ? String(e.message) : "Please try again.",
          });
        }
      } finally {
        if (!cancelled) setBulkTemplateDetailLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [templateCode]);

  const templateFromList = useMemo(
    () => templates.find((t) => templateListCode(t) === templateCode) ?? null,
    [templates, templateCode],
  );

  const selectedTemplate = bulkTemplateDetail ?? templateFromList;

  const selectedTemplateLabel = selectedTemplate ? templateListLabel(selectedTemplate) : templateCode;

  const bulkDataEntryElements = useMemo(
    () => getCreateLabelTemplateInputElements(selectedTemplate),
    [selectedTemplate],
  );

  const bulkNutritionElements = useMemo(
    () => listNutritionElements(((selectedTemplate?.elements ?? []) as LabelElement[])),
    [selectedTemplate],
  );

  const bulkTemplateInputColumns = useMemo(() => {
    const cols: TemplateInputColumn[] = bulkDataEntryElements.map((el) => ({ kind: "element", el }));
    for (const nel of bulkNutritionElements) {
      for (const spec of listNutritionManualFieldSpecs(nel)) {
        cols.push({ kind: "nutrition", parent: nel, spec });
      }
    }
    return cols;
  }, [bulkDataEntryElements, bulkNutritionElements]);

  const showTemplateInputColumns = bulkTemplateInputColumns.length > 0;

  useEffect(() => {
    if (!selectedTemplate) return;
    const empty = emptyLabelFormTemplateInputState(selectedTemplate);
    setRows((prev) =>
      prev.map((row) => ({
        ...row,
        templateDataValues: { ...empty.textValues, ...row.templateDataValues },
        templateDateOffsets: { ...empty.dateOffsets, ...row.templateDateOffsets },
        nutritionByElementId: {
          ...empty.nutritionByElementId,
          ...row.nutritionByElementId,
        },
      })),
    );
  }, [selectedTemplate]);

  const companyOptions = useMemo(
    () =>
      [...partners]
        .filter((p) => (p.id ?? "").trim())
        .sort((a, b) =>
          (a.partnerName ?? "").localeCompare(b.partnerName ?? "", undefined, { sensitivity: "base" }),
        )
        .map((p) => ({
          value: (p.id ?? "").trim(),
          label: toDisplay(p.partnerName),
        })),
    [partners],
  );

  const updateRow = (rowId: string, patch: Partial<BulkAddRow>) => {
    setRows((prev) => prev.map((row) => (row.id === rowId ? { ...row, ...patch } : row)));
  };

  const updateRowTemplateText = (rowId: string, elementId: string, value: string) => {
    setRows((prev) =>
      prev.map((row) =>
        row.id === rowId
          ? {
              ...row,
              templateDataValues: {
                ...row.templateDataValues,
                [elementId]: value,
              },
            }
          : row,
      ),
    );
  };

  const updateRowTemplateDateOffset = (
    rowId: string,
    elementId: string,
    patch: Partial<{ unit: string; value: string }>,
  ) => {
    setRows((prev) =>
      prev.map((row) => {
        if (row.id !== rowId) return row;
        const cur = row.templateDateOffsets[elementId] ?? { unit: "Days", value: "" };
        return {
          ...row,
          templateDateOffsets: {
            ...row.templateDateOffsets,
            [elementId]: { ...cur, ...patch },
          },
        };
      }),
    );
  };

  const updateRowNutritionValue = (
    rowId: string,
    elementId: string,
    subKey: string,
    value: string,
  ) => {
    setRows((prev) =>
      prev.map((row) =>
        row.id === rowId
          ? {
              ...row,
              nutritionByElementId: {
                ...row.nutritionByElementId,
                [elementId]: {
                  ...(row.nutritionByElementId[elementId] ?? {}),
                  [subKey]: value,
                },
              },
            }
          : row,
      ),
    );
  };

  const updateRowPartner = (rowId: string, partnerId: string) => {
    updateRow(rowId, {
      partnerId,
      regionIds: [],
      productCategoryId: "",
      productId: "",
      locationIds: [],
      labelCategoryId: "",
      labelTypeId: "",
    });
  };

  const updateRowProduct = (row: BulkAddRow, productId: string, productOptionsSource: ProductDto[]) => {
    if (!selectedTemplate) {
      updateRow(row.id, { productId });
      return;
    }
    const productCodeValue = (() => {
      const p = productOptionsSource.find((x) => x.id === productId);
      return (p?.codeValue ?? "").trim();
    })();
    updateRow(row.id, {
      productId,
      templateDataValues: {
        ...row.templateDataValues,
        ...buildTemplateBarcodeQrDefaultsFromCodeValue(
          (selectedTemplate.elements ?? []) as LabelElement[],
          productCodeValue,
        ),
      },
    });
  };

  const updateRowLabelName = (row: BulkAddRow, labelName: string) => {
    if (!selectedTemplate) {
      updateRow(row.id, { labelName });
      return;
    }
    updateRow(row.id, {
      labelName,
      templateDataValues: mergeLabelBindingsIntoTextValues(
        row.templateDataValues,
        (selectedTemplate.elements ?? []) as LabelElement[],
        { labelName },
      ),
    });
  };

  const updateRowLabelType = (row: BulkAddRow, labelTypeId: string, labelTypeOptionsSource: LabelTypeDto[]) => {
    if (!selectedTemplate) {
      updateRow(row.id, { labelTypeId });
      return;
    }
    const labelType = labelTypeOptionsSource.find((x) => x.id === labelTypeId);
    updateRow(row.id, {
      labelTypeId,
      templateDataValues: mergeLabelBindingsIntoTextValues(
        row.templateDataValues,
        (selectedTemplate.elements ?? []) as LabelElement[],
        { labelTypeName: labelType?.typeName ?? labelType?.typeCode },
      ),
    });
  };

  const updateRowRegions = (rowId: string, regionIds: string[]) => {
    updateRow(rowId, {
      regionIds,
      productCategoryId: "",
      productId: "",
      locationIds: [],
      labelCategoryId: "",
      labelTypeId: "",
    });
  };

  const addRow = () => setRows((prev) => [...prev, createEmptyBulkAddRow(scopeAuth)]);
  const removeRow = (rowId: string) =>
    setRows((prev) => (prev.length <= 1 ? prev : prev.filter((row) => row.id !== rowId)));

  const validateAndBuildInput = (row: BulkAddRow, index: number): LabelBatchCreateItemInput | null => {
    const rowName = `Row ${index + 1}`;
    const effectivePartnerId = effectiveScopePartnerId(
      scopeAuth.requireCompanySelection,
      row.partnerId,
      scopeAuth.fixedPartnerId,
    );
    const partnerErr = scopePartnerValidationMessage(
      scopeAuth.requireCompanySelection,
      effectivePartnerId,
    );
    if (partnerErr) {
      toast.error(`${rowName}: validation failed`, { description: partnerErr });
      return null;
    }
    const regionPayload = buildLabelRegionScopePayload(row.regionIds, groups, effectivePartnerId);
    if (regionPayload.appliedRegionType === "SPECIFIED" && !regionPayload.regionIds.length) {
      toast.error(`${rowName}: validation failed`, {
        description: "Select company and applicable region(s).",
      });
      return null;
    }
    const locPayload = buildSpecifiedLocationPayload(
      locations,
      partners,
      groups,
      effectivePartnerId,
      row.regionIds,
      row.locationIds,
    );
    if (!locPayload.ok) {
      toast.error(`${rowName}: validation failed`, { description: locPayload.message });
      return null;
    }
    if (selectedTemplate) {
      const allowedLocIds = new Set(
        locationsForRegionGroups(
          locations,
          groups,
          regionNamesToGroupIds(row.regionIds, groups, effectivePartnerId),
        ).map((l) => l.id),
      );
      if (!templateAppliesToRegion(selectedTemplate, allowedLocIds)) {
        toast.error(`${rowName}: validation failed`, {
          description: "The selected template is not available in this row's region.",
        });
        return null;
      }
    }
    if (!row.productCategoryId.trim() || !row.productId.trim()) {
      toast.error(`${rowName}: validation failed`, {
        description: "Select a product category and product.",
      });
      return null;
    }
    if (!row.labelName.trim() || !row.labelCategoryId.trim() || !row.labelTypeId.trim()) {
      toast.error(`${rowName}: validation failed`, {
        description: "Fill label name, label category, and label type.",
      });
      return null;
    }
    const productId = row.productId.trim();
    const labelTypeId = row.labelTypeId.trim();
    const product = products.find((x) => x.id === productId);
    const labelType = types.find((x) => x.id === labelTypeId);
    const productCodeValue = (product?.codeValue ?? "").trim();
    const templateDefaultValues = selectedTemplate
      ? collectTemplateDefaultValuesForSave(
          selectedTemplate,
          row.templateDataValues,
          row.templateDateOffsets,
          row.nutritionByElementId,
          productCodeValue,
          {
            labelName: row.labelName,
            labelTypeName: labelType?.typeName ?? labelType?.typeCode,
          },
        )
      : {};
    return {
      labelName: row.labelName.trim(),
      locationIds: locPayload.locationIds,
      locationId: locPayload.locationIds[0] ?? null,
      labelCategoryId: row.labelCategoryId.trim(),
      labelTypeId,
      productIds: [productId],
      labelInfoJson: null,
      state: row.state,
      appliedRegionType: regionPayload.appliedRegionType,
      regionIds: regionPayload.regionIds,
      groupIds: regionPayload.regionIds,
      partnerId: effectivePartnerId,
      partnerIds: effectivePartnerId ? [effectivePartnerId] : [],
      templateDefaultValues,
      templateDataValues: { ...row.templateDataValues },
      templateDateOffsets: { ...row.templateDateOffsets },
      nutritionByElementId: { ...row.nutritionByElementId },
    };
  };

  const saveTemplateDefaultsAfterLabels = async (validRows: BulkAddRow[]) => {
    if (!selectedTemplate) return;
    const code = templateCode.trim();
    if (!code) return;
    const latest = await getLabelTemplate(code);
    const dataEls = getDataEntryElements(latest);
    const hasNutritionRows = listNutritionElements((latest.elements ?? []) as LabelElement[]).length > 0;
    if (dataEls.length === 0 && !hasNutritionRows) return;

    const defaultsMap = buildTemplateDefaultsMap(latest);
    for (const row of validRows) {
      const productId = row.productId.trim();
      const labelTypeId = row.labelTypeId.trim();
      if (!productId || !labelTypeId) continue;
      const product = products.find((x) => x.id === productId);
      const labelType = types.find((x) => x.id === labelTypeId);
      const productCodeValue = (product?.codeValue ?? "").trim();
      const inputDefaultValues = collectTemplateDefaultValuesForSave(
        latest,
        row.templateDataValues,
        row.templateDateOffsets,
        row.nutritionByElementId,
        productCodeValue,
        {
          labelName: row.labelName,
          labelTypeName: labelType?.typeName ?? labelType?.typeCode,
        },
      );
      defaultsMap.set(`${productId}::${labelTypeId}`, {
        productId,
        labelTypeId,
        defaultValues: { ...inputDefaultValues },
        orderNum: defaultsMap.size + 1,
      });
    }

    const mergedDefaults = Array.from(defaultsMap.values()).map((row, idx) => ({
      ...row,
      orderNum: idx + 1,
    }));
    const elements = sortTemplateElementsForDisplay((latest.elements ?? []) as LabelElement[]);
    await updateLabelTemplate(code, {
      id: latest.id,
      name: (latest.name ?? latest.templateName ?? "").trim() || latest.id,
      labelType: (latest.labelType ?? "PRICE") as any,
      unit: (latest.unit ?? "inch") as any,
      width: Number(latest.width ?? 2),
      height: Number(latest.height ?? 2),
      appliedLocation: appliedLocationToEditor(latest),
      showRuler: latest.showRuler ?? true,
      showGrid: latest.showGrid ?? true,
      state: latest.state ?? true,
      elements: labelElementsToApiPayload(elements),
      contents: buildTemplateContentsFromElements(elements),
      appliedLocationIds:
        appliedLocationToEditor(latest) === "ALL" ? [] : (latest.appliedLocationIds ?? []),
      templateProductDefaults: mergedDefaults,
    });
  };

  const submit = async () => {
    const inputs: LabelBatchCreateItemInput[] = [];
    if (rows.length > 500) {
      toast.error("Too many rows.", {
        description: "Bulk Add supports up to 500 rows at a time.",
      });
      return;
    }
    for (let i = 0; i < rows.length; i += 1) {
      const input = validateAndBuildInput(rows[i], i);
      if (!input) return;
      inputs.push(input);
    }

    setSaving(true);
    try {
      const result = await batchCreateLabels({
        templateCode: templateCode.trim(),
        saveTemplateProductDefaults: true,
        items: inputs,
      });
      const successCount = Number(result.successCount ?? 0);
      const failCount = Number(result.failCount ?? 0);
      if (failCount > 0) {
        const firstErrors = (result.errors ?? [])
          .slice(0, 3)
          .map((err) => {
            const rowNo = err.rowNumber ? `Row ${err.rowNumber}` : "Row";
            return `${rowNo}: ${err.message || "Failed"}`;
          })
          .join("; ");
        toast.warning("Bulk Add completed with errors.", {
          description: `${successCount} created, ${failCount} failed.${firstErrors ? ` ${firstErrors}` : ""}`,
        });
        if (successCount > 0) onBack();
      } else {
        toast.success("Labels created.", {
          description: `${successCount || inputs.length} label(s) have been created successfully.`,
        });
        onBack();
      }
    } catch (e: any) {
      toast.error("Failed to create labels.", {
        description: e?.message ? String(e.message) : "Please check the rows and try again.",
      });
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="flex h-full min-h-0 flex-col">
      <div className="flex shrink-0 items-center gap-3 border-b bg-white px-4 py-3">
        <Button type="button" variant="outline" className="h-9 gap-2" onClick={onBack} disabled={saving}>
          <ArrowLeft className="h-4 w-4" />
          Back
        </Button>
        <div className="min-w-0 flex-1">
          <div className="text-xs font-semibold uppercase tracking-wide text-gray-500">Bulk Add Labels</div>
          <div className="truncate text-base font-semibold text-gray-900">{selectedTemplateLabel}</div>
          {contextHint ? <div className="truncate text-xs text-gray-500">{contextHint}</div> : null}
        </div>
        <Button type="button" variant="outline" className="h-9 gap-2" onClick={addRow} disabled={saving}>
          <Plus className="h-4 w-4" />
          Add row
        </Button>
        <Button
          type="button"
          className="h-9 bg-blue-600 px-6 text-white hover:bg-blue-700"
          onClick={submit}
          disabled={saving || loading || bulkTemplateDetailLoading}
        >
          {saving ? "Saving..." : "Save"}
        </Button>
      </div>

      <div className="min-h-0 flex-1 overflow-auto bg-gray-50 p-4">
        {loading || bulkTemplateDetailLoading ? (
          <div className="rounded-md border bg-white p-8 text-center text-sm text-gray-500">Loading options...</div>
        ) : (
          <div className="min-w-[1900px] rounded-md border bg-white shadow-sm">
            <Table>
              <TableHeader>
                <TableRow className="bg-gray-50 hover:bg-gray-50">
                  <TableHead className="w-[64px] font-bold text-gray-900">#</TableHead>
                  {scopeAuth.requireCompanySelection ? (
                    <TableHead className="min-w-[180px] font-bold text-gray-900">Company *</TableHead>
                  ) : null}
                  <TableHead className="min-w-[220px] font-bold text-gray-900">Applicable Region *</TableHead>
                  <TableHead className="min-w-[210px] font-bold text-gray-900">Product category *</TableHead>
                  <TableHead className="min-w-[210px] font-bold text-gray-900">Product *</TableHead>
                  <TableHead className="min-w-[220px] font-bold text-gray-900">Label Name *</TableHead>
                  <TableHead className="min-w-[220px] font-bold text-gray-900">Location *</TableHead>
                  <TableHead className="min-w-[210px] font-bold text-gray-900">Label Category *</TableHead>
                  <TableHead className="min-w-[210px] font-bold text-gray-900">Label Type *</TableHead>
                  {bulkTemplateInputColumns.map((col) => (
                    <TableHead
                      key={
                        col.kind === "element"
                          ? `template-${col.el.id}`
                          : `nutrition-${col.parent.id}-${col.spec.subKey}`
                      }
                      className="min-w-[180px] font-bold text-gray-900"
                      title={
                        col.kind === "element"
                          ? col.el.id
                          : `${col.parent.id} / ${col.spec.subKey}`
                      }
                    >
                      {col.kind === "element"
                        ? dataEntryColumnLabel(
                            col.el,
                            (selectedTemplate?.elements ?? []) as LabelElement[],
                          )
                        : col.spec.columnLabel}
                    </TableHead>
                  ))}
                  <TableHead className="w-[110px] font-bold text-gray-900">Enabled</TableHead>
                  <TableHead className="w-[80px] text-center font-bold text-gray-900">Actions</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {rows.map((row, index) => {
                  const effectivePartnerId = effectiveScopePartnerId(
                    scopeAuth.requireCompanySelection,
                    row.partnerId,
                    scopeAuth.fixedPartnerId,
                  );
                  const regionOptions = regionOptionsForPartner(groups, effectivePartnerId);
                  const groupIds = regionNamesToGroupIds(row.regionIds, groups, effectivePartnerId);
                  const regionReady = !!(effectivePartnerId && row.regionIds.length && groupIds.length);
                  const locationsInScope = regionReady
                    ? locationsForRegionGroups(locations, groups, groupIds)
                    : [];
                  const allowedLocIds = new Set(locationsInScope.map((l) => l.id).filter(Boolean));
                  const templateAllowed = selectedTemplate
                    ? templateAppliesToRegion(selectedTemplate, allowedLocIds)
                    : true;
                  const productCategoryOptions = productCategories
                    .filter((c) => productCategoryMatchesRegion(c, allowedLocIds))
                    .map((c) => ({
                      value: (c.id ?? "").trim(),
                      label: toDisplay(c.categoryName ?? c.categoryCode ?? c.id),
                    }))
                    .filter((o) => o.value);
                  const productOptions = products
                    .filter((p) => {
                      if ((p.categoryId ?? "").trim() !== row.productCategoryId.trim()) return false;
                      const linkedLocIds = productLocationMap.get(p.id) ?? p.locationIds ?? [];
                      if (linkedLocIds.length) {
                        return linkedLocIds.some((lid) => allowedLocIds.has(String(lid).trim()));
                      }
                      const productPartnerId = (p.partnerId ?? "").trim();
                      if (productPartnerId && productPartnerId !== effectivePartnerId) return false;
                      const productGroupIds = (p.groupIds ?? []).map((gid) => String(gid).trim()).filter(Boolean);
                      if (productGroupIds.length) return productGroupIds.some((gid) => groupIds.includes(gid));
                      return true;
                    })
                    .map((p) => ({
                      value: p.id,
                      label: toDisplay(p.productName ?? p.productCode ?? p.id),
                    }));
                  const locationOptions = locationsInScope.map((loc) => ({
                    value: loc.id,
                    label: toDisplay(loc.locationName ?? loc.locationCode ?? loc.id),
                  }));
                  const labelCategoryOptions = categories
                    .filter((c) => labelCategoryMatchesRegion(c, allowedLocIds))
                    .map((c) => ({
                      value: c.id,
                      label: toDisplay(c.categoryName ?? c.categoryCode ?? c.id),
                    }));
                  const labelTypeOptions = types
                    .filter((t) => labelTypeMatchesRegion(t, allowedLocIds))
                    .map((t) => ({
                      value: t.id,
                      label: toDisplay(t.typeName ?? t.typeCode ?? t.id),
                    }));
                  const gateHint = scopeAuth.requireCompanySelection
                    ? "Select company and region first"
                    : "Select region first";

                  return (
                    <TableRow key={row.id} className={!templateAllowed && regionReady ? "bg-red-50/50" : ""}>
                      <TableCell className="align-top pt-5 text-sm font-semibold text-gray-700">
                        {index + 1}
                      </TableCell>
                      {scopeAuth.requireCompanySelection ? (
                        <TableCell className="align-top">
                          <SearchableSelect
                            value={row.partnerId}
                            onValueChange={(v) => updateRowPartner(row.id, v)}
                            options={companyOptions}
                            placeholder="Select company"
                            searchPlaceholder="Search company..."
                            emptyText="No companies available."
                            disabled={saving}
                          />
                        </TableCell>
                      ) : null}
                      <TableCell className="align-top">
                        <SearchableMultiSelect
                          values={row.regionIds}
                          onValuesChange={(v) => updateRowRegions(row.id, v)}
                          options={regionOptions}
                          placeholder={effectivePartnerId ? "Select region(s)" : "Select company first"}
                          searchPlaceholder="Search regions..."
                          selectAllRowLabel="Select All"
                          emptyText={effectivePartnerId ? "No regions for this company." : "Select company first."}
                          disabled={saving || !effectivePartnerId}
                        />
                        {!templateAllowed && regionReady ? (
                          <div className="mt-1 text-xs text-red-600">Template is not available in this region.</div>
                        ) : null}
                      </TableCell>
                      <TableCell className="align-top">
                        <SearchableSelect
                          value={row.productCategoryId}
                          onValueChange={(v) => updateRow(row.id, { productCategoryId: v, productId: "" })}
                          options={productCategoryOptions}
                          placeholder={regionReady ? "Select category" : gateHint}
                          searchPlaceholder="Search product category..."
                          emptyText={regionReady ? "No product categories." : gateHint}
                          disabled={saving || !regionReady}
                        />
                      </TableCell>
                      <TableCell className="align-top">
                        <SearchableSelect
                          value={row.productId}
                          onValueChange={(v) => updateRowProduct(row, v, products)}
                          options={productOptions}
                          placeholder={
                            regionReady
                              ? row.productCategoryId.trim()
                                ? "Select product"
                                : "Select category first"
                              : gateHint
                          }
                          searchPlaceholder="Search products..."
                          emptyText={
                            regionReady
                              ? row.productCategoryId.trim()
                                ? "No products in this category."
                                : "Select a product category."
                              : gateHint
                          }
                          disabled={saving || !regionReady || !row.productCategoryId.trim()}
                        />
                      </TableCell>
                      <TableCell className="align-top">
                        <Input
                          className="h-10"
                          value={row.labelName}
                          onChange={(e) => updateRowLabelName(row, e.target.value)}
                          placeholder="e.g. Breakfast label"
                          disabled={saving}
                        />
                      </TableCell>
                      <TableCell className="align-top">
                        <SearchableMultiSelect
                          values={row.locationIds}
                          onValuesChange={(v) => updateRow(row.id, { locationIds: v })}
                          options={locationOptions}
                          placeholder={regionReady ? "Select location(s)" : gateHint}
                          searchPlaceholder="Search location..."
                          selectAllRowLabel="Select All"
                          emptyText={regionReady ? "No locations in this region." : gateHint}
                          disabled={saving || !regionReady}
                        />
                      </TableCell>
                      <TableCell className="align-top">
                        <SearchableSelect
                          value={row.labelCategoryId}
                          onValueChange={(v) => updateRow(row.id, { labelCategoryId: v })}
                          options={labelCategoryOptions}
                          placeholder={regionReady ? "Select category" : gateHint}
                          searchPlaceholder="Search category..."
                          emptyText={regionReady ? "No label categories." : gateHint}
                          disabled={saving || !regionReady}
                        />
                      </TableCell>
                      <TableCell className="align-top">
                        <SearchableSelect
                          value={row.labelTypeId}
                          onValueChange={(v) => updateRowLabelType(row, v, types)}
                          options={labelTypeOptions}
                          placeholder={regionReady ? "Select type" : gateHint}
                          searchPlaceholder="Search type..."
                          emptyText={regionReady ? "No label types." : gateHint}
                          disabled={saving || !regionReady}
                        />
                      </TableCell>
                      {bulkTemplateInputColumns.map((col) => {
                        if (col.kind === "nutrition") {
                          const value = row.nutritionByElementId[col.parent.id]?.[col.spec.subKey] ?? "";
                          return (
                            <TableCell
                              key={`nutrition-${row.id}-${col.parent.id}-${col.spec.subKey}`}
                              className="align-top"
                            >
                              {col.spec.inputType === "checkbox" ? (
                                <Switch
                                  checked={value === "true"}
                                  onCheckedChange={(checked) =>
                                    updateRowNutritionValue(
                                      row.id,
                                      col.parent.id,
                                      col.spec.subKey,
                                      checked ? "true" : "false",
                                    )
                                  }
                                  disabled={saving}
                                />
                              ) : (
                                <Input
                                  className="h-10"
                                  value={value}
                                  onChange={(e) =>
                                    updateRowNutritionValue(
                                      row.id,
                                      col.parent.id,
                                      col.spec.subKey,
                                      e.target.value,
                                    )
                                  }
                                  placeholder={col.spec.columnLabel}
                                  disabled={saving}
                                />
                              )}
                            </TableCell>
                          );
                        }

                        const el = col.el;
                        const templateScanLocked = isTemplateSectionBarcodeOrQrElement(el);
                        const label = dataEntryColumnLabel(
                          el,
                          (selectedTemplate?.elements ?? []) as LabelElement[],
                        );
                        return (
                          <TableCell key={`template-${row.id}-${el.id}`} className="align-top">
                            {templateScanLocked ? (
                              <Input
                                className="h-10 bg-gray-50"
                                readOnly
                                disabled
                                value={row.templateDataValues[el.id] ?? ""}
                                placeholder="From product Code Value"
                              />
                            ) : isDateTimeDataEntryField(el) ? (
                              <div className="flex items-center gap-2">
                                <Select
                                  value={row.templateDateOffsets[el.id]?.unit ?? "Days"}
                                  onValueChange={(unit) =>
                                    updateRowTemplateDateOffset(row.id, el.id, { unit })
                                  }
                                  disabled={saving}
                                >
                                  <SelectTrigger className="h-10 min-w-0 flex-1 bg-white text-sm">
                                    <SelectValue />
                                  </SelectTrigger>
                                  <SelectContent>
                                    {LABEL_FORM_OFFSET_UNITS.map((u) => (
                                      <SelectItem key={u} value={u} className="text-xs">
                                        {u}
                                      </SelectItem>
                                    ))}
                                  </SelectContent>
                                </Select>
                                <Input
                                  type="number"
                                  className="h-10 w-20 shrink-0 bg-white"
                                  value={row.templateDateOffsets[el.id]?.value ?? ""}
                                  onChange={(e) =>
                                    updateRowTemplateDateOffset(row.id, el.id, {
                                      value: e.target.value,
                                    })
                                  }
                                  placeholder="Value"
                                  disabled={saving}
                                />
                              </div>
                            ) : (
                              <Input
                                className="h-10"
                                value={row.templateDataValues[el.id] ?? ""}
                                onChange={(e) => updateRowTemplateText(row.id, el.id, e.target.value)}
                                placeholder={`Enter ${label}`}
                                disabled={saving}
                              />
                            )}
                          </TableCell>
                        );
                      })}
                      <TableCell className="align-top pt-4">
                        <Switch
                          checked={row.state}
                          onCheckedChange={(checked) => updateRow(row.id, { state: checked })}
                          disabled={saving}
                        />
                      </TableCell>
                      <TableCell className="align-top text-center">
                        <Button
                          type="button"
                          variant="ghost"
                          size="icon"
                          className="h-9 w-9 text-red-600 hover:bg-red-50 hover:text-red-700"
                          onClick={() => removeRow(row.id)}
                          disabled={saving || rows.length <= 1}
                          aria-label="Delete row"
                        >
                          <Trash2 className="h-4 w-4" />
                        </Button>
                      </TableCell>
                    </TableRow>
                  );
                })}
              </TableBody>
            </Table>
          </div>
        )}
      </div>
    </div>
  );
}

function CreateLabelDialog({
  open,
  onOpenChange,
  onCreated,
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  onCreated: () => void;
}) {
  const scopeAuth = useCategoryScopeAuth();
  const [scopePartnerId, setScopePartnerId] = useState("");
  const [selectedRegionNames, setSelectedRegionNames] = useState<string[]>([]);
  const prevRegionNamesForResetRef = useRef<string | null>(null);
  const prevPartnerForResetRef = useRef<string | null>(null);

  const effectivePartnerId = useMemo(
    () =>
      effectiveScopePartnerId(
        scopeAuth.requireCompanySelection,
        scopePartnerId,
        scopeAuth.fixedPartnerId,
      ),
    [scopeAuth.requireCompanySelection, scopePartnerId, scopeAuth.fixedPartnerId],
  );

  useEffect(() => {
    if (!open || scopeAuth.requireCompanySelection) return;
    const pid = scopeAuth.fixedPartnerId.trim();
    if (!pid || scopePartnerId === pid) return;
    setScopePartnerId(pid);
  }, [open, scopeAuth.requireCompanySelection, scopeAuth.fixedPartnerId, scopePartnerId]);

  const formReferenceScopeIds = useMemo((): LabelFormReferenceScopeIds | null => {
    const partnerId = effectivePartnerId;
    const names = selectedRegionNames.map((x) => x.trim()).filter(Boolean);
    if (!partnerId || !names.length) return null;
    return { partnerId, regionNames: names };
  }, [effectivePartnerId, selectedRegionNames]);

  const {
    loading: refLoading,
    scopedLoading,
    templates,
    locations,
    groups,
    partners,
    categories,
    types,
    products,
    productCategories,
    productLocationMap,
  } = useLabelFormReferenceData(open, formReferenceScopeIds);
  const [productCatalogCategoryId, setProductCatalogCategoryId] = useState("");
  const [submitting, setSubmitting] = useState(false);
  const [templateLoading, setTemplateLoading] = useState(false);
  const [selectedTemplate, setSelectedTemplate] = useState<LabelTemplateDto | null>(null);
  const [templateDataValues, setTemplateDataValues] = useState<Record<string, string>>({});
  /** 日期/时间类：单位 + 数值（相对当前时间） */
  const [templateDateOffsets, setTemplateDateOffsets] = useState<
    Record<string, { unit: string; value: string }>
  >({});
  /** NUTRITION 元素 id → 子字段（calories、fat、extra:…）手动值 */
  const [nutritionByElementId, setNutritionByElementId] = useState<Record<string, Record<string, string>>>({});
  const [form, setForm] = useState<LabelCreateInput>({
    labelName: "",
    templateCode: "",
    locationIds: [],
    labelCategoryId: "",
    labelTypeId: "",
    productIds: [],
    labelInfoJson: null,
    state: true,
  });

  const resetForm = () => {
    setForm({
      labelName: "",
      templateCode: "",
      locationIds: [],
      labelCategoryId: "",
      labelTypeId: "",
      productIds: [],
      labelInfoJson: null,
      state: true,
    });
    setSelectedTemplate(null);
    setTemplateDataValues({});
    setTemplateDateOffsets({});
    setNutritionByElementId({});
    setProductCatalogCategoryId("");
    setScopePartnerId("");
    setSelectedRegionNames([]);
    prevRegionNamesForResetRef.current = null;
    prevPartnerForResetRef.current = null;
  };

  useEffect(() => {
    if (!open) {
      resetForm();
    }
  }, [open]);

  useEffect(() => {
    if (!effectivePartnerId) {
      setSelectedRegionNames([]);
    }
  }, [effectivePartnerId]);

  const createScopeGroupIds = useMemo(
    () => regionNamesToGroupIds(selectedRegionNames, groups, effectivePartnerId),
    [selectedRegionNames, groups, effectivePartnerId],
  );

  const regionScopeReady = !!(
    effectivePartnerId &&
    selectedRegionNames.length > 0 &&
    createScopeGroupIds.length > 0
  );

  const scopeBootstrapLoading = refLoading || (!scopeAuth.requireCompanySelection && scopeAuth.loadingPartner);

  const scopeGateHint = scopeAuth.requireCompanySelection
    ? "Select company and applicable region(s) first"
    : "Select applicable region(s) first";
  const scopeGateEmpty = scopeAuth.requireCompanySelection
    ? "Select company and applicable region(s)."
    : "Select applicable region(s).";

  const locationsInScope = useMemo(() => {
    if (!regionScopeReady) return [];
    return locationsForRegionGroups(locations, groups, createScopeGroupIds);
  }, [locations, groups, createScopeGroupIds, regionScopeReady]);

  useEffect(() => {
    if (!open) return;
    const cur = selectedRegionNames.join("\u0001");
    const prev = prevRegionNamesForResetRef.current;
    prevRegionNamesForResetRef.current = cur;
    if (prev === null) return;
    if (prev === cur) return;
    setProductCatalogCategoryId("");
    setForm((p) => ({
      ...p,
      templateCode: "",
      locationIds: [],
      labelCategoryId: "",
      labelTypeId: "",
      productIds: [],
    }));
  }, [open, selectedRegionNames]);

  useEffect(() => {
    if (!open) return;
    const cur = scopePartnerId.trim();
    const prev = prevPartnerForResetRef.current;
    prevPartnerForResetRef.current = cur;
    if (prev === null) return;
    if (prev === cur) return;
    setProductCatalogCategoryId("");
    setForm((p) => ({
      ...p,
      templateCode: "",
      locationIds: [],
      labelCategoryId: "",
      labelTypeId: "",
      productIds: [],
    }));
  }, [open, scopePartnerId]);

  useEffect(() => {
    if (!regionScopeReady) return;
    const allowed = new Set(locationsInScope.map((l) => l.id));
    setForm((p) => {
      const next = (p.locationIds ?? []).filter((id) => allowed.has(id));
      if (next.length === (p.locationIds ?? []).length) return p;
      return { ...p, locationIds: next };
    });
  }, [locationsInScope, regionScopeReady]);

  const companySelectOptionsForCreate = useMemo(
    () =>
      [...partners]
        .filter((p) => (p.id ?? "").trim())
      .sort((a, b) =>
          (a.partnerName ?? "").localeCompare(b.partnerName ?? "", undefined, { sensitivity: "base" }),
        )
        .map((p) => ({
          value: (p.id ?? "").trim(),
          label: toDisplay(p.partnerName),
        })),
    [partners],
  );

  const regionOptionsForCreate = useMemo(
    () => regionOptionsForPartner(groups, effectivePartnerId),
    [groups, effectivePartnerId],
  );

  useEffect(() => {
    if (!effectivePartnerId || !selectedRegionNames.length) return;
    const allowed = new Set(regionOptionsForCreate.map((o) => o.value));
    const next = selectedRegionNames.filter((n) => allowed.has(n));
    if (next.length !== selectedRegionNames.length) {
      setSelectedRegionNames(next);
    }
  }, [effectivePartnerId, regionOptionsForCreate, selectedRegionNames]);

  const productCategoriesScoped = useMemo(() => {
    if (!regionScopeReady) return [];
    return productCategories;
  }, [productCategories, regionScopeReady]);

  const productsScoped = useMemo(() => {
    if (!regionScopeReady) return [];
    return products;
  }, [products, regionScopeReady]);

  const templatesScoped = useMemo(() => {
    if (!regionScopeReady) return [];
    return templates;
  }, [templates, regionScopeReady]);

  const labelCategoriesScoped = useMemo(() => {
    if (!regionScopeReady) return [];
    return categories;
  }, [categories, regionScopeReady]);

  const labelTypesScoped = useMemo(() => {
    if (!regionScopeReady) return [];
    return types;
  }, [types, regionScopeReady]);

  const scopedOptionsLoading = regionScopeReady && scopedLoading;

  useEffect(() => {
    if (!open) return;
    const code = form.templateCode.trim();
    if (!code) {
      setSelectedTemplate(null);
      setTemplateDataValues({});
      setTemplateDateOffsets({});
      setNutritionByElementId({});
      return;
    }
    let cancelled = false;
    (async () => {
      setTemplateLoading(true);
      try {
        const tpl = await getLabelTemplate(code);
        if (cancelled) return;
        setSelectedTemplate(tpl);
        const pid = (form.productIds[0] ?? "").trim();
        const ltid = form.labelTypeId.trim();
        const labelTypeName = (() => {
          if (!ltid) return "";
          const t = labelTypesScoped.find((x) => x.id === ltid) ?? types.find((x) => x.id === ltid);
          return (t?.typeName ?? t?.typeCode ?? "").trim();
        })();
        const empty = emptyLabelFormTemplateInputState(tpl);
        if (pid && ltid) {
          const hydrated = hydrateLabelFormInputFromProductDefaults(tpl, pid, ltid);
          setTemplateDataValues(
            resolveLabelFormTemplateTextValues(tpl, pid, ltid, labelTypeName, form.labelName),
          );
          setTemplateDateOffsets({ ...empty.dateOffsets, ...hydrated.dateOffsets });
          setNutritionByElementId({ ...empty.nutritionByElementId, ...hydrated.nutritionByElementId });
        } else {
          setTemplateDataValues(
            resolveLabelFormTemplateTextValues(tpl, pid, ltid, labelTypeName, form.labelName),
          );
          setTemplateDateOffsets(empty.dateOffsets);
          setNutritionByElementId(empty.nutritionByElementId);
        }
      } catch (e: any) {
        if (cancelled) return;
        setSelectedTemplate(null);
        setTemplateDataValues({});
        setTemplateDateOffsets({});
        setNutritionByElementId({});
        toast.error("Failed to load template fields.", {
          description: e?.message ? String(e.message) : "Please select another template.",
        });
      } finally {
        if (!cancelled) setTemplateLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open, form.templateCode]);

  useEffect(() => {
    if (!open || !selectedTemplate) return;
    const pid = (form.productIds[0] ?? "").trim();
    const ltid = form.labelTypeId.trim();
    if (!pid && !ltid) return;
    const labelTypeName = (() => {
      if (!ltid) return "";
      const t = labelTypesScoped.find((x) => x.id === ltid) ?? types.find((x) => x.id === ltid);
      return (t?.typeName ?? t?.typeCode ?? "").trim();
    })();
    if (pid && ltid) {
      const hydrated = hydrateLabelFormInputFromProductDefaults(selectedTemplate, pid, ltid);
      const empty = emptyLabelFormTemplateInputState(selectedTemplate);
      setTemplateDataValues(
        resolveLabelFormTemplateTextValues(
          selectedTemplate,
          pid,
          ltid,
          labelTypeName,
          form.labelName,
        ),
      );
      setTemplateDateOffsets({ ...empty.dateOffsets, ...hydrated.dateOffsets });
      setNutritionByElementId({ ...empty.nutritionByElementId, ...hydrated.nutritionByElementId });
      return;
    }
    const elements = (selectedTemplate.elements ?? []) as LabelElement[];
    setTemplateDataValues((prev) =>
      mergeLabelBindingsIntoTextValues(prev, elements, {
        labelTypeName: ltid ? labelTypeName : undefined,
        labelName: form.labelName.trim() || undefined,
      }),
    );
  }, [open, selectedTemplate, form.productIds, form.labelTypeId, form.labelName, productsScoped, labelTypesScoped, types]);

  const submit = async () => {
    const partnerErr = scopePartnerValidationMessage(
      scopeAuth.requireCompanySelection,
      effectivePartnerId,
    );
    if (partnerErr) {
      toast.error("Validation failed", { description: partnerErr });
      return;
    }
    if (!regionScopeReady) {
      toast.error("Validation failed", {
        description: scopeAuth.requireCompanySelection
          ? "Select company and applicable region(s) before other fields."
          : "Select applicable region(s) before other fields.",
      });
      return;
    }
    const regionPayload = buildLabelRegionScopePayload(
      selectedRegionNames,
      groups,
      effectivePartnerId,
    );
    if (regionPayload.appliedRegionType === "SPECIFIED" && !regionPayload.regionIds.length) {
      toast.error("Validation failed", {
        description: "Select at least one applicable region.",
      });
      return;
    }
    if (scopedOptionsLoading) {
      toast.error("Validation failed", {
        description: "Loading options for this region. Please wait.",
      });
      return;
    }
    const locPayload = buildSpecifiedLocationPayload(
      locations,
      partners,
      groups,
      effectivePartnerId,
      selectedRegionNames,
      form.locationIds,
    );
    if (!locPayload.ok) {
      toast.error("Validation failed", { description: locPayload.message });
      return;
    }
    if (!form.labelName.trim() || !form.templateCode.trim() || !form.labelCategoryId.trim()) {
      toast.error("Validation failed", {
        description: "Fill all required fields and select template, location(s), and category.",
      });
      return;
    }
    if (!productCatalogCategoryId.trim() || form.productIds.length === 0) {
      toast.error("Validation failed", {
        description: "Select a product category and one product.",
      });
      return;
    }

    const saveTemplateDataAfterLabel = async () => {
      const code = form.templateCode.trim();
      if (!code) return;
      if (!selectedTemplate) return;
      const labelTypeId = form.labelTypeId.trim();
      if (!labelTypeId) return;
      const latest = await getLabelTemplate(code);
      const dataEls = getDataEntryElements(latest);
      const hasNutritionRows =
        listNutritionElements((latest.elements ?? []) as LabelElement[]).length > 0;
      if (dataEls.length === 0 && !hasNutritionRows) return;

      const productCodeValue = (() => {
        const pid = form.productIds[0]?.trim();
        if (!pid) return "";
        const p = productsScoped.find((x) => x.id === pid);
        return (p?.codeValue ?? "").trim();
      })();
      const pid = form.productIds[0]?.trim() ?? "";
      const product = productsScoped.find((x) => x.id === pid);
      const labelType =
        labelTypesScoped.find((x) => x.id === labelTypeId) ?? types.find((x) => x.id === labelTypeId);
      const inputDefaultValues = collectTemplateDefaultValuesForSave(
        latest,
        templateDataValues,
        templateDateOffsets,
        nutritionByElementId,
        productCodeValue,
        {
          labelName: form.labelName,
          labelTypeName: labelType?.typeName ?? labelType?.typeCode,
        },
      );
      const defaultsMap = buildTemplateDefaultsMap(latest);
      for (const productId of form.productIds) {
        const key = `${productId}::${labelTypeId}`;
        defaultsMap.set(key, {
          productId,
          labelTypeId,
          defaultValues: { ...inputDefaultValues },
          orderNum: defaultsMap.size + 1,
        });
      }
      const mergedDefaults = Array.from(defaultsMap.values()).map((row, idx) => ({
        ...row,
        orderNum: idx + 1,
      }));
      const elements = sortTemplateElementsForDisplay((latest.elements ?? []) as LabelElement[]);
      await updateLabelTemplate(code, {
        id: latest.id,
        name: (latest.name ?? latest.templateName ?? "").trim() || latest.id,
        labelType: (latest.labelType ?? "PRICE") as any,
        unit: (latest.unit ?? "inch") as any,
        width: Number(latest.width ?? 2),
        height: Number(latest.height ?? 2),
        appliedLocation: appliedLocationToEditor(latest),
        showRuler: latest.showRuler ?? true,
        showGrid: latest.showGrid ?? true,
        state: latest.state ?? true,
        elements: labelElementsToApiPayload(elements),
        contents: buildTemplateContentsFromElements(elements),
        appliedLocationIds:
          appliedLocationToEditor(latest) === "ALL" ? [] : (latest.appliedLocationIds ?? []),
        templateProductDefaults: mergedDefaults,
      });
    };

    setSubmitting(true);
    try {
      await createLabel({
        ...form,
        labelTypeId: form.labelTypeId.trim() || null,
        locationIds: locPayload.locationIds,
        appliedRegionType: regionPayload.appliedRegionType,
        regionIds: regionPayload.regionIds,
        groupIds: regionPayload.regionIds,
      });
      try {
        await saveTemplateDataAfterLabel();
      } catch (e: any) {
        toast.warning("Label created, template data failed.", {
          description: e?.message ? String(e.message) : "Please edit template data manually.",
        });
      }
      toast.success("Label created.", {
        description: "The label has been created successfully.",
      });
      onOpenChange(false);
      onCreated();
    } catch (e: any) {
      toast.error("Failed to create label.", {
        description: e?.message ? String(e.message) : "Please try again.",
      });
    } finally {
      setSubmitting(false);
    }
  };

  const templateOptions = useMemo(() => {
    const base = templatesScoped
      .filter((t) => templateListCode(t))
      .map((t) => ({
        value: templateListCode(t),
        label: templateListLabel(t),
      }));
    const c = form.templateCode.trim();
    if (c && !base.some((o) => o.value === c)) {
      return [{ value: c, label: `${c} (current)` }, ...base];
    }
    return base;
  }, [templatesScoped, form.templateCode]);

  const locationOptions = useMemo(
    () =>
      locationsInScope.map((loc) => ({
        value: loc.id,
        label: toDisplay(loc.locationName ?? loc.locationCode ?? loc.id),
      })),
    [locationsInScope],
  );

  const categoryOptions = useMemo(() => {
    const base = labelCategoriesScoped.map((c) => ({
      value: c.id,
      label: toDisplay(c.categoryName ?? c.categoryCode ?? c.id),
    }));
    const id = form.labelCategoryId.trim();
    if (id && !base.some((o) => o.value === id)) {
      const c0 = categories.find((x) => x.id === id);
      return [
        {
          value: id,
          label: c0 ? toDisplay(c0.categoryName ?? c0.categoryCode ?? id) : `${id} (current)`,
        },
        ...base,
      ];
    }
    return base;
  }, [labelCategoriesScoped, categories, form.labelCategoryId]);

  const typeOptions = useMemo(() => {
    const base = labelTypesScoped.map((ty) => ({
      value: ty.id,
      label: toDisplay(ty.typeName ?? ty.typeCode ?? ty.id),
    }));
    const id = form.labelTypeId.trim();
    if (id && !base.some((o) => o.value === id)) {
      const t0 = types.find((x) => x.id === id);
      return [{ value: id, label: t0 ? toDisplay(t0.typeName ?? t0.typeCode ?? id) : `${id} (current)` }, ...base];
    }
    return base;
  }, [labelTypesScoped, types, form.labelTypeId]);

  const dataEntryElements = useMemo(
    () => getCreateLabelTemplateInputElements(selectedTemplate),
    [selectedTemplate],
  );

  const nutritionFieldBlocks = useMemo(() => {
    if (!selectedTemplate) return [] as Array<{ el: LabelElement; spec: NutritionManualFieldSpec }>;
    const out: Array<{ el: LabelElement; spec: NutritionManualFieldSpec }> = [];
    for (const nel of listNutritionElements((selectedTemplate.elements ?? []) as LabelElement[])) {
      for (const spec of listNutritionManualFieldSpecs(nel)) {
        out.push({ el: nel, spec });
      }
    }
    return out;
  }, [selectedTemplate]);

  const showTemplateInputColumn =
    dataEntryElements.length > 0 || nutritionFieldBlocks.length > 0;

  const selectedProductCodeValue = useMemo(() => {
    const pid = (form.productIds[0] ?? "").trim();
    if (!pid) return "";
    const p = productsScoped.find((x) => x.id === pid);
    return (p?.codeValue ?? "").trim();
  }, [form.productIds, productsScoped]);

  /** template_Barcode / template_QR Code：随所选产品 codeValue 同步，且输入框只读 */
  useEffect(() => {
    if (!open || !selectedTemplate) return;
    const cv = selectedProductCodeValue;
    setTemplateDataValues((prev) => {
      let changed = false;
      const next = { ...prev };
      for (const el of (selectedTemplate.elements ?? []) as LabelElement[]) {
        if (!isTemplateSectionBarcodeOrQrElement(el)) continue;
        if (next[el.id] === cv) continue;
        next[el.id] = cv;
        changed = true;
      }
      return changed ? next : prev;
    });
  }, [open, selectedTemplate, selectedProductCodeValue]);

  /** Label Name 模板控件：随 General Settings 中 Label Name 输入框同步 */
  useEffect(() => {
    if (!open || !selectedTemplate) return;
    const name = form.labelName;
    setTemplateDataValues((prev) => {
      let changed = false;
      const next = { ...prev };
      for (const el of (selectedTemplate.elements ?? []) as LabelElement[]) {
        if (!isTemplateLabelNameElement(el)) continue;
        if (next[el.id] === name) continue;
        next[el.id] = name;
        changed = true;
      }
      return changed ? next : prev;
    });
  }, [open, selectedTemplate, form.labelName]);

  const previewTemplate = useMemo(
    () =>
      buildCreateLabelPreviewTemplate(
        selectedTemplate,
        templateDataValues,
        templateDateOffsets,
        nutritionByElementId,
        selectedProductCodeValue,
      ),
    [
      selectedTemplate,
      templateDataValues,
      templateDateOffsets,
      nutritionByElementId,
      selectedProductCodeValue,
    ],
  );
  const hasTemplateSelected = form.templateCode.trim().length > 0;

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent
        className="flex max-h-[calc(100dvh-2rem)] flex-col overflow-hidden max-w-none gap-4 !top-5 !translate-y-0"
        style={{
          width: hasTemplateSelected ? "calc(100vw - 3rem)" : "min(96vw, 780px)",
          maxWidth: hasTemplateSelected ? "calc(100vw - 3rem)" : "min(96vw, 780px)",
        }}
        onInteractOutside={preventDialogDismissOnOverlay}
        onEscapeKeyDown={preventDialogDismissOnOverlay}
      >
        <DialogHeader className="shrink-0">
          <DialogTitle>Add New Label</DialogTitle>
          <DialogDescription>Enter the details for the new label.</DialogDescription>
        </DialogHeader>

        <div
          className={hasTemplateSelected ? "min-h-0 flex-none overflow-hidden py-2" : "min-h-0 flex-1 overflow-hidden py-2"}
          style={
            hasTemplateSelected
              ? {
                  height: "min(72vh, calc(100dvh - 10.5rem))",
                  maxHeight: "min(72vh, calc(100dvh - 10.5rem))",
                }
              : undefined
          }
        >
          <div
            className="grid h-full min-h-0 min-w-0 gap-3 items-stretch"
            style={
              hasTemplateSelected
                ? showTemplateInputColumn
                  ? {
                      gridTemplateColumns: "minmax(0, 1fr) minmax(0, 12.5rem) minmax(0, 1.55fr)",
                      gridTemplateRows: "minmax(0, 1fr)",
                    }
                  : {
                      gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1.55fr)",
                      gridTemplateRows: "minmax(0, 1fr)",
                    }
                : {
                    gridTemplateColumns: "minmax(0, 1fr)",
                    gridTemplateRows: "minmax(0, 1fr)",
                  }
            }
          >
          <div className="min-h-0 min-w-0 w-full overflow-y-auto overflow-x-hidden overscroll-contain rounded-lg border bg-gray-50 p-4 [scrollbar-gutter:stable]">
            <div className="text-sm font-semibold text-gray-900">General Settings</div>
            <div
              className={`grid gap-3 mt-3 mb-2 ${
                scopeAuth.requireCompanySelection ? "grid-cols-2" : "grid-cols-1"
              }`}
            >
              {scopeAuth.requireCompanySelection ? (
              <div className="space-y-2">
                <Label>Company *</Label>
                <SearchableSelect
                    value={scopePartnerId}
                  onValueChange={(v) => {
                      setScopePartnerId(v);
                      setSelectedRegionNames([]);
                  }}
                  options={companySelectOptionsForCreate}
                  placeholder="Select company"
                  searchPlaceholder="Search company…"
                    emptyText="No companies available."
                    disabled={scopeBootstrapLoading}
                />
              </div>
              ) : null}
              <div className="space-y-2">
                <Label>Applicable Region *</Label>
                <SearchableMultiSelect
                  values={selectedRegionNames}
                  onValuesChange={setSelectedRegionNames}
                  options={regionOptionsForCreate}
                  placeholder={
                    effectivePartnerId
                      ? "Select region(s)…"
                      : scopeAuth.requireCompanySelection
                        ? "Select company first"
                        : "Loading company scope…"
                  }
                  searchPlaceholder="Search regions…"
                  selectAllRowLabel="Select All"
                  emptyText={
                    effectivePartnerId
                      ? "No regions for this company."
                      : scopeAuth.requireCompanySelection
                        ? "Select a company first."
                        : "Your account company is not assigned."
                  }
                  disabled={scopeBootstrapLoading || !effectivePartnerId}
                />
                <p className="text-xs text-gray-500">
                  {effectivePartnerId
                    ? "Select All saves as all regions for this company."
                    : scopeAuth.requireCompanySelection
                      ? "Select a company first."
                      : "Your account company is loading or not assigned."}
                </p>
              </div>
            </div>
            <div className="space-y-2 mt-3 mb-2">
              <ProductSingleSelectByCategoryField
                productCatalogCategoryId={productCatalogCategoryId}
                onProductCatalogCategoryIdChange={setProductCatalogCategoryId}
                productId={form.productIds[0] ?? ""}
                onProductIdChange={(id) =>
                  setForm((p) => ({ ...p, productIds: id.trim() ? [id.trim()] : [] }))
                }
                products={productsScoped}
                productCategories={productCategoriesScoped}
                disabled={scopeBootstrapLoading || !regionScopeReady || scopedOptionsLoading}
              />
            </div>
            <div className="grid grid-cols-2 gap-3 mt-3">
              <div className="space-y-2 col-span-2">
                <Label>Label Name *</Label>
                <Input
                  className="h-10"
                  placeholder="e.g. Breakfast label"
                  value={form.labelName}
                  onChange={(e) => setForm((p) => ({ ...p, labelName: e.target.value }))}
                />
              </div>
              <div className="space-y-2">
                <Label>Label Template *</Label>
                <SearchableSelect
                  value={form.templateCode}
                  onValueChange={(v) => setForm((p) => ({ ...p, templateCode: v }))}
                  options={templateOptions}
                  placeholder={regionScopeReady ? "Select template" : scopeGateHint}
                  searchPlaceholder="Search template…"
                  emptyText={regionScopeReady ? "No templates for this region." : scopeGateEmpty}
                  disabled={scopeBootstrapLoading || !regionScopeReady || scopedOptionsLoading}
                />
              </div>
              <div className="space-y-2">
                <Label>Location *</Label>
                <SearchableMultiSelect
                  values={form.locationIds}
                  onValuesChange={(ids) => setForm((p) => ({ ...p, locationIds: ids }))}
                  options={locationOptions}
                  placeholder={regionScopeReady ? "Select location(s)…" : scopeGateHint}
                  searchPlaceholder="Search location…"
                  selectAllRowLabel="Select All"
                  emptyText={regionScopeReady ? "No locations in this region." : scopeGateEmpty}
                  disabled={scopeBootstrapLoading || !regionScopeReady || scopedOptionsLoading}
                />
                <p className="text-xs text-gray-500">
                  {regionScopeReady
                    ? "Select All applies this label to every location in the selected region(s)."
                    : scopeGateEmpty}
                </p>
              </div>
              <div className="space-y-2">
                <Label>Label Category *</Label>
                <SearchableSelect
                  value={form.labelCategoryId}
                  onValueChange={(v) => setForm((p) => ({ ...p, labelCategoryId: v }))}
                  options={categoryOptions}
                  placeholder={regionScopeReady ? "Select category" : scopeGateHint}
                  searchPlaceholder="Search category…"
                  emptyText={
                    regionScopeReady ? "No label categories for this region." : scopeGateEmpty
                  }
                  disabled={scopeBootstrapLoading || !regionScopeReady || scopedOptionsLoading}
                />
              </div>
              <div className="space-y-2">
                <Label>Label Type</Label>
                <SearchableSelect
                  value={form.labelTypeId}
                  onValueChange={(v) => setForm((p) => ({ ...p, labelTypeId: v }))}
                  options={typeOptions}
                  placeholder={
                    scopedOptionsLoading ? "Loading types…" : regionScopeReady ? "Optional" : scopeGateHint
                  }
                  searchPlaceholder="Search type…"
                  emptyText={
                    regionScopeReady
                      ? scopedOptionsLoading
                        ? "Loading…"
                        : "No types for this region."
                      : scopeGateEmpty
                  }
                  disabled={scopeBootstrapLoading || !regionScopeReady || scopedOptionsLoading}
                />
              </div>
            </div>
            <div
              className="flex items-center justify-between border border-gray-200 rounded-md px-3 bg-white mt-4"
              style={{ height: 40 }}
            >
              <div className="text-sm font-medium text-gray-900">Enabled</div>
              <Switch checked={form.state} onCheckedChange={(checked) => setForm((p) => ({ ...p, state: checked }))} />
            </div>
          </div>

          {hasTemplateSelected && showTemplateInputColumn ? (
            <div className="box-border min-h-0 min-w-0 w-full max-w-full overflow-y-auto overflow-x-hidden overscroll-contain rounded-lg border bg-gray-50 p-4 [scrollbar-gutter:stable]">
            <div className="text-sm font-semibold text-gray-900 mb-3">Template Input Data</div>
            {templateLoading ? (
              <div className="text-sm text-gray-500">Loading template fields...</div>
            ) : !form.templateCode.trim() ? (
              <div className="text-sm text-gray-500">Select template first to load input fields.</div>
            ) : (
              <div className="space-y-3">
                {dataEntryElements.map((el) => {
                  const templateScanLocked = isTemplateSectionBarcodeOrQrElement(el);
                  return (
                  <div key={el.id} className="space-y-1.5 w-full min-w-0">
                    <Label className="block">{dataEntryColumnLabel(el, (selectedTemplate?.elements ?? []) as LabelElement[])}</Label>
                    {templateScanLocked ? (
                      <Input
                        className="h-10 w-full min-w-0 box-border bg-gray-50"
                        readOnly
                        disabled
                        value={selectedProductCodeValue || (templateDataValues[el.id] ?? "")}
                        placeholder={
                          selectedProductCodeValue
                            ? ""
                            : "Select a product to fill from Code Value"
                        }
                      />
                    ) : isDateTimeDataEntryField(el) ? (
                      <div className="flex flex-wrap items-center gap-2 w-full min-w-0">
                        <Select
                          value={templateDateOffsets[el.id]?.unit ?? "Days"}
                          onValueChange={(u) =>
                            setTemplateDateOffsets((prev) => ({
                              ...prev,
                              [el.id]: { unit: u, value: prev[el.id]?.value ?? "" },
                            }))
                          }
                        >
                          <SelectTrigger className="h-10 min-w-0 flex-1 bg-white text-sm">
                            <SelectValue />
                          </SelectTrigger>
                          <SelectContent>
                            {LABEL_FORM_OFFSET_UNITS.map((u) => (
                              <SelectItem key={u} value={u} className="text-xs">
                                {u}
                              </SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                        <Input
                          type="number"
                          className="h-10 w-20 shrink-0 bg-white"
                          value={templateDateOffsets[el.id]?.value ?? ""}
                          onChange={(e) =>
                            setTemplateDateOffsets((prev) => ({
                              ...prev,
                              [el.id]: {
                                unit: prev[el.id]?.unit ?? "Days",
                                value: e.target.value,
                              },
                            }))
                          }
                          placeholder="Value"
                        />
                      </div>
                    ) : (
                      <Input
                        className="h-10 w-full min-w-0 box-border"
                        value={templateDataValues[el.id] ?? ""}
                        onChange={(e) =>
                          setTemplateDataValues((prev) => ({ ...prev, [el.id]: e.target.value }))
                        }
                        placeholder={`Enter ${dataEntryColumnLabel(el, (selectedTemplate?.elements ?? []) as LabelElement[])}`}
                      />
                    )}
                  </div>
                  );
                })}
                {nutritionFieldBlocks.length > 0 ? (
                  <div className="pt-2 mt-2 border-t border-gray-200">
                    <div className="text-xs font-semibold text-gray-700 mb-3">Nutrition Facts</div>
                    {listNutritionElements((selectedTemplate.elements ?? []) as LabelElement[]).map((nel) => (
                      <NutritionManualEntryForm
                        key={nel.id}
                        element={nel}
                        values={nutritionByElementId[nel.id] ?? {}}
                        onFieldChange={(subKey, next) =>
                          setNutritionByElementId((prev) => ({
                            ...prev,
                            [nel.id]: {
                              ...(prev[nel.id] ?? {}),
                              [subKey]: next,
                            },
                          }))
                        }
                        className="space-y-4"
                      />
                    ))}
                  </div>
                ) : null}
                <div className="text-xs text-gray-500 pt-1 w-full min-w-0 break-words">
                  Date/time fields: preview uses the current time as base; leave empty or enter 0 for &quot;now&quot;;
                  other numbers add that offset. Format follows each field&apos;s template setting. On save, values
                  are written for the selected product. Nutrition columns follow the template&apos;s nutrient list;
                  values are saved with the template defaults JSON for printing.
                </div>
              </div>
            )}
          </div>
          ) : null}

          {hasTemplateSelected ? (
            <div className="min-h-0 min-w-0 w-full overflow-y-auto overflow-x-hidden overscroll-contain rounded-lg border bg-gray-50 p-4 [scrollbar-gutter:stable]">
            <div className="text-sm font-semibold text-gray-900 mb-3">Label Preview</div>
            {previewTemplate ? (
              <div className="flex justify-center w-full min-w-0 overflow-hidden">
                <LabelPreviewOnly template={previewTemplate} maxWidth={480} previewAsPrinted />
              </div>
            ) : (
              <div className="text-sm text-gray-500">Select template to preview.</div>
            )}
          </div>
          ) : null}
          </div>
        </div>

        <DialogFooter className="shrink-0">
          <Button variant="outline" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            disabled={submitting || scopeBootstrapLoading || !regionScopeReady || scopedOptionsLoading}
            onClick={submit}
          >
            {submitting ? "Creating…" : "Create"}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function EditLabelDialog({
  open,
  label,
  onOpenChange,
  onUpdated,
}: {
  open: boolean;
  label: LabelDto | null;
  onOpenChange: (open: boolean) => void;
  onUpdated: () => void;
}) {
  const scopeAuth = useCategoryScopeAuth();
  const [scopePartnerId, setScopePartnerId] = useState("");
  const [selectedRegionNames, setSelectedRegionNames] = useState<string[]>([]);
  const [productCatalogCategoryId, setProductCatalogCategoryId] = useState("");
  const [labelTypeIdsInEditRegion, setLabelTypeIdsInEditRegion] = useState<Set<string> | null>(null);
  const [submitting, setSubmitting] = useState(false);
  const [detailLoading, setDetailLoading] = useState(false);
  const [loadedDetail, setLoadedDetail] = useState<LabelDto | null>(null);

  const effectivePartnerId = useMemo(
    () =>
      effectiveScopePartnerId(
        scopeAuth.requireCompanySelection,
        scopePartnerId,
        scopeAuth.fixedPartnerId,
      ),
    [scopeAuth.requireCompanySelection, scopePartnerId, scopeAuth.fixedPartnerId],
  );

  useEffect(() => {
    if (!open || scopeAuth.requireCompanySelection) return;
    const pid = scopeAuth.fixedPartnerId.trim();
    if (!pid || scopePartnerId === pid) return;
    setScopePartnerId(pid);
  }, [open, scopeAuth.requireCompanySelection, scopeAuth.fixedPartnerId, scopePartnerId]);

  const formReferenceScopeIds = useMemo((): LabelFormReferenceScopeIds | null => {
    const partnerId = effectivePartnerId;
    const names = selectedRegionNames.map((x) => x.trim()).filter(Boolean);
    if (!partnerId || !names.length) return null;
    return { partnerId, regionNames: names };
  }, [effectivePartnerId, selectedRegionNames]);

  const {
    loading: refLoading,
    scopedLoading: editScopedLoading,
    templates,
    locations,
    groups,
    partners,
    categories,
    types,
    products,
    productCategories,
    productLocationMap,
  } = useLabelFormReferenceData(open, formReferenceScopeIds);
  const [form, setForm] = useState<LabelUpdateInput>({
    labelName: "",
    templateCode: "",
    locationIds: [],
    labelCategoryId: "",
    labelTypeId: "",
    productIds: [],
    labelInfoJson: null,
    state: true,
  });
  const [templateLoading, setTemplateLoading] = useState(false);
  const [selectedTemplate, setSelectedTemplate] = useState<LabelTemplateDto | null>(null);
  const [templateDataValues, setTemplateDataValues] = useState<Record<string, string>>({});
  const [templateDateOffsets, setTemplateDateOffsets] = useState<
    Record<string, { unit: string; value: string }>
  >({});
  const [nutritionByElementId, setNutritionByElementId] = useState<
    Record<string, Record<string, string>>
  >({});

  useEffect(() => {
    if (!open || !label?.id) return;

    const id = label.id;
    setForm(labelDtoToUpdateForm(label));
    setScopePartnerId("");
    setSelectedRegionNames([]);
    setLoadedDetail(null);

    const ac = new AbortController();
    let cancelled = false;
    setDetailLoading(true);
    (async () => {
      try {
        const detail = await getLabel(id, ac.signal);
        if (cancelled) return;
        setLoadedDetail(detail);
        setForm(labelDtoToUpdateForm(detail));
      } catch (e: any) {
        if (cancelled || e?.name === "AbortError") return;
        toast.error("Failed to load label details.", {
          description: e?.message ? String(e.message) : "Form shows list data only; check network.",
        });
      } finally {
        if (!cancelled) setDetailLoading(false);
      }
    })();

    return () => {
      cancelled = true;
      ac.abort();
    };
  }, [open, label]);

  useEffect(() => {
    if (!open || !loadedDetail || !groups.length) return;
    const scope = hydrateLabelRegionScopeFromDto(loadedDetail, groups, partners, locations);
    if (scope.partnerId) setScopePartnerId(scope.partnerId);
    setSelectedRegionNames(scope.regionNames);
  }, [open, loadedDetail, groups, partners, locations]);

  useEffect(() => {
    if (!open) {
      setProductCatalogCategoryId("");
      return;
    }
    const pid = (form.productIds[0] ?? "").trim();
    if (!pid) {
      setProductCatalogCategoryId("");
      return;
    }
    const local = products.find((x) => x.id === pid);
    if (local?.categoryId) {
      setProductCatalogCategoryId(String(local.categoryId).trim());
      return;
    }
    let cancelled = false;
    (async () => {
      try {
        const dto = await getProduct(pid);
        if (cancelled) return;
        setProductCatalogCategoryId((dto.categoryId ?? "").trim());
      } catch {
        if (!cancelled) setProductCatalogCategoryId("");
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open, form.productIds, products]);

  const editScopeGroupIds = useMemo(
    () => regionNamesToGroupIds(selectedRegionNames, groups, effectivePartnerId),
    [selectedRegionNames, groups, effectivePartnerId],
  );

  const editScopeReady = !!(effectivePartnerId && selectedRegionNames.length > 0 && editScopeGroupIds.length > 0);

  const regionOptionsForEdit = useMemo(
    () => regionOptionsForPartner(groups, effectivePartnerId),
    [groups, effectivePartnerId],
  );

  const companySelectOptionsForEdit = useMemo(
    () =>
      [...partners]
        .filter((p) => (p.id ?? "").trim())
        .sort((a, b) =>
          (a.partnerName ?? "").localeCompare(b.partnerName ?? "", undefined, { sensitivity: "base" }),
        )
        .map((p) => ({
          value: (p.id ?? "").trim(),
          label: toDisplay(p.partnerName),
        })),
    [partners],
  );

  const locationsInEditScope = useMemo(
    () => locationsForRegionGroups(locations, groups, editScopeGroupIds),
    [locations, groups, editScopeGroupIds],
  );

  const allowedLocIdsEdit = useMemo(
    () => new Set(locationsInEditScope.map((l) => l.id).filter(Boolean)),
    [locationsInEditScope],
  );

  useEffect(() => {
    if (!open || !editScopeReady) {
      setLabelTypeIdsInEditRegion(null);
      return;
    }
    let cancelled = false;
    (async () => {
      try {
        const res = await getLabels({ skipCount: 1, maxResultCount: 500 });
        if (cancelled) return;
        const next = new Set<string>();
        for (const row of res.items ?? []) {
          const lid = String(row.locationId ?? "").trim();
          if (!allowedLocIdsEdit.has(lid)) continue;
          const tid = String(row.labelTypeId ?? "").trim();
          if (tid) next.add(tid);
        }
        setLabelTypeIdsInEditRegion(next);
      } catch {
        if (!cancelled) setLabelTypeIdsInEditRegion(new Set());
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open, editScopeReady, editScopeGroupIds.join(","), allowedLocIdsEdit]);

  const productCategoriesScopedEdit = useMemo(() => {
    if (!editScopeReady) return productCategories;
    return productCategories.filter((c) => productCategoryMatchesRegion(c, allowedLocIdsEdit));
  }, [productCategories, allowedLocIdsEdit, editScopeReady]);

  const productsScopedEdit = useMemo(() => {
    if (!editScopeReady) return products;
    return products.filter((p) => {
      const lids = productLocationMap.get(p.id) ?? p.locationIds ?? [];
      return lids.some((id) => allowedLocIdsEdit.has(id));
    });
  }, [products, productLocationMap, allowedLocIdsEdit, editScopeReady]);

  const templatesScopedEdit = useMemo(() => {
    if (!editScopeReady) return templates;
    return templates.filter((t) => templateAppliesToRegion(t, allowedLocIdsEdit));
  }, [templates, allowedLocIdsEdit, editScopeReady]);

  const labelCategoriesScopedEdit = useMemo(() => {
    if (!editScopeReady) return categories;
    return categories.filter((c) => labelCategoryMatchesRegion(c, allowedLocIdsEdit));
  }, [categories, allowedLocIdsEdit, editScopeReady]);

  const labelTypesScopedEdit = useMemo(() => {
    if (!editScopeReady) return types;
    if (labelTypeIdsInEditRegion === null) return types;
    if (labelTypeIdsInEditRegion.size === 0) return types;
    return types.filter((t) => labelTypeIdsInEditRegion.has(t.id));
  }, [types, editScopeReady, labelTypeIdsInEditRegion]);

  const typesLoadingEditScope =
    editScopeReady && (labelTypeIdsInEditRegion === null || editScopedLoading);

  useEffect(() => {
    if (!open) {
      setSelectedTemplate(null);
      setTemplateDataValues({});
      setTemplateDateOffsets({});
      setNutritionByElementId({});
      return;
    }
    const code = form.templateCode.trim();
    if (!code) {
      setSelectedTemplate(null);
      setTemplateDataValues({});
      setTemplateDateOffsets({});
      setNutritionByElementId({});
      return;
    }
    let cancelled = false;
    (async () => {
      setTemplateLoading(true);
      try {
        const tpl = await getLabelTemplate(code);
        if (cancelled) return;
        setSelectedTemplate(tpl);
        const pid = (form.productIds[0] ?? "").trim();
        const ltid = form.labelTypeId.trim();
        const labelTypeName = (() => {
          if (!ltid) return "";
          const t =
            labelTypesScopedEdit.find((x) => x.id === ltid) ?? types.find((x) => x.id === ltid);
          return (t?.typeName ?? t?.typeCode ?? "").trim();
        })();
        const empty = emptyLabelFormTemplateInputState(tpl);
        if (pid && ltid) {
          const hydrated = hydrateLabelFormInputFromProductDefaults(tpl, pid, ltid);
          setTemplateDataValues(
            resolveLabelFormTemplateTextValues(tpl, pid, ltid, labelTypeName, form.labelName),
          );
          setTemplateDateOffsets({ ...empty.dateOffsets, ...hydrated.dateOffsets });
          setNutritionByElementId({ ...empty.nutritionByElementId, ...hydrated.nutritionByElementId });
        } else {
          setTemplateDataValues(
            resolveLabelFormTemplateTextValues(tpl, pid, ltid, labelTypeName, form.labelName),
          );
          setTemplateDateOffsets(empty.dateOffsets);
          setNutritionByElementId(empty.nutritionByElementId);
        }
      } catch (e: any) {
        if (cancelled) return;
        setSelectedTemplate(null);
        setTemplateDataValues({});
        setTemplateDateOffsets({});
        setNutritionByElementId({});
        toast.error("Failed to load template fields.", {
          description: e?.message ? String(e.message) : "Please select another template.",
        });
      } finally {
        if (!cancelled) setTemplateLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open, form.templateCode, form.productIds, form.labelTypeId, productsScopedEdit, labelTypesScopedEdit, types]);

  useEffect(() => {
    if (!open || !selectedTemplate) return;
    const pid = (form.productIds[0] ?? "").trim();
    const ltid = form.labelTypeId.trim();
    if (!pid && !ltid) return;
    const labelTypeName = (() => {
      if (!ltid) return "";
      const t =
        labelTypesScopedEdit.find((x) => x.id === ltid) ?? types.find((x) => x.id === ltid);
      return (t?.typeName ?? t?.typeCode ?? "").trim();
    })();
    if (pid && ltid) {
      const hydrated = hydrateLabelFormInputFromProductDefaults(selectedTemplate, pid, ltid);
      const empty = emptyLabelFormTemplateInputState(selectedTemplate);
      setTemplateDataValues(
        resolveLabelFormTemplateTextValues(
          selectedTemplate,
          pid,
          ltid,
          labelTypeName,
          form.labelName,
        ),
      );
      setTemplateDateOffsets({ ...empty.dateOffsets, ...hydrated.dateOffsets });
      setNutritionByElementId({ ...empty.nutritionByElementId, ...hydrated.nutritionByElementId });
      return;
    }
    const elements = (selectedTemplate.elements ?? []) as LabelElement[];
    setTemplateDataValues((prev) =>
      mergeLabelBindingsIntoTextValues(prev, elements, {
        labelTypeName: ltid ? labelTypeName : undefined,
        labelName: form.labelName.trim() || undefined,
      }),
    );
  }, [
    open,
    selectedTemplate,
    form.productIds,
    form.labelTypeId,
    form.labelName,
    productsScopedEdit,
    labelTypesScopedEdit,
    types,
  ]);

  const submit = async () => {
    if (!label?.id) return;
    const partnerErr = scopePartnerValidationMessage(
      scopeAuth.requireCompanySelection,
      effectivePartnerId,
    );
    if (partnerErr) {
      toast.error("Validation failed", { description: partnerErr });
      return;
    }
    if (!editScopeReady) {
      toast.error("Validation failed", {
        description: scopeAuth.requireCompanySelection
          ? "Select company and applicable region(s)."
          : "Select applicable region(s).",
      });
      return;
    }
    const regionPayload = buildLabelRegionScopePayload(
      selectedRegionNames,
      groups,
      effectivePartnerId,
    );
    if (regionPayload.appliedRegionType === "SPECIFIED" && !regionPayload.regionIds.length) {
      toast.error("Validation failed", {
        description: "Select at least one applicable region.",
      });
      return;
    }
    if (editScopeReady && typesLoadingEditScope) {
      toast.error("Validation failed", {
        description: "Loading label types for this region. Please wait.",
      });
      return;
    }
    const locPayload = buildSpecifiedLocationPayload(
      locations,
      partners,
      groups,
      effectivePartnerId,
      selectedRegionNames,
      form.locationIds,
    );
    if (!locPayload.ok) {
      toast.error("Validation failed", { description: locPayload.message });
      return;
    }
    if (!form.labelName.trim() || !form.templateCode.trim() || !form.labelCategoryId.trim() || !form.labelTypeId.trim()) {
      toast.error("Validation failed", {
        description: "Fill all required fields and select template, location(s), category, and type.",
      });
      return;
    }
    if (!productCatalogCategoryId.trim() || form.productIds.length === 0) {
      toast.error("Validation failed", {
        description: "Select a product category and one product.",
      });
      return;
    }

    const saveTemplateDataAfterLabel = async () => {
      const code = form.templateCode.trim();
      if (!code || !selectedTemplate) return;
      const labelTypeId = form.labelTypeId.trim();
      if (!labelTypeId) return;
      const latest = await getLabelTemplate(code);
      const dataEls = getDataEntryElements(latest);
      const hasNutritionRows =
        listNutritionElements((latest.elements ?? []) as LabelElement[]).length > 0;
      if (dataEls.length === 0 && !hasNutritionRows) return;

      const productCodeValue = (() => {
        const pid = form.productIds[0]?.trim();
        if (!pid) return "";
        const p = productsScopedEdit.find((x) => x.id === pid);
        return (p?.codeValue ?? "").trim();
      })();
      const pid = form.productIds[0]?.trim() ?? "";
      const product = productsScopedEdit.find((x) => x.id === pid);
      const labelType =
        labelTypesScopedEdit.find((x) => x.id === labelTypeId) ??
        types.find((x) => x.id === labelTypeId);
      const inputDefaultValues = collectTemplateDefaultValuesForSave(
        latest,
        templateDataValues,
        templateDateOffsets,
        nutritionByElementId,
        productCodeValue,
        {
          labelName: form.labelName,
          labelTypeName: labelType?.typeName ?? labelType?.typeCode,
        },
      );
      const defaultsMap = buildTemplateDefaultsMap(latest);
      for (const productId of form.productIds) {
        const key = `${productId}::${labelTypeId}`;
        defaultsMap.set(key, {
          productId,
          labelTypeId,
          defaultValues: { ...inputDefaultValues },
          orderNum: defaultsMap.size + 1,
        });
      }
      const mergedDefaults = Array.from(defaultsMap.values()).map((row, idx) => ({
        ...row,
        orderNum: idx + 1,
      }));
      const elements = sortTemplateElementsForDisplay((latest.elements ?? []) as LabelElement[]);
      await updateLabelTemplate(code, {
        id: latest.id,
        name: (latest.name ?? latest.templateName ?? "").trim() || latest.id,
        labelType: (latest.labelType ?? "PRICE") as any,
        unit: (latest.unit ?? "inch") as any,
        width: Number(latest.width ?? 2),
        height: Number(latest.height ?? 2),
        appliedLocation: appliedLocationToEditor(latest),
        showRuler: latest.showRuler ?? true,
        showGrid: latest.showGrid ?? true,
        state: latest.state ?? true,
        elements: labelElementsToApiPayload(elements),
        contents: buildTemplateContentsFromElements(elements),
        appliedLocationIds:
          appliedLocationToEditor(latest) === "ALL" ? [] : (latest.appliedLocationIds ?? []),
        templateProductDefaults: mergedDefaults,
      });
    };

    setSubmitting(true);
    try {
      await updateLabel(label.id, {
        ...form,
        locationIds: locPayload.locationIds,
        appliedRegionType: regionPayload.appliedRegionType,
        regionIds: regionPayload.regionIds,
        groupIds: regionPayload.regionIds,
      });
      try {
        await saveTemplateDataAfterLabel();
      } catch (e: any) {
        toast.warning("Label updated, template data failed.", {
          description: e?.message ? String(e.message) : "Please edit template data manually.",
        });
      }
      toast.success("Label updated.", {
        description: "The label has been updated successfully.",
      });
      onOpenChange(false);
      onUpdated();
    } catch (e: any) {
      toast.error("Failed to update label.", {
        description: e?.message ? String(e.message) : "Please try again.",
      });
    } finally {
      setSubmitting(false);
    }
  };

  const editTemplateOptions = useMemo(() => {
    const base = templatesScopedEdit
      .filter((t) => templateListCode(t))
      .map((t) => ({
        value: templateListCode(t),
        label: templateListLabel(t),
      }));
    const c = form.templateCode;
    if (c && !base.some((o) => o.value === c)) {
      return [{ value: c, label: `${c} (current)` }, ...base];
    }
    return base;
  }, [templatesScopedEdit, form.templateCode]);

  const editLocationOptions = useMemo(() => {
    const src = editScopeReady ? locationsInEditScope : locations;
    const base = src.map((loc) => ({
      value: loc.id,
      label: toDisplay(loc.locationName ?? loc.locationCode ?? loc.id),
    }));
    const missing = (form.locationIds ?? []).filter((id) => id && !base.some((o) => o.value === id));
    const extras = missing.map((id) => ({ value: id, label: `${id} (current)` }));
    return extras.length ? [...extras, ...base] : base;
  }, [editScopeReady, locationsInEditScope, locations, form.locationIds]);

  useEffect(() => {
    if (!editScopeReady) return;
    const allowed = new Set(locationsInEditScope.map((l) => l.id));
    setForm((p) => {
      const next = (p.locationIds ?? []).filter((id) => allowed.has(id));
      if (next.length === (p.locationIds ?? []).length) return p;
      return { ...p, locationIds: next };
    });
  }, [locationsInEditScope, editScopeReady]);

  const editCategoryOptions = useMemo(() => {
    const base = labelCategoriesScopedEdit.map((c) => ({
      value: c.id,
      label: toDisplay(c.categoryName ?? c.categoryCode ?? c.id),
    }));
    const id = form.labelCategoryId;
    if (id && !base.some((o) => o.value === id)) {
      return [{ value: id, label: `${id} (current)` }, ...base];
    }
    return base;
  }, [labelCategoriesScopedEdit, form.labelCategoryId]);

  const editTypeOptions = useMemo(() => {
    const base = labelTypesScopedEdit.map((ty) => ({
      value: ty.id,
      label: toDisplay(ty.typeName ?? ty.typeCode ?? ty.id),
    }));
    const id = form.labelTypeId;
    if (id && !base.some((o) => o.value === id)) {
      return [{ value: id, label: `${id} (current)` }, ...base];
    }
    return base;
  }, [labelTypesScopedEdit, form.labelTypeId]);

  const editDataEntryElements = useMemo(
    () => getCreateLabelTemplateInputElements(selectedTemplate),
    [selectedTemplate],
  );

  const editNutritionFieldBlocks = useMemo(() => {
    if (!selectedTemplate) return [] as Array<{ el: LabelElement; spec: NutritionManualFieldSpec }>;
    const out: Array<{ el: LabelElement; spec: NutritionManualFieldSpec }> = [];
    for (const nel of listNutritionElements((selectedTemplate.elements ?? []) as LabelElement[])) {
      for (const spec of listNutritionManualFieldSpecs(nel)) {
        out.push({ el: nel, spec });
      }
    }
    return out;
  }, [selectedTemplate]);

  const editShowTemplateInputColumn =
    editDataEntryElements.length > 0 || editNutritionFieldBlocks.length > 0;

  const editSelectedProductCodeValue = useMemo(() => {
    const pid = (form.productIds[0] ?? "").trim();
    if (!pid) return "";
    const p = productsScopedEdit.find((x) => x.id === pid);
    return (p?.codeValue ?? "").trim();
  }, [form.productIds, productsScopedEdit]);

  useEffect(() => {
    if (!open || !selectedTemplate) return;
    const cv = editSelectedProductCodeValue;
    setTemplateDataValues((prev) => {
      let changed = false;
      const next = { ...prev };
      for (const el of (selectedTemplate.elements ?? []) as LabelElement[]) {
        if (!isTemplateSectionBarcodeOrQrElement(el)) continue;
        if (next[el.id] === cv) continue;
        next[el.id] = cv;
        changed = true;
      }
      return changed ? next : prev;
    });
  }, [open, selectedTemplate, editSelectedProductCodeValue]);

  /** Label Name 模板控件：随 General Settings 中 Label Name 输入框同步 */
  useEffect(() => {
    if (!open || !selectedTemplate) return;
    const name = form.labelName;
    setTemplateDataValues((prev) => {
      let changed = false;
      const next = { ...prev };
      for (const el of (selectedTemplate.elements ?? []) as LabelElement[]) {
        if (!isTemplateLabelNameElement(el)) continue;
        if (next[el.id] === name) continue;
        next[el.id] = name;
        changed = true;
      }
      return changed ? next : prev;
    });
  }, [open, selectedTemplate, form.labelName]);

  const editPreviewTemplate = useMemo(
    () =>
      buildCreateLabelPreviewTemplate(
        selectedTemplate,
        templateDataValues,
        templateDateOffsets,
        nutritionByElementId,
        editSelectedProductCodeValue,
      ),
    [
      selectedTemplate,
      templateDataValues,
      templateDateOffsets,
      nutritionByElementId,
      editSelectedProductCodeValue,
    ],
  );

  const editHasTemplateSelected = form.templateCode.trim().length > 0;

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent
        className="flex max-h-[calc(100dvh-2rem)] flex-col overflow-hidden max-w-none gap-4 !top-5 !translate-y-0"
        style={{
          width: editHasTemplateSelected ? "calc(100vw - 3rem)" : "min(96vw, 780px)",
          maxWidth: editHasTemplateSelected ? "calc(100vw - 3rem)" : "min(96vw, 780px)",
        }}
        onInteractOutside={preventDialogDismissOnOverlay}
        onEscapeKeyDown={preventDialogDismissOnOverlay}
      >
        <DialogHeader className="shrink-0">
          <DialogTitle>Edit Label</DialogTitle>
          <DialogDescription>
            {detailLoading ? "Loading label details…" : "Update the label details."}
          </DialogDescription>
        </DialogHeader>

        <div
          className={
            editHasTemplateSelected ? "min-h-0 flex-none overflow-hidden py-2" : "min-h-0 flex-1 overflow-hidden py-2"
          }
          style={
            editHasTemplateSelected
              ? {
                  height: "min(72vh, calc(100dvh - 10.5rem))",
                  maxHeight: "min(72vh, calc(100dvh - 10.5rem))",
                }
              : undefined
          }
        >
          <div
            className="grid h-full min-h-0 min-w-0 gap-3 items-stretch"
            style={
              editHasTemplateSelected
                ? editShowTemplateInputColumn
                  ? {
                      gridTemplateColumns: "minmax(0, 1fr) minmax(0, 12.5rem) minmax(0, 1.55fr)",
                      gridTemplateRows: "minmax(0, 1fr)",
                    }
                  : {
                      gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1.55fr)",
                      gridTemplateRows: "minmax(0, 1fr)",
                    }
                : {
                    gridTemplateColumns: "minmax(0, 1fr)",
                    gridTemplateRows: "minmax(0, 1fr)",
                  }
            }
          >
          <div className="min-h-0 min-w-0 w-full overflow-y-auto overflow-x-hidden overscroll-contain rounded-lg border bg-gray-50 p-4 [scrollbar-gutter:stable]">
            <div className="text-sm font-semibold text-gray-900">General Settings</div>
        <div className="grid gap-4 mt-3">
          <div
            className={`grid gap-4 ${
              scopeAuth.requireCompanySelection ? "grid-cols-2" : "grid-cols-1"
            }`}
          >
            {scopeAuth.requireCompanySelection ? (
            <div className="space-y-2">
                <Label>Company *</Label>
                <SearchableSelect
                  value={scopePartnerId}
                  onValueChange={(v) => {
                    setScopePartnerId(v);
                    setSelectedRegionNames([]);
                  }}
                  options={companySelectOptionsForEdit}
                  placeholder="Select company"
                  searchPlaceholder="Search company…"
                  emptyText="No companies available."
                  disabled={refLoading || detailLoading}
              />
            </div>
            ) : null}
            <div className="space-y-2">
              <Label>Applicable Region *</Label>
              <SearchableMultiSelect
                values={selectedRegionNames}
                onValuesChange={setSelectedRegionNames}
                options={regionOptionsForEdit}
                placeholder={
                  effectivePartnerId
                    ? "Select region(s)…"
                    : scopeAuth.requireCompanySelection
                      ? "Select company first"
                      : "Loading company scope…"
                }
                searchPlaceholder="Search regions…"
                selectAllRowLabel="Select All"
                emptyText={
                  effectivePartnerId
                    ? "No regions for this company."
                    : scopeAuth.requireCompanySelection
                      ? "Select a company first."
                      : "Your account company is not assigned."
                }
                disabled={refLoading || detailLoading || !effectivePartnerId}
              />
            </div>
          </div>
          <div className="space-y-2">
            <Label>Label Name *</Label>
            <Input
              className="h-10"
              placeholder="e.g. Breakfast label"
              value={form.labelName}
              onChange={(e) => setForm((p) => ({ ...p, labelName: e.target.value }))}
              disabled={detailLoading}
            />
          </div>

          <div className="grid grid-cols-2 gap-4">
            <div className="space-y-2">
              <Label>Label Template *</Label>
              <SearchableSelect
                value={form.templateCode}
                onValueChange={(v) => setForm((p) => ({ ...p, templateCode: v }))}
                options={editTemplateOptions}
                placeholder="Select template"
                searchPlaceholder="Search template…"
                emptyText={editScopeReady ? "No templates for this region." : "No templates found."}
                disabled={refLoading || detailLoading || !editScopeReady || editScopedLoading}
              />
            </div>
            <div className="space-y-2">
              <Label>Location *</Label>
              <SearchableMultiSelect
                values={form.locationIds}
                onValuesChange={(ids) => setForm((p) => ({ ...p, locationIds: ids }))}
                options={editLocationOptions}
                placeholder={editScopeReady ? "Select location(s)…" : "Select applicable region(s) first"}
                searchPlaceholder="Search location…"
                selectAllRowLabel="Select All"
                emptyText={
                  editScopeReady ? "No locations in selected regions." : "Select applicable region(s) first."
                }
                disabled={refLoading || detailLoading || !editScopeReady || editScopedLoading}
              />
              <p className="text-xs text-gray-500">
                {editScopeReady
                  ? "Select All applies this label to every location in the selected region(s)."
                  : "Select applicable region(s) first."}
              </p>
            </div>
          </div>

          <div className="grid grid-cols-2 gap-4">
            <div className="space-y-2">
              <Label>Label Category *</Label>
              <SearchableSelect
                value={form.labelCategoryId}
                onValueChange={(v) => setForm((p) => ({ ...p, labelCategoryId: v }))}
                options={editCategoryOptions}
                placeholder="Select category"
                searchPlaceholder="Search category…"
                emptyText={
                  editScopeReady ? "No label categories for this region." : "No categories found."
                }
                disabled={refLoading || detailLoading || !editScopeReady || editScopedLoading}
              />
            </div>
            <div className="space-y-2">
              <Label>Label Type *</Label>
              <SearchableSelect
                value={form.labelTypeId}
                onValueChange={(v) => setForm((p) => ({ ...p, labelTypeId: v }))}
                options={editTypeOptions}
                placeholder={typesLoadingEditScope ? "Loading types…" : "Select type"}
                searchPlaceholder="Search type…"
                emptyText={
                  editScopeReady
                    ? typesLoadingEditScope
                      ? "Loading…"
                      : "No types for this region."
                    : "No types found."
                }
                disabled={refLoading || detailLoading || typesLoadingEditScope}
              />
            </div>
          </div>

          <div className="space-y-2">
            <ProductSingleSelectByCategoryField
              productCatalogCategoryId={productCatalogCategoryId}
              onProductCatalogCategoryIdChange={setProductCatalogCategoryId}
              productId={form.productIds[0] ?? ""}
              onProductIdChange={(id) =>
                setForm((p) => ({ ...p, productIds: id.trim() ? [id.trim()] : [] }))
              }
              products={productsScopedEdit}
              productCategories={productCategoriesScopedEdit}
              disabled={refLoading || detailLoading || !editScopeReady || editScopedLoading}
            />
          </div>

          <div
            className="flex items-center justify-between border border-gray-200 rounded-md px-3 bg-white"
            style={{ height: 40 }}
          >
            <div className="text-sm font-medium text-gray-900">Enabled</div>
            <Switch
              checked={form.state}
              onCheckedChange={(checked) => setForm((p) => ({ ...p, state: checked }))}
              disabled={detailLoading}
            />
          </div>
          </div>
        </div>

          {editHasTemplateSelected && editShowTemplateInputColumn ? (
            <div className="box-border min-h-0 min-w-0 w-full max-w-full overflow-y-auto overflow-x-hidden overscroll-contain rounded-lg border bg-gray-50 p-4 [scrollbar-gutter:stable]">
              <div className="text-sm font-semibold text-gray-900 mb-3">Template Input Data</div>
              {templateLoading ? (
                <div className="text-sm text-gray-500">Loading template fields...</div>
              ) : !form.templateCode.trim() ? (
                <div className="text-sm text-gray-500">Select template first to load input fields.</div>
              ) : (
                <div className="space-y-3">
                  {editDataEntryElements.map((el) => {
                    const templateScanLocked = isTemplateSectionBarcodeOrQrElement(el);
                    return (
                      <div key={el.id} className="space-y-1.5 w-full min-w-0">
                        <Label className="block">{dataEntryColumnLabel(el, (selectedTemplate?.elements ?? []) as LabelElement[])}</Label>
                        {templateScanLocked ? (
                          <Input
                            className="h-10 w-full min-w-0 box-border bg-gray-50"
                            readOnly
                            disabled
                            value={editSelectedProductCodeValue || (templateDataValues[el.id] ?? "")}
                            placeholder={
                              editSelectedProductCodeValue
                                ? ""
                                : "Select a product to fill from Code Value"
                            }
                          />
                        ) : isDateTimeDataEntryField(el) ? (
                          <div className="flex flex-wrap items-center gap-2 w-full min-w-0">
                            <Select
                              value={templateDateOffsets[el.id]?.unit ?? "Days"}
                              onValueChange={(u) =>
                                setTemplateDateOffsets((prev) => ({
                                  ...prev,
                                  [el.id]: { unit: u, value: prev[el.id]?.value ?? "" },
                                }))
                              }
                            >
                              <SelectTrigger className="h-10 min-w-0 flex-1 bg-white text-sm">
                                <SelectValue />
                              </SelectTrigger>
                              <SelectContent>
                                {LABEL_FORM_OFFSET_UNITS.map((u) => (
                                  <SelectItem key={u} value={u} className="text-xs">
                                    {u}
                                  </SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                            <Input
                              type="number"
                              className="h-10 w-20 shrink-0 bg-white"
                              value={templateDateOffsets[el.id]?.value ?? ""}
                              onChange={(e) =>
                                setTemplateDateOffsets((prev) => ({
                                  ...prev,
                                  [el.id]: {
                                    unit: prev[el.id]?.unit ?? "Days",
                                    value: e.target.value,
                                  },
                                }))
                              }
                              placeholder="Value"
                            />
                          </div>
                        ) : (
                          <Input
                            className="h-10 w-full min-w-0 box-border"
                            value={templateDataValues[el.id] ?? ""}
                            onChange={(e) =>
                              setTemplateDataValues((prev) => ({ ...prev, [el.id]: e.target.value }))
                            }
                            placeholder={`Enter ${dataEntryColumnLabel(el, (selectedTemplate?.elements ?? []) as LabelElement[])}`}
                          />
                        )}
                      </div>
                    );
                  })}
                  {editNutritionFieldBlocks.length > 0 ? (
                    <div className="pt-2 mt-2 border-t border-gray-200">
                      <div className="text-xs font-semibold text-gray-700 mb-3">Nutrition Facts</div>
                      {listNutritionElements((selectedTemplate.elements ?? []) as LabelElement[]).map((nel) => (
                        <NutritionManualEntryForm
                          key={nel.id}
                          element={nel}
                          values={nutritionByElementId[nel.id] ?? {}}
                          onFieldChange={(subKey, next) =>
                            setNutritionByElementId((prev) => ({
                              ...prev,
                              [nel.id]: {
                                ...(prev[nel.id] ?? {}),
                                [subKey]: next,
                              },
                            }))
                          }
                          className="space-y-4"
                        />
                      ))}
                    </div>
                  ) : null}
                  <div className="text-xs text-gray-500 pt-1 w-full min-w-0 break-words">
                    Values shown are for the selected product and label type on this label. Date/time fields
                    preview from the current time plus offset. Changes are saved to the template defaults on
                    Update.
                  </div>
                </div>
              )}
            </div>
          ) : null}

          {editHasTemplateSelected ? (
            <div className="min-h-0 min-w-0 w-full overflow-y-auto overflow-x-hidden overscroll-contain rounded-lg border bg-gray-50 p-4 [scrollbar-gutter:stable]">
              <div className="text-sm font-semibold text-gray-900 mb-3">Label Preview</div>
              {editPreviewTemplate ? (
                <div className="flex justify-center w-full min-w-0 overflow-hidden">
                  <LabelPreviewOnly template={editPreviewTemplate} maxWidth={480} previewAsPrinted />
                </div>
              ) : (
                <div className="text-sm text-gray-500">Select template to preview.</div>
              )}
            </div>
          ) : null}
          </div>
        </div>

        <DialogFooter className="shrink-0">
          <Button variant="outline" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            disabled={
              submitting ||
              refLoading ||
              detailLoading ||
              typesLoadingEditScope ||
              !editScopeReady
            }
            onClick={submit}
          >
            {submitting ? "Updating…" : "Update"}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function DeleteLabelDialog({
  open,
  label,
  onOpenChange,
  onDeleted,
}: {
  open: boolean;
  label: LabelDto | null;
  onOpenChange: (open: boolean) => void;
  onDeleted: () => void;
}) {
  const [submitting, setSubmitting] = useState(false);

  const name = useMemo(() => {
    const n = (label?.labelName ?? "").trim();
    return n || label?.labelCode || label?.id || "this label";
  }, [label]);

  const submit = async () => {
    if (!label?.id) return;
    setSubmitting(true);
    try {
      await deleteLabel(label.id);
      toast.success("Label deleted.", {
        description: "The label has been removed successfully.",
      });
      onOpenChange(false);
      onDeleted();
    } catch (e: any) {
      toast.error("Failed to delete label.", {
        description: e?.message ? String(e.message) : "Please try again.",
      });
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-none" style={{ width: "30%" }}>
        <DialogHeader>
          <DialogTitle>Delete Label</DialogTitle>
          <DialogDescription>This action cannot be undone.</DialogDescription>
        </DialogHeader>

        <div className="text-sm text-gray-700">
          Are you sure you want to delete <span className="font-medium">{name}</span>?
        </div>

        <DialogFooter className="flex-row flex-wrap justify-end">
          <Button className="min-w-24" variant="outline" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            className="min-w-24 gap-2"
            variant="destructive"
            disabled={submitting}
            onClick={submit}
          >
            <Trash2 className="h-4 w-4 shrink-0" />
            {submitting ? "Deleting..." : "Delete"}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
