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 { Plus, Edit, MoreHorizontal, Trash2 } from "lucide-react";
import { toast } from "sonner";
import { skipCountForPage } from "../../lib/paginationQuery";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import { SearchableSelect } from "../ui/searchable-select";
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "../ui/pagination";
import { getLabels, getLabel, createLabel, updateLabel, deleteLabel } from "../../services/labelService";
import type { LabelDto, LabelCreateInput, LabelUpdateInput } from "../../types/label";
import { getLocations } from "../../services/locationService";
import { getGroups } from "../../services/groupService";
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 { 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 { LabelTemplateDataEntryView } from "./LabelTemplateDataEntryView";
import { LabelPreviewOnly } from "./LabelTemplateEditor/LabelCanvas";
import {
  appliedLocationToEditor,
  canonicalElementType,
  dataEntryColumnLabel,
  isDataEntryTableColumnElement,
  isDateTimeDataEntryField,
  labelElementsToApiPayload,
  sortTemplateElementsForDisplay,
} from "../../types/labelTemplate";
import {
  applyOffsetToDate,
  formatDateByPreset,
  LABEL_FORM_OFFSET_UNITS,
  normalizeLabelFormOffsetInput,
  serializePrintInputOffset,
} from "../../lib/labelFormDatePreview";
import {
  listNutritionElements,
  listNutritionManualFieldSpecs,
  mergeNutritionManualIntoConfig,
  nutritionDefaultValuesJsonForSave,
  nutritionManualValuesFromTemplateConfig,
  type NutritionManualFieldSpec,
} from "../../lib/nutritionManualEntry";
import {
  applyProductCodeValueToLabelElements,
  buildTemplateBarcodeQrDefaultsFromCodeValue,
  isTemplateSectionBarcodeOrQrElement,
} from "../../lib/productCodeValueTemplate";

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

/** 列表行：产品列（接口可能返回 `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 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 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 ?? "",
    locationId: d.locationId ?? "",
    labelCategoryId: d.labelCategoryId ?? "",
    labelTypeId: d.labelTypeId ?? "",
    /** 编辑表单仅支持单商品：多商品时取第一个 */
    productIds: arr.length ? [arr[0]] : [],
    labelInfoJson: d.labelInfoJson ?? null,
    state: d.state ?? true,
  };
}
function getDataEntryElements(template: LabelTemplateDto | null): LabelElement[] {
  if (!template) return [];
  return sortTemplateElementsForDisplay((template.elements ?? []) as LabelElement[]).filter(
    isDataEntryTableColumnElement,
  );
}

/** 新建标签弹窗「Template Input Data」：Label 可编辑列 + Template 条码/二维码只读列 */
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 (!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) {
    if (!isDataEntryTableColumnElement(el)) continue;
    const cfg = { ...(el.config as Record<string, unknown>) };
    delete cfg.__previewFormatted;
    const id = el.id;
    const type = canonicalElementType(el.type);
    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.__previewFormatted = "";
      } else {
        const amount = norm.kind === "zero" ? 0 : norm.amount;
        const d = applyOffsetToDate(now, amount, unit);
        if (type === "DATE") {
          const it = String(cfg.inputType ?? cfg.InputType ?? "").toLowerCase();
          const format =
            (typeof cfg.format === "string" && cfg.format.trim()
              ? cfg.format
              : typeof cfg.Format === "string" && cfg.Format.trim()
                ? cfg.Format
                : it === "datetime"
                  ? "YYYY-MM-DD HH:mm"
                  : "DD/MM/YYYY") ?? "DD/MM/YYYY";
          cfg.__previewFormatted = formatDateByPreset(format, d);
        } else if (type === "TIME") {
          cfg.__previewFormatted = formatDateByPreset("HH:mm", d);
        } else {
          cfg.__previewFormatted = `${amount} ${unit}`;
        }
      }
    } 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;
  }
  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,
): 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,
    ),
  );
  return out;
}

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

function templateListLabel(t: LabelTemplateDto): string {
  const name = (t.templateName ?? t.name ?? "").trim() || "None";
  const code = templateListCode(t) || "None";
  return `${name} (${code})`;
}

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,
    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;
}

function useLabelFormReferenceData(open: boolean) {
  const [loading, setLoading] = useState(false);
  const [templates, setTemplates] = useState<LabelTemplateDto[]>([]);
  const [locations, setLocations] = useState<LocationDto[]>([]);
  const [groups, setGroups] = useState<GroupListItem[]>([]);
  const [categories, setCategories] = useState<LabelCategoryDto[]>([]);
  const [types, setTypes] = useState<LabelTypeDto[]>([]);
  const [products, setProducts] = useState<ProductDto[]>([]);
  /** 商品分类（Menu / fl_product_category），用于筛选商品 */
  const [productCategories, setProductCategories] = useState<ProductCategoryDto[]>([]);
  const [productLocationMap, setProductLocationMap] = useState<Map<string, string[]>>(() => new Map());

  useEffect(() => {
    if (!open) return;
    let cancelled = false;
    (async () => {
      setLoading(true);
      try {
        const [tplRes, locRes, grpRes, catRes, typeRes, prodRes, menuCatRes, plRes] = await Promise.all([
          getLabelTemplates({ skipCount: 1, maxResultCount: 500 }),
          getLocations({ skipCount: 1, maxResultCount: 500 }),
          getGroups({ 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 ?? []);
        setGroups(grpRes.items ?? []);
        setCategories(catRes.items ?? []);
        setTypes(typeRes.items ?? []);
        setProducts(prodRes.items ?? []);
        setProductCategories(menuCatRes.items ?? []);
        const rawMap = buildProductLocationMap(plRes.items ?? []);
        setProductLocationMap(mergeProductLocationOverlay(rawMap, prodRes.items ?? []));
      } catch (e: any) {
        if (!cancelled) {
          toast.error("Failed to load options", {
            description: e?.message ? String(e.message) : "Check network or sign-in.",
          });
          setTemplates([]);
          setLocations([]);
          setGroups([]);
          setCategories([]);
          setTypes([]);
          setProducts([]);
          setProductCategories([]);
          setProductLocationMap(new Map());
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open]);

  return {
    loading,
    templates,
    locations,
    groups,
    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()}
        />
        {productId.trim() ? (
          <p className="text-xs text-gray-500 font-mono truncate" title={productId}>
            Id: {productId}
          </p>
        ) : null}
      </div>
    </div>
  );
}

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

export function LabelsList({ openCreateSeq = 0, onOpenCreateIntentConsumed }: LabelsListProps = {}) {
  const [dataEntryTemplateCode, setDataEntryTemplateCode] = useState<string | null>(null);
  const [dataEntryContextHint, setDataEntryContextHint] = 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 [keyword, setKeyword] = useState("");
  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 [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] = await Promise.all([
          getLocations({ skipCount: 1, maxResultCount: 500 }),
          getGroups({ skipCount: 1, maxResultCount: 500 }),
          getLabelCategories({ skipCount: 1, maxResultCount: 500 }),
          getLabelTypes({ skipCount: 1, maxResultCount: 500 }),
        ]);
        if (cancelled) return;
        setFilterLocations(locRes.items ?? []);
        setFilterGroups(grpRes.items ?? []);
        setFilterLabelCategories(catRes.items ?? []);
        setFilterLabelTypes(typeRes.items ?? []);
      } catch (e: unknown) {
        if (!cancelled) {
          setFilterLocations([]);
          setFilterGroups([]);
          setFilterLabelCategories([]);
          setFilterLabelTypes([]);
          toast.error("Failed to load filters.", {
            description: e instanceof Error ? e.message : "Please try again.",
          });
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, []);

  const regionSelectOptions = useMemo(() => {
    const m = new Map<string, GroupListItem>();
    for (const g of filterGroups) {
      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]);

  const locationsForToolbarFilter = useMemo(() => {
    if (regionFilter === "all") return filterLocations;
    const g = filterGroups.find((x) => x.id === regionFilter);
    if (!g) return filterLocations;
    const gn = (g.groupName ?? "").trim();
    const pn = (g.partnerName ?? "").trim();
    return filterLocations.filter(
      (l) => (l.groupName ?? "").trim() === gn && (l.partner ?? "").trim() === pn,
    );
  }, [filterLocations, filterGroups, 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, 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 baseInput = {
          keyword: debouncedKeyword || undefined,
          labelCategoryId: labelCategoryFilter !== "all" ? labelCategoryFilter : undefined,
          labelTypeId: labelTypeFilter !== "all" ? labelTypeFilter : undefined,
          templateCode: templateFilter !== "all" ? templateFilter : undefined,
          state: stateFilter === "all" ? undefined : stateFilter === "true",
        };

        /** 标签接口无 Region 字段：选区域且仍为「全部地点」时与其它模块一致——拉一批再按门店裁剪后分页 */
        if (regionFilter !== "all" && locationFilter === "all") {
          const allowed = new Set(locationsForToolbarFilter.map((l) => l.id));
          const res = await getLabels(
            {
              skipCount: 1,
              maxResultCount: 500,
              ...baseInput,
            },
            ac.signal,
          );
          let list = res.items ?? [];
          list = list.filter((item) => allowed.has(String(item.locationId ?? "").trim()));
          const t = list.length;
          const start = (pageIndex - 1) * pageSize;
          setLabels(list.slice(start, start + pageSize));
          setTotal(t);
          return;
        }

        const skipCount = skipCountForPage(pageIndex);
        const res = await getLabels(
          {
            skipCount,
            maxResultCount: pageSize,
            ...baseInput,
            locationId: locationFilter !== "all" ? locationFilter : undefined,
          },
          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,
    regionFilter,
    locationFilter,
    locationsForToolbarFilter,
    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;
    setDataEntryContextHint(`Bulk Add template: ${title}`);
    setDataEntryTemplateCode(bulkTemplateCode);
    setIsBulkAddDialogOpen(false);
  };

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

  const closeDataEntry = () => {
    setDataEntryTemplateCode(null);
    setDataEntryContextHint(undefined);
    refreshList();
  };

  if (dataEntryTemplateCode) {
    return (
      <div className="h-[calc(100vh-8rem)] min-h-[500px] flex flex-col pt-2">
        <LabelTemplateDataEntryView
          templateCode={dataEntryTemplateCode}
          onBack={closeDataEntry}
          contextHint={dataEntryContextHint}
        />
      </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"
            />
            <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="State" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All States</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-[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={10} className="text-center text-sm text-gray-500 py-10">
                    Loading...
                  </TableCell>
                </TableRow>
              ) : labels.length === 0 ? (
                <TableRow>
                  <TableCell colSpan={10} 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">
                      {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: "30%" }}>
          <DialogHeader>
            <DialogTitle>Select a Template to Bulk Add</DialogTitle>
          </DialogHeader>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-6 py-2">
            <div className="space-y-2">
              <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="rounded-xl border border-gray-200 bg-gray-50 p-4 flex items-center justify-center min-h-[320px]">
              {bulkLoadingPreview ? (
                <div className="text-sm text-gray-500">Loading preview…</div>
              ) : bulkPreviewTemplate ? (
                <div className="w-full h-full flex items-center justify-center overflow-auto">
                  <LabelPreviewOnly template={bulkPreviewTemplate} maxWidth={360} />
                </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>
  );
}

function CreateLabelDialog({
  open,
  onOpenChange,
  onCreated,
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  onCreated: () => void;
}) {
  const {
    loading: refLoading,
    templates,
    locations,
    groups,
    categories,
    types,
    products,
    productCategories,
    productLocationMap,
  } = useLabelFormReferenceData(open);
  const [scopeCompany, setScopeCompany] = useState("");
  const [scopeRegionGroupId, setScopeRegionGroupId] = useState("");
  const [labelTypeIdsInRegion, setLabelTypeIdsInRegion] = useState<Set<string> | null>(null);
  const prevRegionForResetRef = useRef<string | null>(null);
  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: "",
    locationId: "",
    labelCategoryId: "",
    labelTypeId: "",
    productIds: [],
    labelInfoJson: null,
    state: true,
  });

  const resetForm = () => {
    setForm({
      labelName: "",
      templateCode: "",
      locationId: "",
      labelCategoryId: "",
      labelTypeId: "",
      productIds: [],
      labelInfoJson: null,
      state: true,
    });
    setSelectedTemplate(null);
    setTemplateDataValues({});
    setTemplateDateOffsets({});
    setNutritionByElementId({});
    setProductCatalogCategoryId("");
    setScopeCompany("");
    setScopeRegionGroupId("");
    setLabelTypeIdsInRegion(null);
    prevRegionForResetRef.current = null;
  };

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

  useEffect(() => {
    if (!scopeCompany.trim()) {
      setScopeRegionGroupId("");
    }
  }, [scopeCompany]);

  const locationsInScope = useMemo(
    () => locationsForRegionGroup(locations, groups, scopeRegionGroupId),
    [locations, groups, scopeRegionGroupId],
  );

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

  const regionScopeReady = !!(scopeCompany.trim() && scopeRegionGroupId.trim());

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

  useEffect(() => {
    if (!regionScopeReady) {
      setLabelTypeIdsInRegion(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 (!allowedLocIdsForScope.has(lid)) continue;
          const tid = String(row.labelTypeId ?? "").trim();
          if (tid) next.add(tid);
        }
        setLabelTypeIdsInRegion(next);
      } catch {
        if (!cancelled) setLabelTypeIdsInRegion(new Set());
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [regionScopeReady, scopeRegionGroupId, allowedLocIdsForScope]);

  const partnerOptionsForScope = useMemo(() => {
    const s = new Set<string>();
    for (const l of locations) {
      const v = (l.partner ?? "").trim();
      if (v) s.add(v);
    }
    return Array.from(s).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
  }, [locations]);

  const regionGroupOptionsForScope = useMemo(() => {
    const pn = scopeCompany.trim();
    if (!pn) return [];
    return groups
      .filter((g) => (g.partnerName ?? "").trim() === pn)
      .sort((a, b) =>
        (a.groupName ?? "").localeCompare(b.groupName ?? "", undefined, { sensitivity: "base" }),
      );
  }, [groups, scopeCompany]);

  useEffect(() => {
    if (!scopeRegionGroupId.trim()) return;
    if (!regionGroupOptionsForScope.some((g) => g.id === scopeRegionGroupId)) {
      setScopeRegionGroupId("");
    }
  }, [scopeCompany, regionGroupOptionsForScope, scopeRegionGroupId]);

  const productCategoriesScoped = useMemo(() => {
    if (!regionScopeReady) return [];
    return productCategories.filter((c) => productCategoryMatchesRegion(c, allowedLocIdsForScope));
  }, [productCategories, allowedLocIdsForScope, regionScopeReady]);

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

  const templatesScoped = useMemo(() => {
    if (!regionScopeReady) return [];
    return templates.filter((t) => templateAppliesToRegion(t, allowedLocIdsForScope));
  }, [templates, allowedLocIdsForScope, regionScopeReady]);

  const labelCategoriesScoped = useMemo(() => {
    if (!regionScopeReady) return [];
    return categories.filter((c) => labelCategoryMatchesRegion(c, allowedLocIdsForScope));
  }, [categories, allowedLocIdsForScope, regionScopeReady]);

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

  const typesLoadingForScope = regionScopeReady && labelTypeIdsInRegion === null;

  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 els = getCreateLabelTemplateInputElements(tpl);
        const nextValues: Record<string, string> = {};
        const nextOffsets: Record<string, { unit: string; value: string }> = {};
        for (const el of els) {
          nextValues[el.id] = "";
          if (isDateTimeDataEntryField(el)) {
            nextOffsets[el.id] = { unit: "Days", value: "" };
          }
        }
        setTemplateDataValues(nextValues);
        setTemplateDateOffsets(nextOffsets);
        const nuts = listNutritionElements((tpl.elements ?? []) as LabelElement[]);
        const nextNut: Record<string, Record<string, string>> = {};
        for (const n of nuts) {
          nextNut[n.id] = nutritionManualValuesFromTemplateConfig(n);
        }
        setNutritionByElementId(nextNut);
      } 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]);

  const submit = async () => {
    if (!regionScopeReady) {
      toast.error("Validation failed", {
        description: "Select company and region before other fields.",
      });
      return;
    }
    if (typesLoadingForScope) {
      toast.error("Validation failed", {
        description: "Loading label types for this region. Please wait.",
      });
      return;
    }
    if (!form.labelName.trim() || !form.templateCode.trim() || !form.locationId.trim() || !form.labelCategoryId.trim() || !form.labelTypeId.trim()) {
      toast.error("Validation failed", {
        description: "Fill all required fields and select template, location, 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) 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 inputDefaultValues = collectTemplateDefaultValuesForSave(
        latest,
        templateDataValues,
        templateDateOffsets,
        nutritionByElementId,
        productCodeValue,
      );
      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),
        appliedLocationIds:
          appliedLocationToEditor(latest) === "ALL" ? [] : (latest.appliedLocationIds ?? []),
        templateProductDefaults: mergedDefaults,
      });
    };

    setSubmitting(true);
    try {
      await createLabel(form);
      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 companySelectOptionsForCreate = useMemo(
    () => partnerOptionsForScope.map((p) => ({ value: p, label: p })),
    [partnerOptionsForScope],
  );

  const regionSelectOptionsForCreate = useMemo(
    () =>
      regionGroupOptionsForScope.map((g) => ({
        value: g.id,
        label: toDisplay(g.groupName),
      })),
    [regionGroupOptionsForScope],
  );

  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]);

  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)",
        }}
      >
        <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 grid-cols-2 gap-3 mt-3 mb-2">
              <div className="space-y-2">
                <Label>Company *</Label>
                <SearchableSelect
                  value={scopeCompany}
                  onValueChange={(v) => {
                    setScopeCompany(v);
                    setScopeRegionGroupId("");
                  }}
                  options={companySelectOptionsForCreate}
                  placeholder="Select company"
                  searchPlaceholder="Search company…"
                  emptyText="No companies from locations."
                  disabled={refLoading}
                />
              </div>
              <div className="space-y-2">
                <Label>Region *</Label>
                <SearchableSelect
                  value={scopeRegionGroupId}
                  onValueChange={setScopeRegionGroupId}
                  options={regionSelectOptionsForCreate}
                  placeholder={scopeCompany.trim() ? "Select region" : "Select company first"}
                  searchPlaceholder="Search region…"
                  emptyText={
                    scopeCompany.trim()
                      ? "No regions for this company."
                      : "Select a company first."
                  }
                  disabled={refLoading || !scopeCompany.trim()}
                />
              </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={refLoading || !regionScopeReady}
              />
            </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" : "Select company and region first"
                  }
                  searchPlaceholder="Search template…"
                  emptyText={
                    regionScopeReady ? "No templates for this region." : "Select company and region."
                  }
                  disabled={refLoading || !regionScopeReady}
                />
              </div>
              <div className="space-y-2">
                <Label>Location *</Label>
                <SearchableSelect
                  value={form.locationId}
                  onValueChange={(v) => setForm((p) => ({ ...p, locationId: v }))}
                  options={locationOptions}
                  placeholder={
                    regionScopeReady ? "Select location" : "Select company and region first"
                  }
                  searchPlaceholder="Search location…"
                  emptyText={
                    regionScopeReady ? "No locations in this region." : "Select company and region."
                  }
                  disabled={refLoading || !regionScopeReady}
                />
              </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" : "Select company and region first"
                  }
                  searchPlaceholder="Search category…"
                  emptyText={
                    regionScopeReady ? "No label categories for this region." : "Select company and region."
                  }
                  disabled={refLoading || !regionScopeReady}
                />
              </div>
              <div className="space-y-2">
                <Label>Label Type *</Label>
                <SearchableSelect
                  value={form.labelTypeId}
                  onValueChange={(v) => setForm((p) => ({ ...p, labelTypeId: v }))}
                  options={typeOptions}
                  placeholder={
                    typesLoadingForScope
                      ? "Loading types…"
                      : regionScopeReady
                        ? "Select type"
                        : "Select company and region first"
                  }
                  searchPlaceholder="Search type…"
                  emptyText={
                    regionScopeReady
                      ? typesLoadingForScope
                        ? "Loading…"
                        : "No types for this region."
                      : "Select company and region."
                  }
                  disabled={refLoading || !regionScopeReady || typesLoadingForScope}
                />
              </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)}</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)}`}
                      />
                    )}
                  </div>
                  );
                })}
                {nutritionFieldBlocks.length > 0 ? (
                  <div className="pt-2 mt-2 border-t border-gray-200 space-y-3">
                    <div className="text-xs font-semibold text-gray-700">Nutrition Facts (manual)</div>
                    {nutritionFieldBlocks.map(({ el: nel, spec }) => (
                      <div key={`${nel.id}-${spec.subKey}`} className="space-y-1.5 w-full min-w-0">
                        <Label className="block">{spec.columnLabel}</Label>
                        <Input
                          className="h-10 w-full min-w-0 box-border"
                          value={nutritionByElementId[nel.id]?.[spec.subKey] ?? ""}
                          onChange={(e) =>
                            setNutritionByElementId((prev) => ({
                              ...prev,
                              [nel.id]: {
                                ...(prev[nel.id] ?? {}),
                                [spec.subKey]: e.target.value,
                              },
                            }))
                          }
                          placeholder={`Enter ${spec.columnLabel}`}
                        />
                      </div>
                    ))}
                  </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} />
              </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 || !regionScopeReady || typesLoadingForScope}
            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 {
    loading: refLoading,
    templates,
    locations,
    groups,
    categories,
    types,
    products,
    productCategories,
    productLocationMap,
  } = useLabelFormReferenceData(open);
  const [productCatalogCategoryId, setProductCatalogCategoryId] = useState("");
  const [labelTypeIdsInEditRegion, setLabelTypeIdsInEditRegion] = useState<Set<string> | null>(null);
  const [submitting, setSubmitting] = useState(false);
  const [detailLoading, setDetailLoading] = useState(false);
  const [form, setForm] = useState<LabelUpdateInput>({
    labelName: "",
    templateCode: "",
    locationId: "",
    labelCategoryId: "",
    labelTypeId: "",
    productIds: [],
    labelInfoJson: null,
    state: true,
  });

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

    const id = label.id;
    setForm(labelDtoToUpdateForm(label));

    const ac = new AbortController();
    let cancelled = false;
    setDetailLoading(true);
    (async () => {
      try {
        const detail = await getLabel(id, ac.signal);
        if (cancelled) return;
        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) {
      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 editRegionGroupId = useMemo(() => {
    const lid = (form.locationId ?? "").trim();
    if (!lid) return "";
    const loc = locations.find((x) => x.id === lid);
    if (!loc) return "";
    const pn = (loc.partner ?? "").trim();
    const gn = (loc.groupName ?? "").trim();
    const g = groups.find(
      (x) => (x.partnerName ?? "").trim() === pn && (x.groupName ?? "").trim() === gn,
    );
    return g?.id ?? "";
  }, [form.locationId, locations, groups]);

  const editScopeReady = !!editRegionGroupId;

  const locationsInEditScope = useMemo(
    () => locationsForRegionGroup(locations, groups, editRegionGroupId),
    [locations, groups, editRegionGroupId],
  );

  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, editRegionGroupId, 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;

  const editScopeLocation = useMemo(() => {
    const lid = (form.locationId ?? "").trim();
    if (!lid) return null;
    return locations.find((x) => x.id === lid) ?? null;
  }, [form.locationId, locations]);

  const submit = async () => {
    if (!label?.id) return;
    if (editScopeReady && typesLoadingEditScope) {
      toast.error("Validation failed", {
        description: "Loading label types for this region. Please wait.",
      });
      return;
    }
    if (!form.labelName.trim() || !form.templateCode.trim() || !form.locationId.trim() || !form.labelCategoryId.trim() || !form.labelTypeId.trim()) {
      toast.error("Validation failed", {
        description: "Fill all required fields and select template, location, category, and type.",
      });
      return;
    }
    if (!productCatalogCategoryId.trim() || form.productIds.length === 0) {
      toast.error("Validation failed", {
        description: "Select a product category and one product.",
      });
      return;
    }

    setSubmitting(true);
    try {
      await updateLabel(label.id, form);
      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 id = form.locationId;
    if (id && !base.some((o) => o.value === id)) {
      return [{ value: id, label: `${id} (current)` }, ...base];
    }
    return base;
  }, [editScopeReady, locationsInEditScope, locations, form.locationId]);

  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]);

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-[600px]">
        <DialogHeader>
          <DialogTitle>Edit Label</DialogTitle>
          <DialogDescription>
            {detailLoading ? "Loading label details…" : "Update the label details."}
          </DialogDescription>
        </DialogHeader>

        <div className="grid gap-4 py-4">
          <div className="grid grid-cols-2 gap-4">
            <div className="space-y-2">
              <Label>Company</Label>
              <Input
                readOnly
                className="h-10 bg-gray-50"
                value={editScopeLocation ? toDisplay(editScopeLocation.partner) : "None"}
              />
            </div>
            <div className="space-y-2">
              <Label>Region</Label>
              <Input
                readOnly
                className="h-10 bg-gray-50"
                value={editScopeLocation ? toDisplay(editScopeLocation.groupName) : "None"}
              />
            </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}
              />
            </div>
            <div className="space-y-2">
              <Label>Location *</Label>
              <SearchableSelect
                value={form.locationId}
                onValueChange={(v) => setForm((p) => ({ ...p, locationId: v }))}
                options={editLocationOptions}
                placeholder="Select location"
                searchPlaceholder="Search location…"
                emptyText={
                  editScopeReady ? "No locations in this region." : "No locations found."
                }
                disabled={refLoading || detailLoading}
              />
            </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}
              />
            </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}
            />
          </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>

        <DialogFooter>
          <Button variant="outline" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            disabled={submitting || refLoading || detailLoading || typesLoadingEditScope}
            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>
  );
}
