"use client";
import { jsx, jsxs } from "react/jsx-runtime";
import * as React from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useT } from "@open-mercato/shared/lib/i18n/context";
import { cn } from "@open-mercato/shared/lib/utils";
import { flash } from "@open-mercato/ui/backend/FlashMessages";
import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
import { useCustomFieldDefs } from "@open-mercato/ui/backend/utils/customFieldDefs";
import { Alert, AlertDescription, AlertTitle } from "@open-mercato/ui/primitives/alert";
import { Badge } from "@open-mercato/ui/primitives/badge";
import { StatusBadge } from "@open-mercato/ui/primitives/status-badge";
import { Button } from "@open-mercato/ui/primitives/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@open-mercato/ui/primitives/card";
import { Input } from "@open-mercato/ui/primitives/input";
import { Label } from "@open-mercato/ui/primitives/label";
import { Progress } from "@open-mercato/ui/primitives/progress";
import { Spinner } from "@open-mercato/ui/primitives/spinner";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@open-mercato/ui/primitives/table";
import { AlertTriangle, CheckCircle2, Play, RefreshCw, Upload, XCircle } from "lucide-react";
import {
  buildPeopleSuggestedMapping,
  buildPeopleTargetOptions,
  buildSuggestedMappingSignature,
  findMappingTargetOption,
  SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS
} from "./target-options.js";
const ENTITY_TYPE = "customers.person";
const SESSION_STORAGE_PREFIX = "om:sync_excel:session";
const RUN_POLL_INTERVAL_MS = 4e3;
const SELECT_CLASS_NAME = "flex h-9 w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50";
function buildMappingRows(headers, suggestedMapping) {
  const mappedFields = new Map(suggestedMapping.fields.map((field) => [field.externalField, field.localField]));
  return headers.map((header) => ({
    sourceColumn: header,
    targetField: mappedFields.get(header) ?? ""
  }));
}
function buildFieldMapping(sourceColumn, targetField, targetOptions) {
  const matchedOption = findMappingTargetOption(targetOptions, targetField);
  const mappingKind = matchedOption?.mappingKind ?? (targetField.startsWith("cf:") ? "custom_field" : "core");
  const dedupeRole = matchedOption?.dedupeRole;
  return {
    externalField: sourceColumn,
    localField: targetField,
    mappingKind,
    ...dedupeRole ? { dedupeRole } : {}
  };
}
function inferMatchStrategy(rows) {
  if (rows.some((row) => row.targetField === "person.externalId")) return "externalId";
  if (rows.some((row) => row.targetField === "person.primaryEmail")) return "email";
  return "custom";
}
function buildMapping(rows, matchStrategy, targetOptions) {
  const mappedRows = rows.filter((row) => row.targetField.trim().length > 0);
  const fields = mappedRows.map((row) => buildFieldMapping(row.sourceColumn, row.targetField, targetOptions));
  const resolvedMatchStrategy = matchStrategy === "custom" ? inferMatchStrategy(rows) : matchStrategy;
  const matchField = resolvedMatchStrategy === "externalId" ? "person.externalId" : resolvedMatchStrategy === "email" ? "person.primaryEmail" : void 0;
  return {
    entityType: ENTITY_TYPE,
    matchStrategy: resolvedMatchStrategy,
    ...matchField ? { matchField } : {},
    fields,
    unmappedColumns: rows.filter((row) => row.targetField.trim().length === 0).map((row) => row.sourceColumn)
  };
}
function buildDiagnostics(rows) {
  const duplicateTargets = Array.from(
    rows.filter((row) => row.targetField.trim().length > 0).reduce((accumulator, row) => {
      const nextCount = (accumulator.get(row.targetField) ?? 0) + 1;
      accumulator.set(row.targetField, nextCount);
      return accumulator;
    }, /* @__PURE__ */ new Map()).entries()
  ).filter(([, count]) => count > 1).map(([target]) => target);
  const hasNameTarget = rows.some((row) => row.targetField === "person.lastName" || row.targetField === "person.displayName");
  const hasIdentityTarget = rows.some((row) => row.targetField === "person.externalId" || row.targetField === "person.primaryEmail");
  const mappedCount = rows.filter((row) => row.targetField.trim().length > 0).length;
  return {
    duplicateTargets,
    hasNameTarget,
    hasIdentityTarget,
    mappedCount,
    unmappedCount: rows.length - mappedCount
  };
}
function formatTargetLabel(targetField, t, targetOptions) {
  const option = findMappingTargetOption(targetOptions, targetField);
  if (!option) return targetField;
  return option.labelKey ? t(option.labelKey, option.fallback) : option.fallback;
}
function getMatchStrategyLabel(matchStrategy, t) {
  if (matchStrategy === "externalId") {
    return t("sync_excel.widget.matchStrategy.externalId", "Source record ID (Recommended)");
  }
  if (matchStrategy === "email") {
    return t("sync_excel.widget.matchStrategy.email", "Email address");
  }
  return t("sync_excel.widget.matchStrategy.custom", "Custom matching");
}
function formatFileSize(bytes) {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
function readApiErrorMessage(result, fallback) {
  const value = result?.error;
  if (typeof value === "string" && value.trim().length > 0) return value;
  return fallback;
}
function normalizeStatus(status) {
  if (status === "pending" || status === "running" || status === "completed" || status === "failed" || status === "cancelled") {
    return status;
  }
  return "idle";
}
function getSessionStorageKey(integrationId) {
  return `${SESSION_STORAGE_PREFIX}:${integrationId ?? "sync_excel"}`;
}
function readPersistedSessionSnapshot(integrationId) {
  if (typeof window === "undefined") return null;
  try {
    const raw = window.sessionStorage.getItem(getSessionStorageKey(integrationId));
    if (!raw) return null;
    const parsed = JSON.parse(raw);
    if (!parsed || typeof parsed !== "object") return null;
    if (typeof parsed.uploadId !== "string" || parsed.uploadId.trim().length === 0) return null;
    if (typeof parsed.filename !== "string") return null;
    if (!Array.isArray(parsed.mappingRows)) return null;
    if (parsed.matchStrategy !== "externalId" && parsed.matchStrategy !== "email" && parsed.matchStrategy !== "custom") {
      return null;
    }
    const preview = parsed.preview && typeof parsed.preview === "object" && typeof parsed.preview.uploadId === "string" && parsed.preview.uploadId.trim().length > 0 && typeof parsed.preview.filename === "string" && Array.isArray(parsed.preview.headers) && Array.isArray(parsed.preview.sampleRows) && typeof parsed.preview.totalRows === "number" && typeof parsed.preview.fileSize === "number" && parsed.preview.entityType === ENTITY_TYPE && parsed.preview.suggestedMapping ? parsed.preview : null;
    return {
      uploadId: parsed.uploadId,
      filename: parsed.filename,
      preview,
      mappingRows: parsed.mappingRows.filter((row) => Boolean(row) && typeof row.sourceColumn === "string" && typeof row.targetField === "string"),
      matchStrategy: parsed.matchStrategy,
      runId: typeof parsed.runId === "string" && parsed.runId.trim().length > 0 ? parsed.runId : null,
      progressJobId: typeof parsed.progressJobId === "string" && parsed.progressJobId.trim().length > 0 ? parsed.progressJobId : null
    };
  } catch {
    return null;
  }
}
function writePersistedSessionSnapshot(integrationId, snapshot) {
  if (typeof window === "undefined") return;
  try {
    const key = getSessionStorageKey(integrationId);
    if (!snapshot) {
      window.sessionStorage.removeItem(key);
      return;
    }
    window.sessionStorage.setItem(key, JSON.stringify(snapshot));
  } catch {
  }
}
function SyncExcelUploadConfigWidget({
  context,
  data
}) {
  const pathname = usePathname();
  const router = useRouter();
  const searchParams = useSearchParams();
  const t = useT();
  const { data: customFieldDefs = [] } = useCustomFieldDefs([...SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS]);
  const [selectedFile, setSelectedFile] = React.useState(null);
  const [preview, setPreview] = React.useState(null);
  const [mappingRows, setMappingRows] = React.useState([]);
  const [matchStrategy, setMatchStrategy] = React.useState("custom");
  const [isMappingDirty, setIsMappingDirty] = React.useState(false);
  const [runDetail, setRunDetail] = React.useState(null);
  const [runId, setRunId] = React.useState(null);
  const [progressJobId, setProgressJobId] = React.useState(null);
  const [isUploading, setIsUploading] = React.useState(false);
  const [isRefreshingPreview, setIsRefreshingPreview] = React.useState(false);
  const [isStartingImport, setIsStartingImport] = React.useState(false);
  const [isCancelling, setIsCancelling] = React.useState(false);
  const [isRestoringSession, setIsRestoringSession] = React.useState(true);
  const mutationContextId = `${context.formId ?? "sync-excel"}:sync-excel-import`;
  const lastAppliedSuggestionSignatureRef = React.useRef(null);
  const restoringSnapshotRef = React.useRef(null);
  const lastRestoreRequestKeyRef = React.useRef(null);
  const { runMutation } = useGuardedMutation({
    contextId: mutationContextId,
    spotId: context.integrationDetailWidgetSpotId
  });
  const targetOptions = React.useMemo(
    () => buildPeopleTargetOptions(customFieldDefs),
    [customFieldDefs]
  );
  const previewSuggestion = React.useMemo(
    () => preview ? buildPeopleSuggestedMapping(preview.headers, preview.suggestedMapping, customFieldDefs) : null,
    [customFieldDefs, preview]
  );
  const resolvedIntegrationEnabled = data?.state?.isEnabled ?? context.state?.isEnabled ?? true;
  const diagnostics = React.useMemo(() => buildDiagnostics(mappingRows), [mappingRows]);
  const canStartImport = Boolean(preview) && diagnostics.duplicateTargets.length === 0 && diagnostics.hasNameTarget && diagnostics.hasIdentityTarget && mappingRows.some((row) => row.targetField.trim().length > 0);
  const importStatus = normalizeStatus(runDetail?.status ?? (runId ? "pending" : null));
  const progressValue = runDetail?.progressJob?.progressPercent ?? 0;
  const isRunActive = importStatus === "pending" || importStatus === "running";
  const searchParamsKey = searchParams?.toString() ?? "";
  const uploadIdFromUrl = searchParams?.get("uploadId") ?? null;
  const runIdFromUrl = searchParams?.get("runId") ?? null;
  const shouldHideInteractiveContent = isRestoringSession && Boolean(uploadIdFromUrl);
  const hasExistingRunForCurrentUpload = Boolean(preview?.uploadId && runId);
  const hasSafeDedupeStrategy = matchStrategy === "externalId" || matchStrategy === "email";
  const replaceQueryParams = React.useCallback((updates) => {
    const params = new URLSearchParams(searchParamsKey);
    for (const [key, value] of Object.entries(updates)) {
      if (typeof value === "string" && value.trim().length > 0) {
        params.set(key, value);
      } else {
        params.delete(key);
      }
    }
    const query = params.toString();
    if (query === searchParamsKey) return;
    router.replace(query ? `${pathname}?${query}` : pathname);
  }, [pathname, router, searchParamsKey]);
  const clearPersistedSession = React.useCallback(() => {
    writePersistedSessionSnapshot(context.integrationId, null);
    replaceQueryParams({ uploadId: null, runId: null });
  }, [context.integrationId, replaceQueryParams]);
  const persistCurrentSession = React.useCallback((next) => {
    const persistedPreview = next?.preview ?? preview ?? null;
    const uploadId = next?.uploadId ?? persistedPreview?.uploadId ?? null;
    const filename = next?.filename ?? persistedPreview?.filename ?? null;
    if (!uploadId || !filename) {
      writePersistedSessionSnapshot(context.integrationId, null);
      return;
    }
    writePersistedSessionSnapshot(context.integrationId, {
      uploadId,
      filename,
      preview: persistedPreview,
      mappingRows: next?.mappingRows ?? mappingRows,
      matchStrategy: next?.matchStrategy ?? matchStrategy,
      runId: next?.runId ?? runId,
      progressJobId: next?.progressJobId ?? progressJobId
    });
  }, [context.integrationId, mappingRows, matchStrategy, preview, progressJobId, runId]);
  const syncPreviewState = React.useCallback((nextPreview, options) => {
    const nextSuggestedMapping = buildPeopleSuggestedMapping(nextPreview.headers, nextPreview.suggestedMapping, customFieldDefs);
    setPreview(nextPreview);
    const restoredSnapshot = options?.restoredSnapshot;
    const shouldRestoreSnapshot = Boolean(
      restoredSnapshot && restoredSnapshot.uploadId === nextPreview.uploadId && restoredSnapshot.mappingRows.length > 0
    );
    if (shouldRestoreSnapshot) {
      setMappingRows(restoredSnapshot.mappingRows);
      setMatchStrategy(restoredSnapshot.matchStrategy);
      setRunId(restoredSnapshot.runId);
      setProgressJobId(restoredSnapshot.progressJobId);
      setIsMappingDirty(true);
      lastAppliedSuggestionSignatureRef.current = null;
      persistCurrentSession({
        uploadId: nextPreview.uploadId,
        filename: nextPreview.filename,
        preview: nextPreview,
        mappingRows: restoredSnapshot.mappingRows,
        matchStrategy: restoredSnapshot.matchStrategy,
        runId: restoredSnapshot.runId,
        progressJobId: restoredSnapshot.progressJobId
      });
      replaceQueryParams({
        uploadId: nextPreview.uploadId,
        runId: restoredSnapshot.runId
      });
      return;
    }
    if (options?.preserveManualState && preview?.uploadId === nextPreview.uploadId) {
      persistCurrentSession({
        uploadId: nextPreview.uploadId,
        filename: nextPreview.filename,
        preview: nextPreview
      });
      replaceQueryParams({ uploadId: nextPreview.uploadId });
      return;
    }
    const nextRows = buildMappingRows(nextPreview.headers, nextSuggestedMapping);
    setMappingRows(nextRows);
    setMatchStrategy(nextSuggestedMapping.matchStrategy);
    setIsMappingDirty(false);
    lastAppliedSuggestionSignatureRef.current = buildSuggestedMappingSignature(nextPreview.headers, nextSuggestedMapping);
    persistCurrentSession({
      uploadId: nextPreview.uploadId,
      filename: nextPreview.filename,
      preview: nextPreview,
      mappingRows: nextRows,
      matchStrategy: nextSuggestedMapping.matchStrategy,
      runId: null,
      progressJobId: null
    });
    replaceQueryParams({
      uploadId: nextPreview.uploadId,
      runId: null
    });
  }, [customFieldDefs, persistCurrentSession, preview?.uploadId, replaceQueryParams]);
  React.useEffect(() => {
    if (!preview || !previewSuggestion) return;
    if (isMappingDirty) return;
    const nextSignature = buildSuggestedMappingSignature(preview.headers, previewSuggestion);
    if (lastAppliedSuggestionSignatureRef.current === nextSignature) return;
    setMappingRows(buildMappingRows(preview.headers, previewSuggestion));
    setMatchStrategy(previewSuggestion.matchStrategy);
    lastAppliedSuggestionSignatureRef.current = nextSignature;
  }, [isMappingDirty, preview, previewSuggestion]);
  const refreshRunDetail = React.useCallback(async (currentRunId) => {
    const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(currentRunId)}`, void 0, { fallback: null });
    if (call.ok && call.result) {
      setRunDetail(call.result);
      setProgressJobId(call.result.progressJobId ?? null);
      persistCurrentSession({
        runId: currentRunId,
        progressJobId: call.result.progressJobId ?? null
      });
      return call.result;
    }
    return null;
  }, [persistCurrentSession]);
  React.useEffect(() => {
    if (!preview?.uploadId) return;
    persistCurrentSession();
  }, [mappingRows, matchStrategy, persistCurrentSession, preview?.uploadId, progressJobId, runId]);
  React.useEffect(() => {
    const persistedSnapshot = readPersistedSessionSnapshot(context.integrationId);
    const resolvedUploadId = uploadIdFromUrl ?? persistedSnapshot?.uploadId ?? null;
    const resolvedRunId = runIdFromUrl ?? persistedSnapshot?.runId ?? null;
    if (!resolvedUploadId) {
      restoringSnapshotRef.current = null;
      lastRestoreRequestKeyRef.current = null;
      setIsRestoringSession(false);
      return;
    }
    if (!uploadIdFromUrl && persistedSnapshot?.uploadId === resolvedUploadId) {
      replaceQueryParams({
        uploadId: resolvedUploadId,
        runId: resolvedRunId
      });
    }
    if (preview?.uploadId === resolvedUploadId && mappingRows.length > 0) {
      if (resolvedRunId && runId !== resolvedRunId) {
        setRunId(resolvedRunId);
      }
      setIsRestoringSession(false);
      return;
    }
    const restoreRequestKey = `${resolvedUploadId}:${resolvedRunId ?? ""}`;
    if (lastRestoreRequestKeyRef.current === restoreRequestKey) {
      setIsRestoringSession(false);
      return;
    }
    lastRestoreRequestKeyRef.current = restoreRequestKey;
    let cancelled = false;
    restoringSnapshotRef.current = persistedSnapshot;
    const persistedPreview = restoringSnapshotRef.current?.preview?.uploadId === resolvedUploadId ? restoringSnapshotRef.current.preview : null;
    if (persistedPreview) {
      syncPreviewState(persistedPreview, {
        restoredSnapshot: restoringSnapshotRef.current
      });
      setIsRestoringSession(false);
    } else {
      setIsRestoringSession(true);
    }
    const restoreSession = async () => {
      const call = await apiCall(
        `/api/sync_excel/preview?uploadId=${encodeURIComponent(resolvedUploadId)}&entityType=${encodeURIComponent(ENTITY_TYPE)}`,
        void 0,
        { fallback: null }
      );
      if (cancelled) return;
      if (!call.ok || !call.result) {
        setPreview(null);
        setMappingRows([]);
        setMatchStrategy("custom");
        setRunId(null);
        setRunDetail(null);
        setProgressJobId(null);
        clearPersistedSession();
        lastRestoreRequestKeyRef.current = null;
        setIsRestoringSession(false);
        return;
      }
      syncPreviewState(call.result, {
        restoredSnapshot: restoringSnapshotRef.current
      });
      if (resolvedRunId && !restoringSnapshotRef.current?.runId) {
        setRunId(resolvedRunId);
      }
      setIsRestoringSession(false);
    };
    void restoreSession();
    return () => {
      cancelled = true;
    };
  }, [clearPersistedSession, context.integrationId, mappingRows.length, preview?.uploadId, replaceQueryParams, runId, runIdFromUrl, syncPreviewState, uploadIdFromUrl]);
  React.useEffect(() => {
    if (!runId) return;
    let cancelled = false;
    let timeoutId = null;
    const poll = async () => {
      const detail = await refreshRunDetail(runId);
      if (cancelled) return;
      if (context.activeTab === "logs") {
        await context.refreshLogs?.();
      }
      if (context.activeTab === "health" || normalizeStatus(detail?.status) !== "idle") {
        await context.refreshHealthSnapshot?.();
      }
      const status = normalizeStatus(detail?.status);
      if (status === "pending" || status === "running") {
        timeoutId = setTimeout(() => {
          void poll();
        }, RUN_POLL_INTERVAL_MS);
      }
    };
    void poll();
    return () => {
      cancelled = true;
      if (timeoutId) clearTimeout(timeoutId);
    };
  }, [context.activeTab, context.refreshHealthSnapshot, context.refreshLogs, refreshRunDetail, runId]);
  const handleUpload = React.useCallback(async () => {
    if (!selectedFile) {
      flash(t("sync_excel.widget.messages.fileRequired", "Select a CSV file first."), "error");
      return;
    }
    setIsUploading(true);
    try {
      const formData = new FormData();
      formData.set("file", selectedFile);
      formData.set("entityType", ENTITY_TYPE);
      const call = await runMutation({
        operation: () => apiCall("/api/sync_excel/upload", {
          method: "POST",
          body: formData
        }, { fallback: null }),
        mutationPayload: {
          integrationId: context.integrationId ?? "sync_excel",
          entityType: ENTITY_TYPE,
          fileName: selectedFile.name
        },
        context: {
          ...context,
          operation: "create",
          actionId: "sync-excel-upload"
        }
      });
      if (!call.ok || !call.result) {
        flash(
          readApiErrorMessage(call.result, t("sync_excel.widget.messages.uploadError", "Failed to upload CSV file.")),
          "error"
        );
        return;
      }
      syncPreviewState(call.result);
      setSelectedFile(null);
      setRunId(null);
      setRunDetail(null);
      setProgressJobId(null);
      flash(t("sync_excel.widget.messages.uploadSuccess", "CSV preview is ready."), "success");
    } catch {
      flash(t("sync_excel.widget.messages.uploadError", "Failed to upload CSV file."), "error");
    } finally {
      setIsUploading(false);
    }
  }, [context, runMutation, selectedFile, syncPreviewState, t]);
  const handleRefreshPreview = React.useCallback(async () => {
    if (!preview) return;
    setIsRefreshingPreview(true);
    try {
      const call = await apiCall(
        `/api/sync_excel/preview?uploadId=${encodeURIComponent(preview.uploadId)}&entityType=${encodeURIComponent(ENTITY_TYPE)}`,
        void 0,
        { fallback: null }
      );
      if (!call.ok || !call.result) {
        flash(t("sync_excel.widget.messages.previewError", "Failed to refresh preview."), "error");
        return;
      }
      syncPreviewState(call.result, { preserveManualState: true });
      flash(t("sync_excel.widget.messages.previewRefreshed", "Preview refreshed."), "success");
    } catch {
      flash(t("sync_excel.widget.messages.previewError", "Failed to refresh preview."), "error");
    } finally {
      setIsRefreshingPreview(false);
    }
  }, [preview, syncPreviewState, t]);
  const handleTargetFieldChange = React.useCallback((sourceColumn, targetField) => {
    setIsMappingDirty(true);
    setMappingRows((current) => current.map((row) => row.sourceColumn === sourceColumn ? { ...row, targetField } : row));
  }, []);
  const handleResetToSuggested = React.useCallback(() => {
    if (!preview || !previewSuggestion) return;
    setMappingRows(buildMappingRows(preview.headers, previewSuggestion));
    setMatchStrategy(previewSuggestion.matchStrategy);
    setIsMappingDirty(false);
    lastAppliedSuggestionSignatureRef.current = buildSuggestedMappingSignature(preview.headers, previewSuggestion);
    flash(t("sync_excel.widget.messages.mappingReset", "Suggested mapping restored."), "success");
  }, [preview, previewSuggestion, t]);
  const handleStartImport = React.useCallback(async () => {
    if (!preview) return;
    const mapping = buildMapping(mappingRows, matchStrategy, targetOptions);
    setIsStartingImport(true);
    try {
      const call = await runMutation({
        operation: () => apiCall("/api/sync_excel/import", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            uploadId: preview.uploadId,
            entityType: ENTITY_TYPE,
            mapping
          })
        }, { fallback: null }),
        mutationPayload: {
          integrationId: context.integrationId ?? "sync_excel",
          uploadId: preview.uploadId,
          entityType: ENTITY_TYPE,
          mapping
        },
        context: {
          ...context,
          operation: "create",
          actionId: "sync-excel-start-import"
        }
      });
      if (!call.ok || !call.result) {
        flash(
          readApiErrorMessage(call.result, t("sync_excel.widget.messages.importError", "Failed to start import run.")),
          "error"
        );
        return;
      }
      setRunId(call.result.runId);
      setProgressJobId(call.result.progressJobId);
      replaceQueryParams({
        uploadId: preview.uploadId,
        runId: call.result.runId
      });
      persistCurrentSession({
        uploadId: preview.uploadId,
        filename: preview.filename,
        runId: call.result.runId,
        progressJobId: call.result.progressJobId
      });
      flash(t("sync_excel.widget.messages.importStarted", "Import run started."), "success");
      await refreshRunDetail(call.result.runId);
      await context.refreshLogs?.();
      await context.refreshHealthSnapshot?.();
    } catch {
      flash(t("sync_excel.widget.messages.importError", "Failed to start import run."), "error");
    } finally {
      setIsStartingImport(false);
    }
  }, [context, mappingRows, matchStrategy, persistCurrentSession, preview, refreshRunDetail, replaceQueryParams, runMutation, t, targetOptions]);
  const handleCancelRun = React.useCallback(async () => {
    if (!runId) return;
    setIsCancelling(true);
    try {
      const call = await runMutation({
        operation: () => apiCall(`/api/data_sync/runs/${encodeURIComponent(runId)}/cancel`, {
          method: "POST"
        }, { fallback: null }),
        mutationPayload: {
          integrationId: context.integrationId ?? "sync_excel",
          runId
        },
        context: {
          ...context,
          operation: "update",
          actionId: "sync-excel-cancel-import"
        }
      });
      if (!call.ok) {
        flash(t("sync_excel.widget.messages.cancelError", "Failed to cancel import run."), "error");
        return;
      }
      flash(t("sync_excel.widget.messages.cancelSuccess", "Import run cancelled."), "success");
      await refreshRunDetail(runId);
      await context.refreshLogs?.();
      await context.refreshHealthSnapshot?.();
    } catch {
      flash(t("sync_excel.widget.messages.cancelError", "Failed to cancel import run."), "error");
    } finally {
      setIsCancelling(false);
    }
  }, [context, refreshRunDetail, runId, runMutation, t]);
  const handleRefreshAll = React.useCallback(async () => {
    if (!runId) return;
    await refreshRunDetail(runId);
    await context.refreshLogs?.();
    await context.refreshHealthSnapshot?.();
  }, [context, refreshRunDetail, runId]);
  const firstSampleRow = preview?.sampleRows[0] ?? null;
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
    isRestoringSession ? /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "flex items-center gap-3 py-6", children: [
      /* @__PURE__ */ jsx(Spinner, { className: "size-4" }),
      /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("sync_excel.widget.messages.restoringSession", "Restoring the last CSV session...") })
    ] }) }) : null,
    !resolvedIntegrationEnabled ? /* @__PURE__ */ jsx(
      StatusAlert,
      {
        variant: "warning",
        title: t("sync_excel.widget.disabled.title", "Integration disabled"),
        message: t("sync_excel.widget.disabled.message", "Enable this integration to upload CSV files and start imports.")
      }
    ) : null,
    !shouldHideInteractiveContent ? /* @__PURE__ */ jsxs(Card, { children: [
      /* @__PURE__ */ jsxs(CardHeader, { children: [
        /* @__PURE__ */ jsx(CardTitle, { children: t("sync_excel.widget.upload.title", "Upload CSV") }),
        /* @__PURE__ */ jsx(CardDescription, { children: t("sync_excel.widget.upload.description", "Upload a CSV file, review the suggested people mapping, and start a background import run.") })
      ] }),
      /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
        /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
          /* @__PURE__ */ jsx(Label, { htmlFor: "sync-excel-upload-file", children: t("sync_excel.widget.upload.fileLabel", "CSV file") }),
          /* @__PURE__ */ jsx(
            Input,
            {
              id: "sync-excel-upload-file",
              type: "file",
              accept: ".csv,text/csv",
              onChange: (event) => setSelectedFile(event.target.files?.[0] ?? null),
              disabled: isUploading || isRunActive || isRestoringSession
            }
          )
        ] }),
        /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
          /* @__PURE__ */ jsxs(Button, { type: "button", onClick: () => void handleUpload(), disabled: !selectedFile || isUploading || isRunActive || isRestoringSession, children: [
            isUploading ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 size-4" }) : /* @__PURE__ */ jsx(Upload, { className: "mr-2 size-4" }),
            isUploading ? t("sync_excel.widget.actions.uploadPending", "Uploading...") : t("sync_excel.widget.actions.upload", "Upload and preview")
          ] }),
          preview ? /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", onClick: () => void handleRefreshPreview(), disabled: isRefreshingPreview || isUploading || isRunActive || isRestoringSession, children: [
            isRefreshingPreview ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 size-4" }) : /* @__PURE__ */ jsx(RefreshCw, { className: "mr-2 size-4" }),
            isRefreshingPreview ? t("sync_excel.widget.actions.refreshPending", "Refreshing...") : t("sync_excel.widget.actions.refresh", "Refresh preview")
          ] }) : null
        ] }),
        selectedFile ? /* @__PURE__ */ jsxs("div", { className: "text-sm text-muted-foreground", children: [
          t("sync_excel.widget.upload.selectedFile", "Selected file"),
          ": ",
          /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: selectedFile.name })
        ] }) : null
      ] })
    ] }) : null,
    preview && !shouldHideInteractiveContent ? /* @__PURE__ */ jsxs(Card, { children: [
      /* @__PURE__ */ jsx(CardHeader, { 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(CardTitle, { children: t("sync_excel.widget.preview.title", "Preview and mapping") }),
          /* @__PURE__ */ jsx(CardDescription, { children: t("sync_excel.widget.preview.description", "Every source column stays visible here. Leave a target empty to keep the column unmapped for now.") })
        ] }),
        /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
          /* @__PURE__ */ jsx(Badge, { variant: "outline", children: preview.filename }),
          /* @__PURE__ */ jsx(Badge, { variant: "outline", children: t("sync_excel.widget.preview.rows", "{count} rows", { count: preview.totalRows }) }),
          /* @__PURE__ */ jsx(Badge, { variant: "outline", children: formatFileSize(preview.fileSize) })
        ] })
      ] }) }),
      /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
        /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4", children: [
          /* @__PURE__ */ jsx(
            MetricCard,
            {
              label: t("sync_excel.widget.metrics.mapped", "Mapped columns"),
              value: String(diagnostics.mappedCount)
            }
          ),
          /* @__PURE__ */ jsx(
            MetricCard,
            {
              label: t("sync_excel.widget.metrics.unmapped", "Unmapped columns"),
              value: String(diagnostics.unmappedCount)
            }
          ),
          /* @__PURE__ */ jsx(
            MetricCard,
            {
              label: t("sync_excel.widget.metrics.identity", "Identity strategy"),
              value: getMatchStrategyLabel(matchStrategy, t)
            }
          ),
          /* @__PURE__ */ jsx(
            MetricCard,
            {
              label: t("sync_excel.widget.metrics.entity", "Target entity"),
              value: ENTITY_TYPE
            }
          )
        ] }),
        /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1fr)_280px]", children: [
          /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsxs(Table, { children: [
            /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
              /* @__PURE__ */ jsx(TableHead, { children: t("sync_excel.widget.mapping.sourceColumn", "Source column") }),
              /* @__PURE__ */ jsx(TableHead, { children: t("sync_excel.widget.mapping.sampleValue", "Sample value") }),
              /* @__PURE__ */ jsx(TableHead, { children: t("sync_excel.widget.mapping.targetField", "Target field") })
            ] }) }),
            /* @__PURE__ */ jsx(TableBody, { children: mappingRows.map((row) => /* @__PURE__ */ jsxs(TableRow, { children: [
              /* @__PURE__ */ jsx(TableCell, { className: "font-medium", children: row.sourceColumn }),
              /* @__PURE__ */ jsx(TableCell, { className: "max-w-[260px] truncate text-muted-foreground", children: firstSampleRow?.[row.sourceColumn] ?? "\u2014" }),
              /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs(
                "select",
                {
                  className: SELECT_CLASS_NAME,
                  value: row.targetField,
                  onChange: (event) => handleTargetFieldChange(row.sourceColumn, event.target.value),
                  disabled: isStartingImport || isRunActive,
                  children: [
                    /* @__PURE__ */ jsx("option", { value: "", children: t("sync_excel.mapping.targets.ignore", "Leave unmapped") }),
                    targetOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.labelKey ? t(option.labelKey, option.fallback) : option.fallback }, option.value))
                  ]
                }
              ) })
            ] }, row.sourceColumn)) })
          ] }) }),
          /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
            /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
              /* @__PURE__ */ jsx(Label, { htmlFor: "sync-excel-match-strategy", children: t("sync_excel.widget.matchStrategy.label", "How to match existing people") }),
              /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t(
                "sync_excel.widget.matchStrategy.help",
                "This decides whether imported rows update existing people or create new ones."
              ) }),
              /* @__PURE__ */ jsxs(
                "select",
                {
                  id: "sync-excel-match-strategy",
                  className: SELECT_CLASS_NAME,
                  value: matchStrategy,
                  onChange: (event) => {
                    setIsMappingDirty(true);
                    setMatchStrategy(event.target.value);
                  },
                  disabled: isStartingImport || isRunActive,
                  children: [
                    /* @__PURE__ */ jsx("option", { value: "externalId", children: t("sync_excel.widget.matchStrategy.externalId", "Source record ID (Recommended)") }),
                    /* @__PURE__ */ jsx("option", { value: "email", children: t("sync_excel.widget.matchStrategy.email", "Email address") }),
                    /* @__PURE__ */ jsx("option", { value: "custom", children: t("sync_excel.widget.matchStrategy.custom", "Custom matching") })
                  ]
                }
              ),
              /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: matchStrategy === "externalId" ? t(
                "sync_excel.widget.matchStrategy.externalIdDescription",
                "Best for repeated imports from the same source. Future imports update the same people instead of creating duplicates."
              ) : matchStrategy === "email" ? t(
                "sync_excel.widget.matchStrategy.emailDescription",
                "Matches people by email. Works best when each person has one reliable, unique email address."
              ) : t(
                "sync_excel.widget.matchStrategy.customDescription",
                "Advanced option. Use only if you know how existing records should be identified in this import."
              ) })
            ] }),
            /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
              /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", onClick: handleResetToSuggested, disabled: isStartingImport || isRunActive, children: [
                /* @__PURE__ */ jsx(RefreshCw, { className: "mr-2 size-4" }),
                t("sync_excel.widget.actions.reset", "Reset to suggestion")
              ] }),
              /* @__PURE__ */ jsxs(Button, { type: "button", onClick: () => void handleStartImport(), disabled: !canStartImport || isStartingImport || isRunActive, children: [
                isStartingImport ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 size-4" }) : /* @__PURE__ */ jsx(Play, { className: "mr-2 size-4" }),
                isStartingImport ? t("sync_excel.widget.actions.startPending", "Starting...") : t("sync_excel.widget.actions.start", "Start import")
              ] })
            ] }),
            matchStrategy === "custom" ? /* @__PURE__ */ jsx(
              StatusAlert,
              {
                variant: "warning",
                title: t("sync_excel.widget.matchStrategy.customWarningTitle", "Use custom matching with care"),
                message: t(
                  "sync_excel.widget.matchStrategy.customWarningMessage",
                  "Custom matching may create duplicates if the mapped fields do not uniquely identify a person."
                )
              }
            ) : null,
            hasExistingRunForCurrentUpload ? /* @__PURE__ */ jsx(
              StatusAlert,
              {
                variant: "warning",
                title: t("sync_excel.widget.validation.reimportTitle", "This upload already has a run"),
                message: t(
                  "sync_excel.widget.validation.reimportMessage",
                  "Starting another import from the same uploaded CSV may create duplicates unless your matching strategy reliably identifies existing people."
                )
              }
            ) : null,
            !hasSafeDedupeStrategy ? /* @__PURE__ */ jsx(
              StatusAlert,
              {
                variant: "warning",
                title: t("sync_excel.widget.validation.duplicateRiskTitle", "Duplicate risk"),
                message: t(
                  "sync_excel.widget.validation.duplicateRiskMessage",
                  "This mapping does not use the recommended External ID or Email matching strategy. Re-importing the same dataset may create duplicate people."
                )
              }
            ) : null,
            diagnostics.duplicateTargets.length > 0 ? /* @__PURE__ */ jsx(
              StatusAlert,
              {
                variant: "error",
                title: t("sync_excel.widget.validation.duplicateTargetsTitle", "Duplicate target fields"),
                message: t(
                  "sync_excel.widget.validation.duplicateTargetsMessage",
                  "Each target field can only be mapped once in this foundation slice: {targets}",
                  { targets: diagnostics.duplicateTargets.map((target) => formatTargetLabel(target, t, targetOptions)).join(", ") }
                )
              }
            ) : null,
            !diagnostics.hasIdentityTarget ? /* @__PURE__ */ jsx(
              StatusAlert,
              {
                variant: "warning",
                title: t("sync_excel.widget.validation.identityTitle", "Map an identity field"),
                message: t("sync_excel.widget.validation.identityMessage", "Map either External ID or Primary email so reimports can match existing people.")
              }
            ) : null,
            !diagnostics.hasNameTarget ? /* @__PURE__ */ jsx(
              StatusAlert,
              {
                variant: "warning",
                title: t("sync_excel.widget.validation.nameTitle", "Map a name field"),
                message: t("sync_excel.widget.validation.nameMessage", "Map Last name or Display name so the importer can build person records.")
              }
            ) : null,
            diagnostics.hasIdentityTarget && diagnostics.hasNameTarget && diagnostics.duplicateTargets.length === 0 ? /* @__PURE__ */ jsx(
              StatusAlert,
              {
                variant: "info",
                title: t("sync_excel.widget.validation.readyTitle", "Ready to import"),
                message: t("sync_excel.widget.validation.readyMessage", "The current mapping is valid for the first customers.person slice.")
              }
            ) : null
          ] })
        ] })
      ] })
    ] }) : null,
    runId && !shouldHideInteractiveContent ? /* @__PURE__ */ jsxs(Card, { children: [
      /* @__PURE__ */ jsx(CardHeader, { 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(CardTitle, { children: t("sync_excel.widget.run.title", "Import run status") }),
          /* @__PURE__ */ jsx(CardDescription, { children: t("sync_excel.widget.run.description", "The import runs in the background through Data Sync and updates this view as progress changes.") })
        ] }),
        /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
          /* @__PURE__ */ jsx(RunStatusBadge, { status: importStatus, t }),
          progressJobId ? /* @__PURE__ */ jsx(Badge, { variant: "outline", children: progressJobId }) : null
        ] })
      ] }) }),
      /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
        /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
          /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
            /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("sync_excel.widget.run.progressLabel", "Progress") }),
            /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
              progressValue,
              "%"
            ] })
          ] }),
          /* @__PURE__ */ jsx(Progress, { value: progressValue, className: "h-2.5" })
        ] }),
        /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2 xl:grid-cols-5", children: [
          /* @__PURE__ */ jsx(MetricCard, { label: t("sync_excel.widget.run.metrics.created", "Created"), value: String(runDetail?.createdCount ?? 0) }),
          /* @__PURE__ */ jsx(MetricCard, { label: t("sync_excel.widget.run.metrics.updated", "Updated"), value: String(runDetail?.updatedCount ?? 0) }),
          /* @__PURE__ */ jsx(MetricCard, { label: t("sync_excel.widget.run.metrics.skipped", "Skipped"), value: String(runDetail?.skippedCount ?? 0) }),
          /* @__PURE__ */ jsx(MetricCard, { label: t("sync_excel.widget.run.metrics.failed", "Failed"), value: String(runDetail?.failedCount ?? 0) }),
          /* @__PURE__ */ jsx(MetricCard, { label: t("sync_excel.widget.run.metrics.processed", "Processed"), value: String(runDetail?.progressJob?.processedCount ?? 0) })
        ] }),
        runDetail?.lastError ? /* @__PURE__ */ jsx(
          StatusAlert,
          {
            variant: "error",
            title: t("sync_excel.widget.run.errorTitle", "Run error"),
            message: runDetail.lastError
          }
        ) : null,
        /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
          /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", onClick: () => void handleRefreshAll(), disabled: isRefreshingPreview || isRestoringSession, children: [
            /* @__PURE__ */ jsx(RefreshCw, { className: "mr-2 size-4" }),
            t("sync_excel.widget.actions.refreshRun", "Refresh run status")
          ] }),
          isRunActive ? /* @__PURE__ */ jsxs(Button, { type: "button", variant: "destructive", onClick: () => void handleCancelRun(), disabled: isCancelling, children: [
            isCancelling ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 size-4" }) : /* @__PURE__ */ jsx(XCircle, { className: "mr-2 size-4" }),
            isCancelling ? t("sync_excel.widget.actions.cancelPending", "Cancelling...") : t("sync_excel.widget.actions.cancel", "Cancel run")
          ] }) : null
        ] })
      ] })
    ] }) : null
  ] });
}
function StatusAlert({
  variant,
  title,
  message
}) {
  return /* @__PURE__ */ jsxs(Alert, { variant: variant === "error" ? "destructive" : variant, children: [
    /* @__PURE__ */ jsx(AlertTitle, { children: title }),
    /* @__PURE__ */ jsx(AlertDescription, { children: message })
  ] });
}
function MetricCard({ label, value }) {
  return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-muted/30 p-4", children: [
    /* @__PURE__ */ jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: label }),
    /* @__PURE__ */ jsx("div", { className: "mt-2 text-lg font-semibold text-foreground", children: value })
  ] });
}
const RUN_STATUS_BADGE_VARIANTS = {
  completed: "success",
  failed: "error",
  cancelled: "neutral",
  pending: "info",
  running: "info",
  idle: "neutral"
};
function RunStatusBadge({
  status,
  t
}) {
  const variant = RUN_STATUS_BADGE_VARIANTS[status] ?? "neutral";
  if (status === "completed") {
    return /* @__PURE__ */ jsxs(StatusBadge, { variant, className: "gap-1.5", children: [
      /* @__PURE__ */ jsx(CheckCircle2, { className: "size-3.5" }),
      t("sync_excel.widget.run.status.completed", "Completed")
    ] });
  }
  if (status === "failed") {
    return /* @__PURE__ */ jsxs(StatusBadge, { variant, className: "gap-1.5", children: [
      /* @__PURE__ */ jsx(AlertTriangle, { className: "size-3.5" }),
      t("sync_excel.widget.run.status.failed", "Failed")
    ] });
  }
  if (status === "cancelled") {
    return /* @__PURE__ */ jsxs(StatusBadge, { variant, className: "gap-1.5", children: [
      /* @__PURE__ */ jsx(XCircle, { className: "size-3.5" }),
      t("sync_excel.widget.run.status.cancelled", "Cancelled")
    ] });
  }
  if (status === "pending" || status === "running") {
    return /* @__PURE__ */ jsxs(StatusBadge, { variant, className: "gap-1.5", children: [
      /* @__PURE__ */ jsx(RefreshCw, { className: cn("size-3.5", status === "running" ? "animate-spin" : "") }),
      status === "pending" ? t("sync_excel.widget.run.status.pending", "Pending") : t("sync_excel.widget.run.status.running", "Running")
    ] });
  }
  return /* @__PURE__ */ jsx(StatusBadge, { variant, children: t("sync_excel.widget.run.status.idle", "Idle") });
}
export {
  RUN_STATUS_BADGE_VARIANTS,
  RunStatusBadge,
  SyncExcelUploadConfigWidget as default
};
//# sourceMappingURL=widget.client.js.map
