"use client";
import { jsx, jsxs } from "react/jsx-runtime";
import * as React from "react";
import { Check, Plus, Search, SearchX, SlidersHorizontal, Tag, X } from "lucide-react";
import { EmptyState } from "@open-mercato/ui/primitives/empty-state";
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
import { useT } from "@open-mercato/shared/lib/i18n/context";
import { cn, slugifyTagLabel } from "@open-mercato/shared/lib/utils";
import { apiCall, apiCallOrThrow, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
import { flash } from "@open-mercato/ui/backend/FlashMessages";
import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
import { Button } from "@open-mercato/ui/primitives/button";
import { IconButton } from "@open-mercato/ui/primitives/icon-button";
import {
  Dialog,
  DialogContent,
  DialogTitle
} from "@open-mercato/ui/primitives/dialog";
import { ManageTagsDialog } from "./ManageTagsDialog.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("customers");
const CATEGORY_DEFS = [
  {
    kind: "tags",
    source: "tags",
    supportedEntityTypes: ["person", "company"],
    labelKey: "customers.personTags.category.tags",
    labelFallback: "Tags",
    descriptionKey: "customers.personTags.description.tags",
    descriptionFallback: "Shared CRM tags that can be assigned to many records.",
    selectionMode: "multi",
    hasColorDots: true,
    supportsCreate: true
  },
  {
    kind: "labels",
    source: "labels",
    supportedEntityTypes: ["person", "company"],
    labelKey: "customers.personTags.category.labels",
    labelFallback: "Labels",
    descriptionKey: "customers.personTags.description.labels",
    descriptionFallback: "Quick labels you can create inline for this record.",
    selectionMode: "multi",
    hasColorDots: false,
    supportsCreate: true
  },
  {
    kind: "statuses",
    source: "dictionary",
    supportedEntityTypes: ["person", "company"],
    labelKey: "customers.personTags.category.statuses",
    labelFallback: "Status",
    descriptionKey: "customers.personTags.description.statuses",
    descriptionFallback: "Primary CRM status shown in the header badges.",
    routeKind: "statuses",
    settingKind: "status",
    entityField: "status",
    selectionMode: "single",
    hasColorDots: true
  },
  {
    kind: "lifecycle-stages",
    source: "dictionary",
    supportedEntityTypes: ["person", "company"],
    labelKey: "customers.personTags.category.lifecycle-stages",
    labelFallback: "Lifecycle",
    descriptionKey: "customers.personTags.description.lifecycleStages",
    descriptionFallback: "Lifecycle stage used across CRM detail views.",
    routeKind: "lifecycle-stages",
    settingKind: "lifecycle_stage",
    entityField: "lifecycleStage",
    selectionMode: "single",
    hasColorDots: true
  },
  {
    kind: "sources",
    source: "dictionary",
    supportedEntityTypes: ["person", "company"],
    labelKey: "customers.personTags.category.sources",
    labelFallback: "Source",
    descriptionKey: "customers.personTags.description.sources",
    descriptionFallback: "How this record entered the pipeline.",
    routeKind: "sources",
    settingKind: "source",
    entityField: "source",
    selectionMode: "single",
    hasColorDots: true
  },
  {
    kind: "temperature",
    source: "dictionary",
    supportedEntityTypes: ["person", "company"],
    labelKey: "customers.personTags.category.temperature",
    labelFallback: "Temperature",
    descriptionKey: "customers.personTags.description.temperature",
    descriptionFallback: "Sales temperature or engagement level.",
    routeKind: "temperature",
    settingKind: "temperature",
    entityField: "temperature",
    selectionMode: "single",
    hasColorDots: true
  },
  {
    kind: "renewal-quarters",
    source: "dictionary",
    supportedEntityTypes: ["person", "company"],
    labelKey: "customers.personTags.category.renewal-quarters",
    labelFallback: "Renewal quarter",
    descriptionKey: "customers.personTags.description.renewalQuarters",
    descriptionFallback: "Quarter used for renewal planning and badges.",
    routeKind: "renewal-quarters",
    settingKind: "renewal_quarter",
    entityField: "renewalQuarter",
    selectionMode: "single",
    hasColorDots: true
  },
  {
    kind: "job-titles",
    source: "dictionary",
    supportedEntityTypes: ["person"],
    labelKey: "customers.personTags.category.job-titles",
    labelFallback: "Job title",
    descriptionKey: "customers.personTags.description.jobTitles",
    descriptionFallback: "The role or title used for this person.",
    routeKind: "job-titles",
    settingKind: "job_title",
    entityField: "jobTitle",
    selectionMode: "single",
    hasColorDots: true
  },
  {
    kind: "industries",
    source: "dictionary",
    supportedEntityTypes: ["company"],
    labelKey: "customers.personTags.category.industries",
    labelFallback: "Industry",
    descriptionKey: "customers.personTags.description.industries",
    descriptionFallback: "The industry used to classify this company.",
    routeKind: "industries",
    settingKind: "industry",
    entityField: "industry",
    selectionMode: "single",
    hasColorDots: true
  }
];
const REMOTE_CATEGORY_PAGE_SIZE = 50;
const CUSTOM_CATEGORY_FIELD_PREFIX = "crmTagCategory:";
function cloneSelectionMap(values) {
  return Object.fromEntries(
    Object.entries(values).map(([key, selection]) => [key, new Set(selection)])
  );
}
function sortOptions(entries) {
  return [...entries].sort(
    (left, right) => left.label.localeCompare(right.label, void 0, { sensitivity: "base" })
  );
}
function mergeOptions(...groups) {
  const merged = /* @__PURE__ */ new Map();
  groups.flat().forEach((entry) => {
    merged.set(entry.value, entry);
  });
  return sortOptions(Array.from(merged.values()));
}
function humanizeCategoryKind(kind) {
  return kind.split(/[-_]+/).filter((part) => part.trim().length > 0).map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
}
function resolveCustomCategoryFieldKey(kind) {
  return `${CUSTOM_CATEGORY_FIELD_PREFIX}${kind}`;
}
function createCustomCategoryDef(kind, selectionMode) {
  const label = humanizeCategoryKind(kind);
  return {
    kind,
    source: "dictionary",
    supportedEntityTypes: ["person", "company"],
    labelKey: "",
    labelFallback: label,
    descriptionKey: "customers.personTags.description.customCategory",
    descriptionFallback: `Custom CRM category: ${label}.`,
    routeKind: kind,
    settingKind: kind,
    customFieldKey: resolveCustomCategoryFieldKey(kind),
    selectionMode,
    hasColorDots: true
  };
}
function normalizeSelectionValues(value, selectionMode) {
  if (typeof value === "string") {
    const trimmed = value.trim();
    return trimmed.length > 0 ? [trimmed] : [];
  }
  if (Array.isArray(value)) {
    const normalized = value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
    if (selectionMode === "single") {
      return normalized.length > 0 ? [normalized[0]] : [];
    }
    return normalized;
  }
  return [];
}
function readCategorySelectionValues(category, entityData, selectionMode) {
  if (category.entityField) {
    return normalizeSelectionValues(entityData[category.entityField], selectionMode);
  }
  if (category.customFieldKey) {
    return normalizeSelectionValues(entityData.customFields?.[category.customFieldKey], selectionMode);
  }
  return [];
}
function areSelectionsEqual(left, right) {
  if (left.size !== right.size) return false;
  for (const value of left) {
    if (!right.has(value)) return false;
  }
  return true;
}
function TagChip({
  label,
  color,
  active,
  showColorDot,
  onClick
}) {
  const activeColorStyle = active && color ? { color, borderColor: color, backgroundColor: `${color}1A` } : void 0;
  return /* @__PURE__ */ jsxs(
    Button,
    {
      type: "button",
      variant: "ghost",
      size: "sm",
      onClick,
      className: cn(
        "inline-flex h-auto items-center gap-1 rounded-full border px-2.5 py-1.5 transition-colors",
        active ? activeColorStyle ? "font-semibold hover:opacity-90" : "border-transparent bg-muted font-semibold text-foreground hover:bg-muted" : "border-border bg-transparent font-normal text-muted-foreground hover:bg-muted/60 hover:text-foreground"
      ),
      style: activeColorStyle,
      children: [
        showColorDot && color ? /* @__PURE__ */ jsx(
          "span",
          {
            className: "inline-block size-2 shrink-0 rounded-full",
            style: { backgroundColor: color }
          }
        ) : null,
        /* @__PURE__ */ jsx("span", { className: "text-xs", children: label }),
        active ? /* @__PURE__ */ jsx(X, { className: "size-2.5 shrink-0" }) : null
      ]
    }
  );
}
function buildApplicableCategories(entityType, kindSettings) {
  const baseCategories = CATEGORY_DEFS.filter((category) => category.supportedEntityTypes.includes(entityType));
  const customCategories = kindSettings.filter((setting) => setting.visibleInTags).filter((setting) => !CATEGORY_DEFS.some((category) => category.kind === setting.kind || category.settingKind === setting.kind)).map((setting) => createCustomCategoryDef(setting.kind, setting.selectionMode));
  return [...baseCategories, ...customCategories];
}
function EntityTagsDialog({
  open,
  onClose,
  entityId,
  entityType,
  entityOrganizationId,
  entityData,
  onSaved
}) {
  const t = useT();
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState(false);
  const [searchValue, setSearchValue] = React.useState("");
  const [categories, setCategories] = React.useState([]);
  const [selectedEntrySeeds, setSelectedEntrySeeds] = React.useState({});
  const [selectedValues, setSelectedValues] = React.useState({});
  const [originalValues, setOriginalValues] = React.useState({});
  const [activeCategoryKind, setActiveCategoryKind] = React.useState(null);
  const [newEntryInputByKind, setNewEntryInputByKind] = React.useState({});
  const [creatingKind, setCreatingKind] = React.useState(null);
  const [manageTagsOpen, setManageTagsOpen] = React.useState(false);
  const [activeCategoryPage, setActiveCategoryPage] = React.useState(1);
  const [activeCategoryTotalPages, setActiveCategoryTotalPages] = React.useState(1);
  const [activeCategoryLoading, setActiveCategoryLoading] = React.useState(false);
  const creationInFlightRef = React.useRef(null);
  const mutationContextId = React.useMemo(
    () => `customer-tags:${entityType}:${entityId}`,
    [entityId, entityType]
  );
  const { runMutation, retryLastMutation } = useGuardedMutation({
    contextId: mutationContextId,
    blockedMessage: t("ui.forms.flash.saveBlocked", "Save blocked by validation")
  });
  const mutationContext = React.useMemo(
    () => ({
      formId: mutationContextId,
      resourceKind: entityType === "person" ? "customers.person" : "customers.company",
      resourceId: entityId,
      entityType,
      retryLastMutation
    }),
    [entityId, entityType, mutationContextId, retryLastMutation]
  );
  const runGuardedMutation = React.useCallback(
    async (operation, mutationPayload) => runMutation({
      operation,
      mutationPayload,
      context: mutationContext
    }),
    [mutationContext, runMutation]
  );
  const updateCategoryEntries = React.useCallback(
    (kind, updater) => {
      setCategories(
        (previous) => previous.map(
          (category) => category.kind === kind ? { ...category, entries: sortOptions(updater(category.entries)) } : category
        )
      );
    },
    []
  );
  const loadData = React.useCallback(async () => {
    setLoading(true);
    try {
      const scopedQuery = new URLSearchParams();
      if (entityOrganizationId) {
        scopedQuery.set("organizationId", entityOrganizationId);
      }
      const loadKindSettings = async () => {
        try {
          const settingsCall = await apiCall(
            `/api/customers/dictionaries/kind-settings${scopedQuery.size ? `?${scopedQuery.toString()}` : ""}`,
            { cache: "no-store", headers: { "x-om-unauthorized-redirect": "0" } }
          );
          if (settingsCall.ok && settingsCall.result?.items) {
            return settingsCall.result.items;
          }
        } catch {
        }
        return [];
      };
      const loadLabelData = async () => {
        try {
          const labelsQuery = new URLSearchParams();
          labelsQuery.set("entityId", entityId);
          labelsQuery.set("pageSize", "1");
          if (entityOrganizationId) {
            labelsQuery.set("organizationId", entityOrganizationId);
          }
          const labelsCall = await apiCall(`/api/customers/labels?${labelsQuery.toString()}`, {
            cache: "no-store",
            headers: { "x-om-unauthorized-redirect": "0" }
          });
          const labelsData = labelsCall.ok ? labelsCall.result : null;
          const assignedLabelIds2 = labelsData?.assignedIds ?? [];
          if (assignedLabelIds2.length === 0) {
            return { assignedLabelIds: assignedLabelIds2, selectedLabelEntries: [] };
          }
          const detailQuery = new URLSearchParams({
            ids: assignedLabelIds2.join(","),
            pageSize: String(Math.min(assignedLabelIds2.length, 100))
          });
          if (entityOrganizationId) {
            detailQuery.set("organizationId", entityOrganizationId);
          }
          const selectedLabelsCall = await apiCall(
            `/api/customers/labels?${detailQuery.toString()}`,
            {
              cache: "no-store",
              headers: { "x-om-unauthorized-redirect": "0" }
            }
          );
          const selectedLabels = selectedLabelsCall.ok ? selectedLabelsCall.result?.items ?? [] : [];
          const selectedLabelEntries2 = selectedLabels.map((label) => ({
            id: label.id,
            value: label.id,
            label: label.label,
            color: null
          }));
          return { assignedLabelIds: assignedLabelIds2, selectedLabelEntries: selectedLabelEntries2 };
        } catch {
          return { assignedLabelIds: [], selectedLabelEntries: [] };
        }
      };
      const selectedTagEntries = Array.isArray(entityData.tags) ? entityData.tags.map((tag) => ({
        id: tag.id,
        value: tag.id,
        label: tag.label,
        color: tag.color ?? null
      })) : [];
      const labelDataPromise = loadLabelData();
      const kindSettings = await loadKindSettings();
      const settingsMap = new Map(kindSettings.map((setting) => [setting.kind, setting]));
      const categoryDefs = buildApplicableCategories(entityType, kindSettings);
      const loadDictionaryCategory = async (categoryDef) => {
        try {
          const dictionaryUrl = new URL(`/api/customers/dictionaries/${categoryDef.routeKind}`, "http://localhost");
          if (entityOrganizationId) {
            dictionaryUrl.searchParams.set("organizationId", entityOrganizationId);
          }
          const dictionaryCall = await apiCall(
            `${dictionaryUrl.pathname}${dictionaryUrl.search}`,
            { cache: "no-store", headers: { "x-om-unauthorized-redirect": "0" } }
          );
          const dictionaryItems = dictionaryCall.ok ? dictionaryCall.result?.items ?? [] : [];
          const entries = dictionaryItems.map((entry) => ({
            id: entry.id,
            value: entry.value,
            label: entry.label,
            color: entry.color ?? null
          }));
          const setting = categoryDef.settingKind ? settingsMap.get(categoryDef.settingKind) : void 0;
          const selectionMode = setting?.selectionMode ?? categoryDef.selectionMode ?? "single";
          const currentValues = readCategorySelectionValues(categoryDef, entityData, selectionMode);
          currentValues.forEach((currentValue) => {
            if (entries.some((entry) => entry.value === currentValue)) return;
            entries.push({
              id: `current:${categoryDef.kind}:${currentValue}`,
              value: currentValue,
              label: currentValue,
              color: null
            });
          });
          return {
            ...categoryDef,
            label: categoryDef.labelKey ? t(categoryDef.labelKey, categoryDef.labelFallback) : categoryDef.labelFallback,
            description: categoryDef.descriptionKey ? t(
              categoryDef.descriptionKey,
              categoryDef.descriptionFallback,
              categoryDef.customFieldKey ? { name: categoryDef.labelFallback } : void 0
            ) : categoryDef.descriptionFallback,
            entries: sortOptions(entries),
            selectionMode
          };
        } catch {
          const setting = categoryDef.settingKind ? settingsMap.get(categoryDef.settingKind) : void 0;
          const selectionMode = setting?.selectionMode ?? categoryDef.selectionMode ?? "single";
          const fallbackEntries = readCategorySelectionValues(categoryDef, entityData, selectionMode).map((value) => ({
            id: `current:${categoryDef.kind}:${value}`,
            value,
            label: value,
            color: null
          }));
          return {
            ...categoryDef,
            label: categoryDef.labelKey ? t(categoryDef.labelKey, categoryDef.labelFallback) : categoryDef.labelFallback,
            description: categoryDef.descriptionKey ? t(
              categoryDef.descriptionKey,
              categoryDef.descriptionFallback,
              categoryDef.customFieldKey ? { name: categoryDef.labelFallback } : void 0
            ) : categoryDef.descriptionFallback,
            entries: fallbackEntries,
            selectionMode
          };
        }
      };
      const loadedCategories = await Promise.all(
        categoryDefs.map(async (categoryDef) => {
          if (categoryDef.source === "tags") {
            return {
              ...categoryDef,
              label: t(categoryDef.labelKey, categoryDef.labelFallback),
              description: t(categoryDef.descriptionKey, categoryDef.descriptionFallback),
              entries: sortOptions(selectedTagEntries),
              selectionMode: categoryDef.selectionMode ?? "multi"
            };
          }
          if (categoryDef.source === "labels") {
            const { selectedLabelEntries: selectedLabelEntries2 } = await labelDataPromise;
            return {
              ...categoryDef,
              label: t(categoryDef.labelKey, categoryDef.labelFallback),
              description: t(categoryDef.descriptionKey, categoryDef.descriptionFallback),
              entries: sortOptions(selectedLabelEntries2),
              selectionMode: categoryDef.selectionMode ?? "multi"
            };
          }
          return loadDictionaryCategory(categoryDef);
        })
      );
      const { assignedLabelIds, selectedLabelEntries } = await labelDataPromise;
      loadedCategories.sort((left, right) => {
        const leftSortOrder = left.settingKind && settingsMap.has(left.settingKind) ? settingsMap.get(left.settingKind)?.sortOrder ?? 1e3 : 1e3 + CATEGORY_DEFS.findIndex((category) => category.kind === left.kind);
        const rightSortOrder = right.settingKind && settingsMap.has(right.settingKind) ? settingsMap.get(right.settingKind)?.sortOrder ?? 1e3 : 1e3 + CATEGORY_DEFS.findIndex((category) => category.kind === right.kind);
        return leftSortOrder - rightSortOrder;
      });
      const initialValues = {};
      for (const category of loadedCategories) {
        if (category.source === "dictionary" && (category.entityField || category.customFieldKey)) {
          initialValues[category.kind] = new Set(
            readCategorySelectionValues(category, entityData, category.selectionMode)
          );
          continue;
        }
        if (category.source === "tags") {
          const availableTagIds = new Set(category.entries.map((entry) => entry.value));
          const assignedTagIds = Array.isArray(entityData.tags) ? entityData.tags.map((tag) => tag.id).filter((tagId) => availableTagIds.has(tagId)) : [];
          initialValues[category.kind] = new Set(assignedTagIds);
          continue;
        }
        if (category.source === "labels") {
          const availableLabelIds = new Set(category.entries.map((entry) => entry.value));
          initialValues[category.kind] = new Set(
            assignedLabelIds.filter((labelId) => availableLabelIds.has(labelId))
          );
          continue;
        }
        initialValues[category.kind] = /* @__PURE__ */ new Set();
      }
      setCategories(loadedCategories);
      setSelectedEntrySeeds({
        tags: selectedTagEntries,
        labels: selectedLabelEntries
      });
      setSelectedValues(initialValues);
      setOriginalValues(cloneSelectionMap(initialValues));
      setActiveCategoryKind((previous) => {
        if (previous && loadedCategories.some((category) => category.kind === previous)) {
          return previous;
        }
        return loadedCategories[0]?.kind ?? null;
      });
    } finally {
      setLoading(false);
    }
  }, [entityData, entityId, entityOrganizationId, entityType, t]);
  React.useEffect(() => {
    if (!open) return;
    setSearchValue("");
    setNewEntryInputByKind({});
    loadData().catch((err) => logger.warn("loadData failed", { component: "EntityTagsDialog", err }));
  }, [loadData, open]);
  React.useEffect(() => {
    if (open) return;
    setManageTagsOpen(false);
  }, [open]);
  const activeCategory = React.useMemo(() => {
    if (!categories.length) return null;
    return categories.find((category) => category.kind === activeCategoryKind) ?? categories[0];
  }, [activeCategoryKind, categories]);
  const activeCategoryKindValue = activeCategory?.kind ?? null;
  const activeCategorySource = activeCategory?.source ?? null;
  React.useEffect(() => {
    if (!open) return;
    setActiveCategoryPage(1);
  }, [activeCategoryKind, open, searchValue]);
  React.useEffect(() => {
    if (!open || !activeCategoryKindValue || activeCategorySource !== "tags" && activeCategorySource !== "labels") {
      setActiveCategoryLoading(false);
      setActiveCategoryTotalPages(1);
      return;
    }
    let cancelled = false;
    const params = new URLSearchParams({
      page: String(activeCategoryPage),
      pageSize: String(REMOTE_CATEGORY_PAGE_SIZE)
    });
    if (searchValue.trim().length > 0) {
      params.set("search", searchValue.trim());
    }
    if (activeCategorySource === "labels") {
      params.set("entityId", entityId);
      if (entityOrganizationId) {
        params.set("organizationId", entityOrganizationId);
      }
    }
    const endpoint = activeCategorySource === "tags" ? `/api/customers/tags?${params.toString()}` : `/api/customers/labels?${params.toString()}`;
    const seedEntries = searchValue.trim().length > 0 ? [] : selectedEntrySeeds[activeCategoryKindValue] ?? [];
    const mapEntries = (items) => items.map((entry) => ({
      id: entry.id,
      value: entry.id,
      label: entry.label,
      color: activeCategorySource === "tags" && "color" in entry ? entry.color ?? null : null
    }));
    setActiveCategoryLoading(true);
    void apiCall(endpoint, {
      cache: "no-store",
      headers: { "x-om-unauthorized-redirect": "0" }
    }).then((response) => {
      if (!response.ok || cancelled) return;
      const fetchedEntries = mapEntries(Array.isArray(response.result?.items) ? response.result.items : []);
      setActiveCategoryTotalPages(
        typeof response.result?.totalPages === "number" ? response.result.totalPages : 1
      );
      updateCategoryEntries(
        activeCategoryKindValue,
        (currentEntries) => activeCategoryPage <= 1 ? mergeOptions(seedEntries, fetchedEntries) : mergeOptions(currentEntries, seedEntries, fetchedEntries)
      );
    }).catch(() => {
      if (cancelled) return;
      setActiveCategoryTotalPages(1);
      updateCategoryEntries(activeCategoryKindValue, () => seedEntries);
    }).finally(() => {
      if (!cancelled) {
        setActiveCategoryLoading(false);
      }
    });
    return () => {
      cancelled = true;
    };
  }, [
    activeCategoryKindValue,
    activeCategoryPage,
    activeCategorySource,
    entityId,
    entityOrganizationId,
    open,
    searchValue,
    selectedEntrySeeds,
    updateCategoryEntries
  ]);
  const activeCount = React.useMemo(
    () => Object.values(selectedValues).reduce((count, values) => count + values.size, 0),
    [selectedValues]
  );
  const hasChanges = React.useMemo(() => {
    const kinds = /* @__PURE__ */ new Set([
      ...Object.keys(selectedValues),
      ...Object.keys(originalValues)
    ]);
    for (const kind of kinds) {
      const current = selectedValues[kind] ?? /* @__PURE__ */ new Set();
      const original = originalValues[kind] ?? /* @__PURE__ */ new Set();
      if (current.size !== original.size) return true;
      for (const value of current) {
        if (!original.has(value)) return true;
      }
    }
    return false;
  }, [originalValues, selectedValues]);
  const filteredEntries = React.useMemo(() => {
    if (!activeCategory) return [];
    const query = searchValue.trim().toLowerCase();
    if (!query) return activeCategory.entries;
    return activeCategory.entries.filter(
      (entry) => entry.label.toLowerCase().includes(query) || entry.value.toLowerCase().includes(query)
    );
  }, [activeCategory, searchValue]);
  const toggleValue = React.useCallback(
    (kind, value, selectionMode) => {
      setSelectedValues((previous) => {
        const next = new Set(previous[kind] ?? []);
        if (next.has(value)) {
          next.delete(value);
        } else {
          if (selectionMode === "single") {
            next.clear();
          }
          next.add(value);
        }
        return { ...previous, [kind]: next };
      });
    },
    []
  );
  const handleCreateEntry = React.useCallback(async () => {
    if (!activeCategory || !activeCategory.supportsCreate) return;
    const draftValue = newEntryInputByKind[activeCategory.kind] ?? "";
    const trimmed = draftValue.trim();
    if (!trimmed || creatingKind || creationInFlightRef.current) {
      if (!trimmed) {
        setNewEntryInputByKind((previous) => ({ ...previous, [activeCategory.kind]: null }));
      }
      return;
    }
    const existingEntry = activeCategory.entries.find(
      (entry) => entry.label.trim().toLowerCase() === trimmed.toLowerCase()
    );
    if (existingEntry) {
      setSelectedValues((previous) => {
        const current = new Set(previous[activeCategory.kind] ?? []);
        current.add(existingEntry.value);
        return { ...previous, [activeCategory.kind]: current };
      });
      setNewEntryInputByKind((previous) => ({ ...previous, [activeCategory.kind]: null }));
      return;
    }
    creationInFlightRef.current = activeCategory.kind;
    setCreatingKind(activeCategory.kind);
    try {
      if (activeCategory.source === "tags") {
        const result = await runGuardedMutation(
          () => readApiResultOrThrow(
            "/api/customers/tags",
            {
              // optimistic-lock-exempt: tag dictionary create-only
              method: "POST",
              headers: { "content-type": "application/json" },
              body: JSON.stringify({
                label: trimmed,
                slug: slugifyTagLabel(trimmed)
              })
            }
          ),
          { categoryKind: activeCategory.kind, operation: "createTag", label: trimmed }
        );
        const id = typeof result?.id === "string" ? result.id : "";
        if (!id) {
          throw new Error(t("customers.people.detail.tags.createError", "Failed to create tag."));
        }
        const option = {
          id,
          value: id,
          label: trimmed,
          color: typeof result?.color === "string" ? result.color : null
        };
        updateCategoryEntries(activeCategory.kind, (entries) => [...entries, option]);
        setSelectedEntrySeeds((previous) => ({
          ...previous,
          [activeCategory.kind]: mergeOptions(previous[activeCategory.kind] ?? [], [option])
        }));
        setSelectedValues((previous) => ({
          ...previous,
          [activeCategory.kind]: /* @__PURE__ */ new Set([...previous[activeCategory.kind] ?? [], option.value])
        }));
      } else if (activeCategory.source === "labels") {
        const payload = entityOrganizationId ? { label: trimmed, organizationId: entityOrganizationId } : { label: trimmed };
        const result = await runGuardedMutation(
          () => readApiResultOrThrow(
            "/api/customers/labels",
            {
              // optimistic-lock-exempt: label dictionary create-only
              method: "POST",
              headers: { "content-type": "application/json" },
              body: JSON.stringify(payload)
            }
          ),
          { categoryKind: activeCategory.kind, operation: "createLabel", label: trimmed }
        );
        const option = {
          id: result.id,
          value: result.id,
          label: result.label,
          color: null
        };
        updateCategoryEntries(activeCategory.kind, (entries) => [...entries, option]);
        setSelectedEntrySeeds((previous) => ({
          ...previous,
          [activeCategory.kind]: mergeOptions(previous[activeCategory.kind] ?? [], [option])
        }));
        setSelectedValues((previous) => ({
          ...previous,
          [activeCategory.kind]: /* @__PURE__ */ new Set([...previous[activeCategory.kind] ?? [], option.value])
        }));
      }
      setNewEntryInputByKind((previous) => ({ ...previous, [activeCategory.kind]: null }));
    } catch (error) {
      const message = error instanceof Error ? error.message : t("customers.personTags.createLabelError", "Failed to create label");
      flash(message, "error");
    } finally {
      creationInFlightRef.current = null;
      setCreatingKind(null);
    }
  }, [activeCategory, creatingKind, entityOrganizationId, newEntryInputByKind, runGuardedMutation, t, updateCategoryEntries]);
  const handleSave = React.useCallback(async () => {
    if (saving) return;
    setSaving(true);
    try {
      const entityUpdate = {};
      const customFieldUpdate = {};
      categories.forEach((category) => {
        if (category.source !== "dictionary") return;
        const currentSelection = selectedValues[category.kind] ?? /* @__PURE__ */ new Set();
        const originalSelection = originalValues[category.kind] ?? /* @__PURE__ */ new Set();
        if (areSelectionsEqual(currentSelection, originalSelection)) return;
        if (category.entityField) {
          const currentValue = currentSelection.size > 0 ? Array.from(currentSelection)[0] ?? null : null;
          entityUpdate[category.entityField] = currentValue;
          return;
        }
        if (category.customFieldKey) {
          customFieldUpdate[category.customFieldKey] = category.selectionMode === "single" ? Array.from(currentSelection)[0] ?? null : Array.from(currentSelection);
        }
      });
      if (Object.keys(entityUpdate).length > 0 || Object.keys(customFieldUpdate).length > 0) {
        await runGuardedMutation(
          () => apiCallOrThrow(`/api/customers/${entityType === "person" ? "people" : "companies"}`, {
            // optimistic-lock-exempt: tag/label dictionary + custom-field selection write (assignment add/remove)
            method: "PUT",
            headers: { "content-type": "application/json" },
            body: JSON.stringify({
              id: entityId,
              ...entityUpdate,
              ...Object.keys(customFieldUpdate).length > 0 ? {
                customFields: {
                  ...entityData.customFields ?? {},
                  ...customFieldUpdate
                }
              } : {}
            })
          }),
          { operation: "updateEntityTags", entityUpdate, customFieldUpdate }
        );
      }
      const currentTags = selectedValues.tags ?? /* @__PURE__ */ new Set();
      const originalTags = originalValues.tags ?? /* @__PURE__ */ new Set();
      const addedTags = Array.from(currentTags).filter((tagId) => !originalTags.has(tagId));
      const removedTags = Array.from(originalTags).filter((tagId) => !currentTags.has(tagId));
      for (const tagId of addedTags) {
        await runGuardedMutation(
          () => apiCallOrThrow("/api/customers/tags/assign", {
            // optimistic-lock-exempt: tag assignment add/remove
            method: "POST",
            headers: { "content-type": "application/json" },
            body: JSON.stringify({ tagId, entityId })
          }),
          { operation: "assignTag", tagId }
        );
      }
      for (const tagId of removedTags) {
        await runGuardedMutation(
          () => apiCallOrThrow("/api/customers/tags/unassign", {
            // optimistic-lock-exempt: tag assignment add/remove
            method: "POST",
            headers: { "content-type": "application/json" },
            body: JSON.stringify({ tagId, entityId })
          }),
          { operation: "unassignTag", tagId }
        );
      }
      const currentLabels = selectedValues.labels ?? /* @__PURE__ */ new Set();
      const originalLabels = originalValues.labels ?? /* @__PURE__ */ new Set();
      const addedLabels = Array.from(currentLabels).filter((labelId) => !originalLabels.has(labelId));
      const removedLabels = Array.from(originalLabels).filter((labelId) => !currentLabels.has(labelId));
      for (const labelId of addedLabels) {
        const payload = entityOrganizationId ? { labelId, entityId, organizationId: entityOrganizationId } : { labelId, entityId };
        await runGuardedMutation(
          () => apiCallOrThrow("/api/customers/labels/assign", {
            // optimistic-lock-exempt: label assignment add/remove
            method: "POST",
            headers: { "content-type": "application/json" },
            body: JSON.stringify(payload)
          }),
          { operation: "assignLabel", labelId }
        );
      }
      for (const labelId of removedLabels) {
        const payload = entityOrganizationId ? { labelId, entityId, organizationId: entityOrganizationId } : { labelId, entityId };
        await runGuardedMutation(
          () => apiCallOrThrow("/api/customers/labels/unassign", {
            // optimistic-lock-exempt: label assignment add/remove
            method: "POST",
            headers: { "content-type": "application/json" },
            body: JSON.stringify(payload)
          }),
          { operation: "unassignLabel", labelId }
        );
      }
      flash(t("customers.personTags.saveSuccess", "Tags updated."), "success");
      onSaved?.();
      onClose();
    } catch (error) {
      const message = error instanceof Error ? error.message : t("customers.personTags.saveError", "Failed to save tags");
      flash(message, "error");
    } finally {
      setSaving(false);
    }
  }, [
    categories,
    entityId,
    entityOrganizationId,
    entityType,
    onClose,
    onSaved,
    originalValues,
    saving,
    selectedValues,
    runGuardedMutation,
    t
  ]);
  React.useEffect(() => {
    if (!open) return;
    const handler = (event) => {
      if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
        event.preventDefault();
        void handleSave();
      }
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [handleSave, open]);
  React.useEffect(() => {
    if (!open) return;
    setSearchValue("");
  }, [activeCategoryKind, open]);
  const activeSelection = activeCategory ? selectedValues[activeCategory.kind] ?? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set();
  return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: (next) => {
    if (!next) onClose();
  }, children: [
    /* @__PURE__ */ jsxs(
      DialogContent,
      {
        className: "flex max-h-[85vh] flex-col overflow-hidden border-border bg-background p-0 shadow-[0px_16px_40px_0px_rgba(0,0,0,0.14)] sm:max-w-[760px] sm:rounded-xl [&>[data-dialog-close]]:hidden",
        "aria-describedby": void 0,
        children: [
          /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t("customers.personTags.title", "Edit tags") }) }),
          /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between border-b border-border bg-card px-5 py-4", children: [
            /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
              /* @__PURE__ */ jsx(Tag, { className: "size-4 text-foreground" }),
              /* @__PURE__ */ jsx("span", { className: "text-sm font-bold text-foreground", children: t("customers.personTags.title", "Edit tags") })
            ] }),
            /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
              /* @__PURE__ */ jsxs(
                Button,
                {
                  type: "button",
                  variant: "outline",
                  size: "sm",
                  className: "h-auto gap-2 rounded-lg px-3.5 py-2 text-sm font-medium",
                  onClick: () => setManageTagsOpen(true),
                  children: [
                    /* @__PURE__ */ jsx(SlidersHorizontal, { className: "size-3.5" }),
                    t("customers.personTags.settingsButton", "Tag settings")
                  ]
                }
              ),
              /* @__PURE__ */ jsx(
                IconButton,
                {
                  type: "button",
                  variant: "outline",
                  size: "xs",
                  className: "size-7 rounded-sm border-border bg-background",
                  onClick: onClose,
                  children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
                }
              )
            ] })
          ] }),
          /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-hidden bg-background", children: loading ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center px-6 py-8 text-center text-sm text-muted-foreground", children: t("customers.personTags.loading", "Loading...") }) : /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col gap-4 px-5 py-4 md:flex-row", children: [
            /* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto pb-1 md:w-[220px] md:shrink-0 md:flex-col md:overflow-x-visible md:pb-0", children: categories.map((category) => {
              const count = selectedValues[category.kind]?.size ?? 0;
              const isActive = activeCategory?.kind === category.kind;
              return /* @__PURE__ */ jsxs(
                Button,
                {
                  type: "button",
                  variant: isActive ? "secondary" : "ghost",
                  size: "sm",
                  className: cn(
                    "h-auto min-w-[140px] justify-between rounded-lg px-3 py-2 text-left md:w-full",
                    isActive ? "border border-border bg-muted text-foreground" : "border border-transparent text-muted-foreground"
                  ),
                  onClick: () => setActiveCategoryKind(category.kind),
                  children: [
                    /* @__PURE__ */ jsx("span", { className: "truncate text-xs font-medium", children: category.label }),
                    /* @__PURE__ */ jsx("span", { className: "ml-3 shrink-0 rounded-full bg-background px-2 py-0.5 text-xs font-semibold text-muted-foreground", children: count })
                  ]
                },
                category.kind
              );
            }) }),
            /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto rounded-xl border border-border bg-card", children: activeCategory ? /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col", children: [
              /* @__PURE__ */ jsx("div", { className: "border-b border-border px-4 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
                /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
                  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground", children: activeCategory.label }),
                  /* @__PURE__ */ jsx("p", { className: "max-w-[520px] text-xs leading-5 text-muted-foreground", children: activeCategory.description })
                ] }),
                /* @__PURE__ */ jsx("div", { className: "shrink-0 rounded-full border border-border bg-background px-3 py-1 text-xs font-medium text-muted-foreground", children: t("customers.personTags.activeCount", "{{count}} selected", {
                  count: activeSelection.size
                }) })
              ] }) }),
              /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto px-4 py-4", children: [
                /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-input bg-background px-3 py-2", children: [
                  /* @__PURE__ */ jsx(Search, { className: "size-3.5 shrink-0 text-muted-foreground" }),
                  /* @__PURE__ */ jsx(
                    "input",
                    {
                      type: "text",
                      value: searchValue,
                      onChange: (event) => setSearchValue(event.target.value),
                      placeholder: t(
                        "customers.personTags.searchPlaceholder",
                        "Search {{category}}...",
                        { category: activeCategory.label.toLowerCase() }
                      ),
                      className: "flex-1 bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground"
                    }
                  )
                ] }),
                filteredEntries.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
                  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-x-1.5 gap-y-2", children: filteredEntries.map((entry) => /* @__PURE__ */ jsx(
                    TagChip,
                    {
                      label: entry.label,
                      color: entry.color,
                      active: activeSelection.has(entry.value),
                      showColorDot: activeCategory.hasColorDots,
                      onClick: () => toggleValue(
                        activeCategory.kind,
                        entry.value,
                        activeCategory.selectionMode
                      )
                    },
                    `${activeCategory.kind}:${entry.value}`
                  )) }),
                  activeCategoryLoading ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: t("customers.personTags.loading", "Loading...") }) : null,
                  (activeCategory.source === "tags" || activeCategory.source === "labels") && activeCategoryPage < activeCategoryTotalPages ? /* @__PURE__ */ jsx(
                    Button,
                    {
                      type: "button",
                      variant: "outline",
                      size: "sm",
                      className: "rounded-lg px-3 text-xs",
                      onClick: () => setActiveCategoryPage((current) => current + 1),
                      children: t("customers.activities.loadMore", "Load more")
                    }
                  ) : null
                ] }) : activeCategoryLoading ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-border bg-background px-4 py-6 text-center text-sm text-muted-foreground", children: t("customers.personTags.loading", "Loading...") }) : /* @__PURE__ */ jsx(
                  EmptyState,
                  {
                    size: "sm",
                    icon: /* @__PURE__ */ jsx(SearchX, { className: "h-8 w-8", "aria-hidden": "true" }),
                    title: searchValue.trim().length > 0 ? t("customers.personTags.emptySearchResults", "No options match the current search.") : activeCategory.source === "dictionary" ? t("customers.personTags.emptyDictionaryCategory", "No options are configured for this category yet.") : t("customers.personTags.emptyCategory", "No items have been added for this category yet.")
                  }
                ),
                activeCategory.supportsCreate ? /* @__PURE__ */ jsx("div", { children: newEntryInputByKind[activeCategory.kind] !== null ? /* @__PURE__ */ jsx("div", { className: "inline-flex items-center rounded-full border border-dashed border-status-success-border bg-status-success-bg/70 px-2.5 py-1", children: /* @__PURE__ */ jsx(
                  "input",
                  {
                    type: "text",
                    autoFocus: true,
                    value: newEntryInputByKind[activeCategory.kind] ?? "",
                    disabled: creatingKind === activeCategory.kind,
                    onChange: (event) => setNewEntryInputByKind((previous) => ({
                      ...previous,
                      [activeCategory.kind]: event.target.value
                    })),
                    onKeyDown: (event) => {
                      if (event.key === "Enter") {
                        event.preventDefault();
                        void handleCreateEntry();
                      }
                      if (event.key === "Escape") {
                        setNewEntryInputByKind((previous) => ({
                          ...previous,
                          [activeCategory.kind]: null
                        }));
                      }
                    },
                    onBlur: () => {
                      const value = newEntryInputByKind[activeCategory.kind] ?? "";
                      if (creationInFlightRef.current === activeCategory.kind) {
                        return;
                      }
                      if (value.trim()) {
                        void handleCreateEntry();
                      } else {
                        setNewEntryInputByKind((previous) => ({
                          ...previous,
                          [activeCategory.kind]: null
                        }));
                      }
                    },
                    placeholder: activeCategory.kind === "tags" ? t("customers.people.detail.tags.placeholder", "Type to add tags") : t("customers.personTags.newLabelPlaceholder", "Label name..."),
                    className: "w-[150px] bg-transparent text-xs font-semibold text-status-success-text outline-none placeholder:text-status-success-text/60 disabled:cursor-wait disabled:opacity-70"
                  }
                ) }) : /* @__PURE__ */ jsxs(
                  Button,
                  {
                    type: "button",
                    variant: "ghost",
                    size: "sm",
                    disabled: creatingKind === activeCategory.kind,
                    onClick: () => setNewEntryInputByKind((previous) => ({
                      ...previous,
                      [activeCategory.kind]: ""
                    })),
                    className: "inline-flex h-auto items-center gap-1 rounded-full border border-dashed border-status-success-border bg-transparent px-2.5 py-1.5 font-semibold text-status-success-text hover:bg-status-success-bg disabled:opacity-60",
                    children: [
                      /* @__PURE__ */ jsx(Plus, { className: "size-2.5" }),
                      /* @__PURE__ */ jsx("span", { className: "text-xs", children: activeCategory.kind === "tags" ? t("customers.personTags.newTag", "New tag") : t("customers.personTags.newLabel", "New label") })
                    ]
                  }
                ) }) : null
              ] })
            ] }) : /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center px-6 py-8 text-center text-sm text-muted-foreground", children: t("customers.personTags.emptyCategory", "No items have been added for this category yet.") }) })
          ] }) }),
          /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between border-t border-border bg-muted/20 px-5 py-3.5", children: [
            /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t("customers.personTags.activeCount", "{{count}} selected", { count: activeCount }) }),
            /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-6", children: [
              /* @__PURE__ */ jsx(
                Button,
                {
                  type: "button",
                  variant: "outline",
                  onClick: onClose,
                  className: "rounded-md border-border bg-background px-4 py-2 text-sm font-semibold text-foreground",
                  children: t("customers.personTags.cancel", "Cancel")
                }
              ),
              /* @__PURE__ */ jsxs(
                Button,
                {
                  type: "button",
                  onClick: () => {
                    void handleSave();
                  },
                  disabled: saving || !hasChanges,
                  className: "rounded-md bg-foreground px-4 py-2 text-sm font-semibold text-background hover:bg-foreground/90",
                  children: [
                    /* @__PURE__ */ jsx(Check, { className: "mr-2 size-3.5" }),
                    saving ? t("customers.personTags.saving", "Saving...") : t("customers.personTags.save", "Save")
                  ]
                }
              )
            ] })
          ] })
        ]
      }
    ),
    /* @__PURE__ */ jsx(
      ManageTagsDialog,
      {
        open: manageTagsOpen,
        onClose: () => {
          setManageTagsOpen(false);
          void loadData();
        }
      }
    )
  ] });
}
export {
  EntityTagsDialog
};
//# sourceMappingURL=EntityTagsDialog.js.map
