import { useEffect, useRef, useState, type ReactNode } from "react";
import { Search, Plus, ChevronRight, ChevronDown, Check, Building, Calendar, DollarSign, Activity, X, Clock, FileText, User, Tag, AlertTriangle, ListFilter } from "lucide-react";
import { motion, AnimatePresence } from "motion/react";
import { createOpportunity, getExpansionOverview, getOpportunityMeta, getOpportunityOverview, pushOpportunityToOms, updateOpportunity, type ChannelExpansionItem, type CreateOpportunityPayload, type OpportunityDictOption, type OpportunityFollowUp, type OpportunityItem, type SalesExpansionItem } from "@/lib/auth";
import { AdaptiveSelect } from "@/components/AdaptiveSelect";
import { useIsWecomBrowser } from "@/hooks/useIsWecomBrowser";
import { cn } from "@/lib/utils";

const detailBadgeClass = "crm-btn-chip text-[11px] font-semibold";

const CONFIDENCE_OPTIONS = [
  { value: "80", label: "A" },
  { value: "60", label: "B" },
  { value: "40", label: "C" },
] as const;

const COMPETITOR_OPTIONS = [
  "深信服",
  "锐捷",
  "华为",
  "中兴",
  "噢易云",
  "无",
  "其他",
] as const;

type CompetitorOption = (typeof COMPETITOR_OPTIONS)[number] | "";
type OperatorMode = "none" | "h3c" | "channel" | "both";
type OpportunityArchiveTab = "active" | "archived";
type OpportunityField =
  | "projectLocation"
  | "opportunityName"
  | "customerName"
  | "operatorName"
  | "salesExpansionId"
  | "channelExpansionId"
  | "amount"
  | "expectedCloseDate"
  | "confidencePct"
  | "stage"
  | "competitorName"
  | "opportunityType";

const defaultForm: CreateOpportunityPayload = {
  opportunityName: "",
  customerName: "",
  projectLocation: "",
  operatorName: "",
  amount: 0,
  expectedCloseDate: "",
  confidencePct: 40,
  stage: "",
  opportunityType: "新建",
  productType: "VDI云桌面",
  source: "主动开发",
  salesExpansionId: undefined,
  channelExpansionId: undefined,
  competitorName: "",
  description: "",
};

function formatAmount(value?: number) {
  if (value === undefined || value === null || Number.isNaN(Number(value))) {
    return "0";
  }
  return new Intl.NumberFormat("zh-CN").format(Number(value));
}

function toFormFromItem(item: OpportunityItem): CreateOpportunityPayload {
  return {
    opportunityName: item.name || "",
    customerName: item.client || "",
    projectLocation: item.projectLocation || "",
    amount: item.amount || 0,
    expectedCloseDate: item.date || "",
    confidencePct: item.confidence ?? 50,
    stage: item.stageCode || item.stage || "",
    opportunityType: item.type || "新建",
    productType: item.product || "VDI云桌面",
    source: item.source || "主动开发",
    salesExpansionId: item.salesExpansionId,
    channelExpansionId: item.channelExpansionId,
    operatorName: item.operatorCode || item.operatorName || "",
    competitorName: item.competitorName || "",
    description: item.notes || "",
  };
}

function getConfidenceOptionValue(score?: number) {
  const value = score ?? 0;
  if (value >= 80) return "80";
  if (value >= 60) return "60";
  return "40";
}

function getConfidenceLabel(score?: number) {
  const matchedOption = CONFIDENCE_OPTIONS.find((item) => item.value === getConfidenceOptionValue(score));
  return matchedOption?.label || "C";
}

function getConfidenceBadgeClass(score?: number) {
  const normalizedScore = Number(getConfidenceOptionValue(score));
  if (normalizedScore >= 80) return "crm-pill crm-pill-emerald";
  if (normalizedScore >= 60) return "crm-pill crm-pill-amber";
  return "crm-pill crm-pill-rose";
}

function getCompetitorSelection(value?: string): CompetitorOption {
  const competitor = value?.trim() || "";
  if (!competitor) {
    return "";
  }
  return (COMPETITOR_OPTIONS as readonly string[]).includes(competitor) ? (competitor as CompetitorOption) : "其他";
}

function normalizeOperatorToken(value?: string) {
  return (value || "")
    .trim()
    .toLowerCase()
    .replace(/\s+/g, "")
    .replace(/＋/g, "+");
}

function resolveOperatorMode(operatorValue: string | undefined, operatorOptions: OpportunityDictOption[]): OperatorMode {
  const selectedOption = operatorOptions.find((item) => (item.value || "") === (operatorValue || ""));
  const candidates = [selectedOption?.label, selectedOption?.value, operatorValue]
    .filter(Boolean)
    .map((item) => normalizeOperatorToken(item));

  if (!candidates.length || candidates.every((item) => !item)) {
    return "none";
  }

  const hasH3c = candidates.some((item) => item.includes("新华三") || item.includes("h3c"));
  const hasChannel = candidates.some((item) => item.includes("渠道") || item.includes("channel"));

  if (hasH3c && hasChannel) {
    return "both";
  }
  if (hasH3c) {
    return "h3c";
  }
  if (hasChannel) {
    return "channel";
  }
  return "none";
}

function buildOpportunitySubmitPayload(
  form: CreateOpportunityPayload,
  competitorSelection: CompetitorOption,
  operatorMode: OperatorMode,
): CreateOpportunityPayload {
  const normalizedCompetitorName = competitorSelection === "其他"
    ? form.competitorName?.trim()
    : competitorSelection || undefined;

  return {
    ...form,
    salesExpansionId: operatorMode === "h3c" || operatorMode === "both" ? form.salesExpansionId : undefined,
    channelExpansionId: operatorMode === "channel" || operatorMode === "both" ? form.channelExpansionId : undefined,
    competitorName: normalizedCompetitorName || undefined,
  };
}

function validateOperatorRelations(
  operatorMode: OperatorMode,
  salesExpansionId: number | undefined,
  channelExpansionId: number | undefined,
) {
  if (operatorMode === "h3c" && !salesExpansionId) {
    throw new Error("运作方选择“新华三”时，新华三负责人必须填写");
  }
  if (operatorMode === "channel" && !channelExpansionId) {
    throw new Error("运作方选择“渠道”时，渠道名称必须填写");
  }
  if (operatorMode === "both" && !salesExpansionId && !channelExpansionId) {
    throw new Error("运作方选择“新华三+渠道”时，新华三负责人和渠道名称都必须填写");
  }
  if (operatorMode === "both" && !salesExpansionId) {
    throw new Error("运作方选择“新华三+渠道”时，新华三负责人必须填写");
  }
  if (operatorMode === "both" && !channelExpansionId) {
    throw new Error("运作方选择“新华三+渠道”时，渠道名称必须填写");
  }
}

function getFieldInputClass(hasError: boolean) {
  return cn(
    "crm-input-box crm-input-text w-full border bg-white outline-none focus:border-violet-500 focus:ring-1 focus:ring-violet-500 dark:bg-slate-900/50",
    hasError
      ? "border-rose-400 bg-rose-50/60 focus:border-rose-500 focus:ring-rose-500 dark:border-rose-500/70 dark:bg-rose-500/10"
      : "border-slate-200 dark:border-slate-800",
  );
}

function RequiredMark() {
  return <span className="ml-1 text-rose-500">*</span>;
}

function validateOpportunityForm(
  form: CreateOpportunityPayload,
  competitorSelection: CompetitorOption,
  operatorMode: OperatorMode,
) {
  const errors: Partial<Record<OpportunityField, string>> = {};

  if (!form.projectLocation?.trim()) {
    errors.projectLocation = "请填写项目地";
  }
  if (!form.opportunityName?.trim()) {
    errors.opportunityName = "请填写项目名称";
  }
  if (!form.customerName?.trim()) {
    errors.customerName = "请填写最终客户";
  }
  if (!form.operatorName?.trim()) {
    errors.operatorName = "请选择运作方";
  }
  if (!form.amount || form.amount <= 0) {
    errors.amount = "请填写预计金额";
  }
  if (!form.expectedCloseDate?.trim()) {
    errors.expectedCloseDate = "请选择预计下单时间";
  }
  if (!form.confidencePct || form.confidencePct <= 0) {
    errors.confidencePct = "请选择项目把握度";
  }
  if (!form.stage?.trim()) {
    errors.stage = "请选择项目阶段";
  }
  if (!form.opportunityType?.trim()) {
    errors.opportunityType = "请选择建设类型";
  }
  if (!competitorSelection) {
    errors.competitorName = "请选择竞争对手";
  } else if (competitorSelection === "其他" && !form.competitorName?.trim()) {
    errors.competitorName = "请选择“其他”时，请填写具体竞争对手";
  }

  if (operatorMode === "h3c" && !form.salesExpansionId) {
    errors.salesExpansionId = "请选择新华三负责人";
  }
  if (operatorMode === "channel" && !form.channelExpansionId) {
    errors.channelExpansionId = "请选择渠道名称";
  }
  if (operatorMode === "both" && !form.salesExpansionId) {
    errors.salesExpansionId = "请选择新华三负责人";
  }
  if (operatorMode === "both" && !form.channelExpansionId) {
    errors.channelExpansionId = "请选择渠道名称";
  }

  return errors;
}

function ModalShell({
  title,
  subtitle,
  onClose,
  children,
  footer,
}: {
  title: string;
  subtitle: string;
  onClose: () => void;
  children: ReactNode;
  footer: ReactNode;
}) {
  const isMobileViewport = useIsMobileViewport();
  const isWecomBrowser = useIsWecomBrowser();
  const disableMobileMotion = isMobileViewport || isWecomBrowser;

  return (
    <>
      <motion.div
        initial={disableMobileMotion ? false : { opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={disableMobileMotion ? undefined : { opacity: 0 }}
        onClick={onClose}
        className={cn("fixed inset-0 z-[70] bg-slate-900/35 dark:bg-slate-950/70", !disableMobileMotion && "backdrop-blur-sm")}
      />
      <motion.div
        initial={disableMobileMotion ? false : { opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        exit={disableMobileMotion ? undefined : { opacity: 0, y: 20 }}
        transition={disableMobileMotion ? { duration: 0 } : undefined}
        className="fixed inset-0 z-[80] px-0 pb-0 pt-[env(safe-area-inset-top)] sm:p-6"
      >
        <div className="mx-auto flex h-[calc(100dvh-env(safe-area-inset-top))] w-full items-end sm:h-full sm:max-w-3xl sm:items-center">
          <div className="flex h-[92dvh] w-full flex-col overflow-hidden rounded-t-3xl border border-slate-200 bg-white shadow-2xl dark:border-slate-800 dark:bg-slate-900 sm:h-full sm:rounded-3xl">
            <div className="flex items-center justify-between border-b border-slate-100 px-5 py-4 dark:border-slate-800 sm:px-6">
              <div>
                <h2 className="text-base font-semibold text-slate-900 dark:text-white sm:text-lg">{title}</h2>
                <p className="mt-1 text-xs leading-5 text-slate-500 dark:text-slate-400">{subtitle}</p>
              </div>
              <button onClick={onClose} className="rounded-full p-2 text-slate-400 transition-colors hover:bg-slate-100 dark:hover:bg-slate-800">
                <X className="h-5 w-5" />
              </button>
            </div>
            <div className="flex-1 overflow-y-auto px-5 py-5 sm:px-6">{children}</div>
            <div className="border-t border-slate-100 px-5 pb-[calc(1rem+env(safe-area-inset-bottom))] pt-4 dark:border-slate-800 sm:px-6 sm:pb-4">{footer}</div>
          </div>
        </div>
      </motion.div>
    </>
  );
}

function DetailItem({
  label,
  value,
  icon,
  className = "",
}: {
  label: string;
  value: ReactNode;
  icon?: ReactNode;
  className?: string;
}) {
  return (
    <div className={`crm-detail-item ${className}`.trim()}>
      <p className="crm-detail-label">
        {icon}
        {label}
      </p>
      <div className="crm-detail-value">{value}</div>
    </div>
  );
}

type SearchableOption = {
  value: number | string;
  label: string;
  keywords?: string[];
};

function useIsMobileViewport() {
  const [isMobile, setIsMobile] = useState(() => {
    if (typeof window === "undefined") {
      return false;
    }
    return window.matchMedia("(max-width: 639px)").matches;
  });

  useEffect(() => {
    if (typeof window === "undefined") {
      return;
    }

    const mediaQuery = window.matchMedia("(max-width: 639px)");
    const handleChange = () => setIsMobile(mediaQuery.matches);
    handleChange();

    if (typeof mediaQuery.addEventListener === "function") {
      mediaQuery.addEventListener("change", handleChange);
      return () => mediaQuery.removeEventListener("change", handleChange);
    }

    mediaQuery.addListener(handleChange);
    return () => mediaQuery.removeListener(handleChange);
  }, []);

  return isMobile;
}

function SearchableSelect({
  value,
  options,
  placeholder,
  searchPlaceholder,
  emptyText,
  className,
  onChange,
}: {
  value?: number;
  options: SearchableOption[];
  placeholder: string;
  searchPlaceholder: string;
  emptyText: string;
  className?: string;
  onChange: (value?: number) => void;
}) {
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState("");
  const containerRef = useRef<HTMLDivElement | null>(null);
  const isMobile = useIsMobileViewport();
  const selectedOption = options.find((item) => item.value === value);
  const normalizedQuery = query.trim().toLowerCase();
  const filteredOptions = options.filter((item) => {
    if (!normalizedQuery) {
      return true;
    }

    const haystacks = [item.label, ...(item.keywords ?? [])]
      .filter(Boolean)
      .map((entry) => entry.toLowerCase());

    return haystacks.some((entry) => entry.includes(normalizedQuery));
  });

  useEffect(() => {
    if (!open || isMobile) {
      return;
    }

    const handlePointerDown = (event: MouseEvent) => {
      if (!containerRef.current?.contains(event.target as Node)) {
        setOpen(false);
        setQuery("");
      }
    };

    document.addEventListener("mousedown", handlePointerDown);
    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
    };
  }, [isMobile, open]);

  useEffect(() => {
    if (!open || !isMobile) {
      return;
    }

    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      document.body.style.overflow = previousOverflow;
    };
  }, [isMobile, open]);

  const renderSearchBody = () => (
    <>
      <div className="relative">
        <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" />
        <input
          autoFocus
          value={query}
          onChange={(event) => setQuery(event.target.value)}
          placeholder={searchPlaceholder}
          className="crm-input-text w-full rounded-xl border border-slate-200 bg-slate-50 py-2.5 pl-10 pr-3 text-slate-900 outline-none focus:border-violet-500 focus:ring-1 focus:ring-violet-500 dark:border-slate-800 dark:bg-slate-800/60 dark:text-white"
        />
      </div>

      <div className="mt-3 max-h-64 space-y-1 overflow-y-auto">
        <button
          type="button"
          onClick={() => {
            onChange(undefined);
            setOpen(false);
            setQuery("");
          }}
          className="w-full rounded-xl px-3 py-2 text-left text-sm text-slate-500 transition-colors hover:bg-slate-50 dark:text-slate-400 dark:hover:bg-slate-800"
        >
          不选择
        </button>

        {filteredOptions.length > 0 ? (
          filteredOptions.map((item) => (
            <button
              type="button"
              key={item.value}
              onClick={() => {
                onChange(Number(item.value));
                setOpen(false);
                setQuery("");
              }}
              className={`flex w-full items-center justify-between rounded-xl px-3 py-2 text-left text-sm transition-colors ${
                item.value === value
                  ? "bg-violet-50 text-violet-700 dark:bg-violet-500/10 dark:text-violet-300"
                  : "text-slate-700 hover:bg-slate-50 dark:text-slate-200 dark:hover:bg-slate-800"
              }`}
            >
              <span>{item.label}</span>
              {item.value === value ? <Check className="h-4 w-4 shrink-0" /> : null}
            </button>
          ))
        ) : (
          <div className="crm-empty-state px-3 py-6">{emptyText}</div>
        )}
      </div>
    </>
  );

  return (
    <div ref={containerRef} className="relative">
      <button
        type="button"
        onClick={() => {
          setOpen((current) => {
            const next = !current;
            if (!next) {
              setQuery("");
            }
            return next;
          });
        }}
        className={cn(
          "crm-btn-sm crm-input-text flex w-full items-center justify-between rounded-xl border border-slate-200 bg-white text-left outline-none transition-colors hover:border-slate-300 focus:border-violet-500 focus:ring-1 focus:ring-violet-500 dark:border-slate-800 dark:bg-slate-900/50 dark:hover:border-slate-700",
          className,
        )}
      >
        <span className={selectedOption ? "text-slate-900 dark:text-white" : "text-slate-400 dark:text-slate-500"}>
          {selectedOption?.label || placeholder}
        </span>
        <ChevronDown className={`h-4 w-4 shrink-0 text-slate-400 transition-transform ${open ? "rotate-180" : ""}`} />
      </button>

      <AnimatePresence>
        {open && !isMobile ? (
          <motion.div
            initial={{ opacity: 0, y: 8 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: 8 }}
            className="absolute z-20 mt-2 w-full rounded-2xl border border-slate-200 bg-white p-3 shadow-xl dark:border-slate-800 dark:bg-slate-900"
          >
            {renderSearchBody()}
          </motion.div>
        ) : null}
      </AnimatePresence>

      <AnimatePresence>
        {open && isMobile ? (
          <>
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              className="fixed inset-0 z-[120] bg-slate-900/35 backdrop-blur-sm dark:bg-slate-950/70"
              onClick={() => {
                setOpen(false);
                setQuery("");
              }}
            />
            <motion.div
              initial={{ opacity: 0, y: 24 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: 24 }}
              className="fixed inset-x-0 bottom-0 z-[130] px-3 pb-[calc(0.75rem+env(safe-area-inset-bottom))] pt-3"
            >
              <div className="mx-auto w-full max-w-lg rounded-3xl border border-slate-200 bg-white shadow-2xl dark:border-slate-800 dark:bg-slate-900">
                <div className="flex items-center justify-between border-b border-slate-100 px-5 py-4 dark:border-slate-800">
                  <div>
                    <p className="text-base font-semibold text-slate-900 dark:text-white">{placeholder}</p>
                    <p className="crm-field-note mt-1">搜索并选择一个对象</p>
                  </div>
                  <button
                    type="button"
                    onClick={() => {
                      setOpen(false);
                      setQuery("");
                    }}
                    className="rounded-full p-2 text-slate-400 transition-colors hover:bg-slate-100 dark:hover:bg-slate-800"
                  >
                    <X className="h-5 w-5" />
                  </button>
                </div>
                <div className="px-4 py-4 pb-[calc(1rem+env(safe-area-inset-bottom))]">
                  {renderSearchBody()}
                </div>
              </div>
            </motion.div>
          </>
        ) : null}
      </AnimatePresence>
    </div>
  );
}

export default function Opportunities() {
  const isMobileViewport = useIsMobileViewport();
  const isWecomBrowser = useIsWecomBrowser();
  const disableMobileMotion = isMobileViewport || isWecomBrowser;
  const [archiveTab, setArchiveTab] = useState<OpportunityArchiveTab>("active");
  const [filter, setFilter] = useState("全部");
  const [stageFilterOpen, setStageFilterOpen] = useState(false);
  const [keyword, setKeyword] = useState("");
  const [selectedItem, setSelectedItem] = useState<OpportunityItem | null>(null);
  const [createOpen, setCreateOpen] = useState(false);
  const [editOpen, setEditOpen] = useState(false);
  const [pushConfirmOpen, setPushConfirmOpen] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [pushingOms, setPushingOms] = useState(false);
  const [error, setError] = useState("");
  const [items, setItems] = useState<OpportunityItem[]>([]);
  const [salesExpansionOptions, setSalesExpansionOptions] = useState<SalesExpansionItem[]>([]);
  const [channelExpansionOptions, setChannelExpansionOptions] = useState<ChannelExpansionItem[]>([]);
  const [stageOptions, setStageOptions] = useState<OpportunityDictOption[]>([]);
  const [operatorOptions, setOperatorOptions] = useState<OpportunityDictOption[]>([]);
  const [form, setForm] = useState<CreateOpportunityPayload>(defaultForm);
  const [competitorSelection, setCompetitorSelection] = useState<CompetitorOption>("");
  const [fieldErrors, setFieldErrors] = useState<Partial<Record<OpportunityField, string>>>({});
  const [detailTab, setDetailTab] = useState<"sales" | "channel" | "followups">("sales");
  const hasForegroundModal = createOpen || editOpen || pushConfirmOpen;

  useEffect(() => {
    let cancelled = false;

    async function load() {
      try {
        const data = await getOpportunityOverview(keyword, filter);
        if (!cancelled) {
          setItems(data.items ?? []);
          setSelectedItem(null);
        }
      } catch {
        if (!cancelled) {
          setItems([]);
          setSelectedItem(null);
        }
      }
    }

    void load();
    return () => {
      cancelled = true;
    };
  }, [keyword, filter]);

  useEffect(() => {
    let cancelled = false;

    async function loadSalesExpansionOptions() {
      try {
        const data = await getExpansionOverview("");
        if (!cancelled) {
          setSalesExpansionOptions(data.salesItems ?? []);
          setChannelExpansionOptions(data.channelItems ?? []);
        }
      } catch {
        if (!cancelled) {
          setSalesExpansionOptions([]);
          setChannelExpansionOptions([]);
        }
      }
    }

    void loadSalesExpansionOptions();
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    let cancelled = false;

    async function loadMeta() {
      try {
        const data = await getOpportunityMeta();
        if (!cancelled) {
          setStageOptions((data.stageOptions ?? []).filter((item) => item.value));
          setOperatorOptions((data.operatorOptions ?? []).filter((item) => item.value));
        }
      } catch {
        if (!cancelled) {
          setStageOptions([]);
          setOperatorOptions([]);
        }
      }
    }

    void loadMeta();
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    if (!stageOptions.length) {
      return;
    }

    const defaultStage = stageOptions[0]?.value || "";
    setForm((current) => (current.stage ? current : { ...current, stage: defaultStage }));
  }, [stageOptions]);

  const followUpRecords: OpportunityFollowUp[] = selectedItem?.followUps ?? [];
  const visibleItems = items.filter((item) => (archiveTab === "active" ? !item.archived : Boolean(item.archived)));
  const stageFilterOptions = [
    { label: "全部", value: "全部" },
    ...stageOptions.map((item) => ({ label: item.label || item.value || "", value: item.value || "" })),
  ].filter((item) => item.value);
  const activeStageFilterLabel = stageFilterOptions.find((item) => item.value === filter)?.label || "全部";
  const selectedSalesExpansion = selectedItem?.salesExpansionId
    ? salesExpansionOptions.find((item) => item.id === selectedItem.salesExpansionId) ?? null
    : null;
  const selectedChannelExpansion = selectedItem?.channelExpansionId
    ? channelExpansionOptions.find((item) => item.id === selectedItem.channelExpansionId) ?? null
    : null;
  const selectedSalesExpansionName = selectedItem?.salesExpansionName || selectedSalesExpansion?.name || "";
  const selectedChannelExpansionName = selectedItem?.channelExpansionName || selectedChannelExpansion?.name || "";
  const operatorMode = resolveOperatorMode(form.operatorName, operatorOptions);
  const showSalesExpansionField = operatorMode === "h3c" || operatorMode === "both";
  const showChannelExpansionField = operatorMode === "channel" || operatorMode === "both";
  const salesExpansionSearchOptions: SearchableOption[] = salesExpansionOptions.map((item) => ({
    value: item.id,
    label: item.name || `拓展人员#${item.id}`,
    keywords: [item.employeeNo || "", item.officeName || "", item.phone || "", item.title || ""],
  }));
  const channelExpansionSearchOptions: SearchableOption[] = channelExpansionOptions.map((item) => ({
    value: item.id,
    label: item.name || `渠道#${item.id}`,
    keywords: [item.channelCode || "", item.province || "", item.primaryContactName || "", item.primaryContactMobile || ""],
  }));

  useEffect(() => {
    if (selectedItem) {
      setDetailTab("sales");
    } else {
      setPushConfirmOpen(false);
    }
  }, [selectedItem]);

  useEffect(() => {
    if (selectedItem && !visibleItems.some((item) => item.id === selectedItem.id)) {
      setSelectedItem(null);
    }
  }, [archiveTab, selectedItem, visibleItems]);

  const getConfidenceColor = (score: number) => {
    const normalizedScore = Number(getConfidenceOptionValue(score));
    if (normalizedScore >= 80) return "text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10 border-emerald-200 dark:border-emerald-500/20";
    if (normalizedScore >= 60) return "text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-500/10 border-amber-200 dark:border-amber-500/20";
    return "text-rose-600 dark:text-rose-400 bg-rose-50 dark:bg-rose-500/10 border-rose-200 dark:border-rose-500/20";
  };

  const handleChange = <K extends keyof CreateOpportunityPayload>(key: K, value: CreateOpportunityPayload[K]) => {
    setForm((current) => ({ ...current, [key]: value }));
    if (key in fieldErrors) {
      setFieldErrors((current) => {
        const next = { ...current };
        delete next[key as OpportunityField];
        return next;
      });
    }
  };

  const handleOpenCreate = () => {
    setError("");
    setFieldErrors({});
    setForm(defaultForm);
    setCompetitorSelection("");
    setCreateOpen(true);
  };

  const resetCreateState = () => {
    setCreateOpen(false);
    setEditOpen(false);
    setSubmitting(false);
    setError("");
    setFieldErrors({});
    setForm(defaultForm);
    setCompetitorSelection("");
  };

  const reload = async (preferredSelectedId?: number) => {
    const data = await getOpportunityOverview(keyword, filter);
    const nextItems = data.items ?? [];
    setItems(nextItems);
    if (preferredSelectedId) {
      setSelectedItem(nextItems.find((item) => item.id === preferredSelectedId) ?? null);
    }
  };

  const handleCreateSubmit = async () => {
    if (submitting) {
      return;
    }

    setError("");
    const validationErrors = validateOpportunityForm(form, competitorSelection, operatorMode);
    if (Object.keys(validationErrors).length > 0) {
      setFieldErrors(validationErrors);
      setError("请先完整填写商机必填字段");
      return;
    }

    setSubmitting(true);

    try {
      await createOpportunity(buildOpportunitySubmitPayload(form, competitorSelection, operatorMode));
      await reload();
      resetCreateState();
    } catch (createError) {
      setError(createError instanceof Error ? createError.message : "新增商机失败");
      setSubmitting(false);
    }
  };

  const handleOpenEdit = () => {
    if (!selectedItem) {
      return;
    }
    if (selectedItem.pushedToOms) {
      setError("该商机已推送 OMS，不能再编辑");
      return;
    }
    setError("");
    setFieldErrors({});
    setForm(toFormFromItem(selectedItem));
    setCompetitorSelection(getCompetitorSelection(selectedItem.competitorName));
    setEditOpen(true);
  };

  const handleEditSubmit = async () => {
    if (!selectedItem || submitting) {
      return;
    }

    setError("");
    const validationErrors = validateOpportunityForm(form, competitorSelection, operatorMode);
    if (Object.keys(validationErrors).length > 0) {
      setFieldErrors(validationErrors);
      setError("请先完整填写商机必填字段");
      return;
    }

    setSubmitting(true);

    try {
      await updateOpportunity(selectedItem.id, buildOpportunitySubmitPayload(form, competitorSelection, operatorMode));
      await reload(selectedItem.id);
      resetCreateState();
    } catch (updateError) {
      setError(updateError instanceof Error ? updateError.message : "编辑商机失败");
      setSubmitting(false);
    }
  };

  const handlePushToOms = async () => {
    if (!selectedItem || selectedItem.pushedToOms || pushingOms) {
      return;
    }

    setPushingOms(true);
    setError("");

    try {
      await pushOpportunityToOms(selectedItem.id);
      await reload(selectedItem.id);
    } catch (pushError) {
      setError(pushError instanceof Error ? pushError.message : "推送 OMS 失败");
    } finally {
      setPushingOms(false);
    }
  };

  const handleOpenPushConfirm = () => {
    if (!selectedItem || selectedItem.pushedToOms || pushingOms) {
      return;
    }
    setPushConfirmOpen(true);
  };

  const handleConfirmPushToOms = async () => {
    setPushConfirmOpen(false);
    await handlePushToOms();
  };

  const renderEmpty = () => (
    <div className="crm-empty-panel">
      {archiveTab === "active" ? "暂无未归档商机，先新增一条试试。" : "暂无已归档商机。"}
    </div>
  );

  return (
    <div className="crm-page-stack">
      <header className="crm-page-header">
        <div className="crm-page-heading">
          <h1 className="crm-page-title">商机储备</h1>
          <p className="crm-page-subtitle hidden sm:block">维护商机基础信息，查看关联拓展对象和日报自动回写的跟进记录。</p>
        </div>
        <button
          onClick={handleOpenCreate}
          className={cn("crm-btn-sm crm-btn-primary flex items-center gap-2", disableMobileMotion ? "active:scale-100" : "active:scale-95")}
        >
          <Plus className="crm-icon-md" />
          <span className="hidden sm:inline">新增商机</span>
        </button>
      </header>

      <div className={cn("crm-filter-bar flex", !disableMobileMotion && "backdrop-blur-sm")}>
        <button
          onClick={() => setArchiveTab("active")}
          className={`flex-1 rounded-lg py-2 text-sm font-medium transition-colors duration-200 ${
            archiveTab === "active"
              ? "bg-white text-violet-600 shadow-sm dark:bg-slate-800 dark:text-violet-400"
              : "text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white"
          }`}
        >
          未归档
        </button>
        <button
          onClick={() => setArchiveTab("archived")}
          className={`flex-1 rounded-lg py-2 text-sm font-medium transition-colors duration-200 ${
            archiveTab === "archived"
              ? "bg-white text-violet-600 shadow-sm dark:bg-slate-800 dark:text-violet-400"
              : "text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white"
          }`}
        >
          已归档
        </button>
      </div>

      <div className="flex items-center gap-3">
        <div className="relative group min-w-0 flex-1">
          <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400 group-focus-within:text-violet-500 transition-colors" />
          <input
            type="text"
            placeholder="搜索项目名称、最终客户、编码..."
            value={keyword}
            onChange={(event) => setKeyword(event.target.value)}
            className="crm-input-text w-full rounded-xl border border-slate-200 bg-white py-2.5 pl-10 pr-4 text-slate-900 outline-none transition-all focus:border-violet-500 focus:ring-1 focus:ring-violet-500 dark:border-slate-800 dark:bg-slate-900/50 dark:text-white"
          />
        </div>
        <button
          type="button"
          onClick={() => setStageFilterOpen(true)}
          className={`relative inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border transition-all ${
            filter !== "全部"
              ? "border-violet-200 bg-violet-50 text-violet-600 shadow-sm dark:border-violet-500/30 dark:bg-violet-500/10 dark:text-violet-300"
              : "border-slate-200 bg-white text-slate-500 hover:bg-slate-50 dark:border-slate-800 dark:bg-slate-900/50 dark:text-slate-400 dark:hover:bg-slate-800"
          }`}
          aria-label={`项目阶段筛选，当前：${activeStageFilterLabel}`}
          title={`项目阶段筛选：${activeStageFilterLabel}`}
        >
          <ListFilter className="h-5 w-5" />
          {filter !== "全部" ? <span className="absolute right-2 top-2 h-2 w-2 rounded-full bg-violet-500" /> : null}
        </button>
      </div>

      <div className="crm-list-stack">
        {visibleItems.length > 0 ? (
          visibleItems.map((opp, i) => (
            <motion.div
              initial={disableMobileMotion ? false : { opacity: 0, y: 10 }}
              animate={{ opacity: 1, y: 0 }}
              transition={disableMobileMotion ? { duration: 0 } : { delay: i * 0.05 }}
              key={opp.id}
              onClick={() => setSelectedItem(opp)}
              className="crm-card crm-card-pad group relative cursor-pointer rounded-2xl transition-shadow transition-colors hover:border-violet-100 hover:shadow-md dark:hover:border-violet-900/50"
            >
              <div className="flex items-start justify-between gap-3">
                <div className="min-w-0 flex-1">
                  <h3 className="truncate text-base font-semibold text-slate-900 dark:text-white sm:text-lg">{opp.name || "未命名商机"}</h3>
                  <p className="mt-1 truncate text-xs leading-5 text-slate-500 dark:text-slate-400 sm:text-sm">{opp.pushedToOms ? "已推送 OMS" : "未推送 OMS"}</p>
                </div>
                <div className="shrink-0 flex items-center gap-2 pl-2">
                  <span className={getConfidenceBadgeClass(opp.confidence)}>
                    {getConfidenceLabel(opp.confidence)}
                  </span>
                  <span className="crm-pill crm-pill-neutral">
                    {opp.stage || "初步沟通"}
                  </span>
                </div>
              </div>

              <div className="mt-4 space-y-3 text-xs sm:text-sm">
                <div className="flex min-w-0 items-center gap-2 text-slate-600 dark:text-slate-300">
                  <span className="shrink-0 text-slate-400 dark:text-slate-500">预计下单时间:</span>
                  <span className="min-w-0 flex-1 truncate font-medium text-slate-900 dark:text-white">{opp.date || "待定"}</span>
                </div>
                <div className="flex min-w-0 items-center gap-2 text-slate-600 dark:text-slate-300">
                  <span className="shrink-0 text-slate-400 dark:text-slate-500">预计金额:</span>
                  <span className="min-w-0 flex-1 truncate font-medium text-slate-900 dark:text-white">¥{formatAmount(opp.amount)}</span>
                </div>
                <div className="flex min-w-0 items-center gap-2 text-slate-600 dark:text-slate-300">
                  <span className="shrink-0 text-slate-400 dark:text-slate-500">项目最新进展:</span>
                  <span className="min-w-0 flex-1 truncate font-medium text-slate-900 dark:text-white">{opp.latestProgress || "暂无回写进展"}</span>
                </div>
                <div className="flex min-w-0 items-center gap-2 text-slate-600 dark:text-slate-300">
                  <span className="shrink-0 text-slate-400 dark:text-slate-500">后续规划:</span>
                  <span className="min-w-0 flex-1 truncate font-medium text-slate-900 dark:text-white">{opp.nextPlan || "暂无回写规划"}</span>
                </div>
              </div>

              <div className="mt-4 flex justify-end border-t border-slate-50 pt-3 dark:border-slate-800/50">
                <button type="button" className={detailBadgeClass}>
                  查看详情 <ChevronRight className="crm-icon-sm ml-0.5" />
                </button>
              </div>
            </motion.div>
          ))
        ) : renderEmpty()}
      </div>

      <AnimatePresence>
        {stageFilterOpen ? (
          <>
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              onClick={() => setStageFilterOpen(false)}
              className="fixed inset-0 z-[90] bg-slate-900/35 backdrop-blur-sm dark:bg-slate-950/70"
            />
            <motion.div
              initial={{ opacity: 0, y: 24 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: 24 }}
              className="fixed inset-x-0 bottom-0 z-[100] px-3 pb-[calc(0.75rem+env(safe-area-inset-bottom))] pt-3 sm:inset-0 sm:flex sm:items-center sm:justify-center sm:p-6"
            >
              <div className="mx-auto w-full max-w-md rounded-3xl border border-slate-200 bg-white shadow-2xl dark:border-slate-800 dark:bg-slate-900">
                <div className="border-b border-slate-100 px-5 py-4 dark:border-slate-800 sm:px-6">
                  <div className="mx-auto mb-3 h-1.5 w-10 rounded-full bg-slate-200 dark:bg-slate-700 sm:hidden" />
                  <div className="flex items-start justify-between gap-3">
                    <div className="min-w-0">
                      <h3 className="text-base font-semibold text-slate-900 dark:text-white">项目阶段筛选</h3>
                      <p className="mt-1 text-sm leading-6 text-slate-500 dark:text-slate-400">按项目阶段字典筛选商机数据。</p>
                    </div>
                    <button
                      type="button"
                      onClick={() => setStageFilterOpen(false)}
                      className="rounded-full p-2 text-slate-400 transition-colors hover:bg-slate-100 dark:hover:bg-slate-800"
                    >
                      <X className="h-5 w-5" />
                    </button>
                  </div>
                </div>
                <div className="px-4 py-4">
                  <div className="space-y-2">
                    {stageFilterOptions.map((stage) => (
                      <button
                        key={stage.value}
                        type="button"
                        onClick={() => {
                          setFilter(stage.value);
                          setStageFilterOpen(false);
                        }}
                        className={`flex w-full items-center justify-between rounded-2xl border px-4 py-3 text-left text-sm font-medium transition-colors ${
                          filter === stage.value
                            ? "border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-500/30 dark:bg-violet-500/10 dark:text-violet-300"
                            : "border-slate-200 bg-white text-slate-700 hover:bg-slate-50 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-200 dark:hover:bg-slate-800"
                        }`}
                      >
                        <span>{stage.label}</span>
                        {filter === stage.value ? <Check className="h-4 w-4 shrink-0" /> : null}
                      </button>
                    ))}
                  </div>
                </div>
              </div>
            </motion.div>
          </>
        ) : null}
      </AnimatePresence>

      <AnimatePresence>
        {(createOpen || editOpen) && (
          <ModalShell
            title={editOpen ? "编辑商机" : "新增商机"}
            subtitle={editOpen ? "支持手机与电脑端修改商机资料，保存后会同步刷新详情与列表。" : "支持手机与电脑端填写，提交后会自动刷新商机列表。"}
            onClose={resetCreateState}
            footer={(
              <div className="flex flex-col-reverse gap-3 sm:flex-row sm:justify-end">
                <button onClick={resetCreateState} className="crm-btn crm-btn-secondary">取消</button>
                <button onClick={() => void (editOpen ? handleEditSubmit() : handleCreateSubmit())} disabled={submitting} className="crm-btn crm-btn-primary disabled:cursor-not-allowed disabled:opacity-60">{submitting ? "提交中..." : editOpen ? "保存修改" : "确认新增"}</button>
              </div>
            )}
          >
            <div className="crm-form-grid">
              <label className="space-y-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">项目地<RequiredMark /></span>
                <input value={form.projectLocation || ""} onChange={(e) => handleChange("projectLocation", e.target.value)} className={getFieldInputClass(Boolean(fieldErrors.projectLocation))} />
                {fieldErrors.projectLocation ? <p className="text-xs text-rose-500">{fieldErrors.projectLocation}</p> : null}
              </label>
              <label className="space-y-2 sm:col-span-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">项目名称<RequiredMark /></span>
                <input value={form.opportunityName} onChange={(e) => handleChange("opportunityName", e.target.value)} className={getFieldInputClass(Boolean(fieldErrors.opportunityName))} />
                {fieldErrors.opportunityName ? <p className="text-xs text-rose-500">{fieldErrors.opportunityName}</p> : null}
              </label>
              <label className="space-y-2 sm:col-span-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">最终客户<RequiredMark /></span>
                <input value={form.customerName} onChange={(e) => handleChange("customerName", e.target.value)} className={getFieldInputClass(Boolean(fieldErrors.customerName))} />
                {fieldErrors.customerName ? <p className="text-xs text-rose-500">{fieldErrors.customerName}</p> : null}
              </label>
              <label className="space-y-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">运作方<RequiredMark /></span>
                <AdaptiveSelect
                  value={form.operatorName || ""}
                  placeholder="请选择"
                  sheetTitle="运作方"
                  options={[
                    { value: "", label: "请选择" },
                    ...operatorOptions.map((item) => ({
                      value: item.value || "",
                      label: item.label || item.value || "无",
                    })),
                  ]}
                  className={cn(
                    fieldErrors.operatorName ? "border-rose-400 bg-rose-50/60 focus:border-rose-500 focus:ring-rose-500 dark:border-rose-500/70 dark:bg-rose-500/10" : "",
                  )}
                  onChange={(value) => handleChange("operatorName", value)}
                />
                {fieldErrors.operatorName ? <p className="text-xs text-rose-500">{fieldErrors.operatorName}</p> : null}
              </label>
              {showSalesExpansionField ? (
                <label className="space-y-2">
                  <span className="text-sm font-medium text-slate-700 dark:text-slate-300">新华三负责人<RequiredMark /></span>
                  <SearchableSelect
                    value={form.salesExpansionId}
                    options={salesExpansionSearchOptions}
                    placeholder="请选择新华三负责人"
                    searchPlaceholder="搜索姓名、工号、办事处、电话"
                    emptyText="未找到匹配的销售拓展人员"
                    className={cn(
                      fieldErrors.salesExpansionId ? "border-rose-400 bg-rose-50/60 focus:border-rose-500 focus:ring-rose-500 dark:border-rose-500/70 dark:bg-rose-500/10" : "",
                    )}
                    onChange={(value) => handleChange("salesExpansionId", value)}
                  />
                  {fieldErrors.salesExpansionId ? <p className="text-xs text-rose-500">{fieldErrors.salesExpansionId}</p> : null}
                </label>
              ) : null}
              {showChannelExpansionField ? (
                <label className="space-y-2">
                  <span className="text-sm font-medium text-slate-700 dark:text-slate-300">渠道名称<RequiredMark /></span>
                  <SearchableSelect
                    value={form.channelExpansionId}
                    options={channelExpansionSearchOptions}
                    placeholder="请选择渠道名称"
                    searchPlaceholder="搜索渠道名称、编码、省份、联系人"
                    emptyText="未找到匹配的渠道"
                    className={cn(
                      fieldErrors.channelExpansionId ? "border-rose-400 bg-rose-50/60 focus:border-rose-500 focus:ring-rose-500 dark:border-rose-500/70 dark:bg-rose-500/10" : "",
                    )}
                    onChange={(value) => handleChange("channelExpansionId", value)}
                  />
                  {fieldErrors.channelExpansionId ? <p className="text-xs text-rose-500">{fieldErrors.channelExpansionId}</p> : null}
                </label>
              ) : null}
              <label className="space-y-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">预计金额（元）<RequiredMark /></span>
                <input type="number" min="0" value={form.amount || ""} onChange={(e) => handleChange("amount", Number(e.target.value) || 0)} className={getFieldInputClass(Boolean(fieldErrors.amount))} />
                {fieldErrors.amount ? <p className="text-xs text-rose-500">{fieldErrors.amount}</p> : null}
              </label>
              <label className="space-y-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">预计下单时间<RequiredMark /></span>
                <input type="date" value={form.expectedCloseDate} onChange={(e) => handleChange("expectedCloseDate", e.target.value)} className={`${getFieldInputClass(Boolean(fieldErrors.expectedCloseDate))} min-w-0`} />
                {fieldErrors.expectedCloseDate ? <p className="text-xs text-rose-500">{fieldErrors.expectedCloseDate}</p> : null}
              </label>
              <label className="space-y-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">项目把握度<RequiredMark /></span>
                <AdaptiveSelect
                  value={getConfidenceOptionValue(form.confidencePct)}
                  placeholder="请选择"
                  sheetTitle="项目把握度"
                  options={CONFIDENCE_OPTIONS.map((item) => ({ value: item.value, label: item.label }))}
                  className={cn(
                    fieldErrors.confidencePct ? "border-rose-400 bg-rose-50/60 focus:border-rose-500 focus:ring-rose-500 dark:border-rose-500/70 dark:bg-rose-500/10" : "",
                  )}
                  onChange={(value) => handleChange("confidencePct", Number(value) || 40)}
                />
                {fieldErrors.confidencePct ? <p className="text-xs text-rose-500">{fieldErrors.confidencePct}</p> : null}
              </label>
              <label className="space-y-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">项目阶段<RequiredMark /></span>
                <AdaptiveSelect
                  value={form.stage}
                  placeholder="请选择"
                  sheetTitle="项目阶段"
                  options={stageOptions.map((item) => ({
                    value: item.value || "",
                    label: item.label || item.value || "无",
                  }))}
                  className={cn(
                    fieldErrors.stage ? "border-rose-400 bg-rose-50/60 focus:border-rose-500 focus:ring-rose-500 dark:border-rose-500/70 dark:bg-rose-500/10" : "",
                  )}
                  onChange={(value) => handleChange("stage", value)}
                />
                {fieldErrors.stage ? <p className="text-xs text-rose-500">{fieldErrors.stage}</p> : null}
              </label>
              <label className="space-y-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">竞争对手<RequiredMark /></span>
                <AdaptiveSelect
                  value={competitorSelection}
                  placeholder="请选择"
                  sheetTitle="竞争对手"
                  options={COMPETITOR_OPTIONS.map((item) => ({ value: item, label: item }))}
                  className={cn(
                    fieldErrors.competitorName ? "border-rose-400 bg-rose-50/60 focus:border-rose-500 focus:ring-rose-500 dark:border-rose-500/70 dark:bg-rose-500/10" : "",
                  )}
                  onChange={(value) => {
                    const nextSelection = value as CompetitorOption;
                    setCompetitorSelection(nextSelection);
                    setFieldErrors((current) => {
                      if (!current.competitorName) {
                        return current;
                      }
                      const next = { ...current };
                      delete next.competitorName;
                      return next;
                    });
                    if (nextSelection === "其他") {
                      handleChange("competitorName", getCompetitorSelection(form.competitorName) === "其他" ? form.competitorName || "" : "");
                      return;
                    }
                    handleChange("competitorName", nextSelection || "");
                  }}
                />
                {fieldErrors.competitorName && competitorSelection !== "其他" ? <p className="text-xs text-rose-500">{fieldErrors.competitorName}</p> : null}
              </label>
              {competitorSelection === "其他" ? (
                <label className="space-y-2">
                  <span className="text-sm font-medium text-slate-700 dark:text-slate-300">其他竞争对手<RequiredMark /></span>
                  <input
                    value={getCompetitorSelection(form.competitorName) === "其他" ? form.competitorName || "" : ""}
                    onChange={(e) => handleChange("competitorName", e.target.value)}
                    placeholder="请输入竞争对手名称"
                    className={getFieldInputClass(Boolean(fieldErrors.competitorName))}
                  />
                  {fieldErrors.competitorName ? <p className="text-xs text-rose-500">{fieldErrors.competitorName}</p> : null}
                </label>
              ) : null}
              <label className="space-y-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">建设类型<RequiredMark /></span>
                <AdaptiveSelect
                  value={form.opportunityType}
                  sheetTitle="建设类型"
                  options={[
                    { value: "新建", label: "新建" },
                    { value: "扩容", label: "扩容" },
                    { value: "替换", label: "替换" },
                  ]}
                  className={cn(
                    fieldErrors.opportunityType ? "border-rose-400 bg-rose-50/60 focus:border-rose-500 focus:ring-rose-500 dark:border-rose-500/70 dark:bg-rose-500/10" : "",
                  )}
                  onChange={(value) => handleChange("opportunityType", value)}
                />
                {fieldErrors.opportunityType ? <p className="text-xs text-rose-500">{fieldErrors.opportunityType}</p> : null}
              </label>
              {selectedItem ? (
                <>
                  <label className="space-y-2 sm:col-span-2">
                    <span className="text-sm font-medium text-slate-700 dark:text-slate-300">项目最新进展</span>
                    <textarea
                      rows={3}
                      readOnly
                      value={selectedItem.latestProgress || "暂无日报回写进展"}
                      className="w-full cursor-not-allowed rounded-xl border border-slate-200 bg-slate-100 px-4 py-3 text-sm text-slate-500 outline-none dark:border-slate-800 dark:bg-slate-800/70 dark:text-slate-400"
                    />
                    <p className="text-xs text-slate-400 dark:text-slate-500">该字段由日报自动回写，基础信息编辑时不可修改。</p>
                  </label>
                  <label className="space-y-2 sm:col-span-2">
                    <span className="text-sm font-medium text-slate-700 dark:text-slate-300">后续规划</span>
                    <textarea
                      rows={3}
                      readOnly
                      value={selectedItem.nextPlan || "暂无日报回写规划"}
                      className="w-full cursor-not-allowed rounded-xl border border-slate-200 bg-slate-100 px-4 py-3 text-sm text-slate-500 outline-none dark:border-slate-800 dark:bg-slate-800/70 dark:text-slate-400"
                    />
                    <p className="text-xs text-slate-400 dark:text-slate-500">该字段由日报自动回写，基础信息编辑时不可修改。</p>
                  </label>
                </>
              ) : null}
              <label className="space-y-2 sm:col-span-2">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">备注说明</span>
                <textarea rows={4} value={form.description || ""} onChange={(e) => handleChange("description", e.target.value)} className="crm-input-box crm-input-text w-full border border-slate-200 bg-white outline-none focus:border-violet-500 focus:ring-1 focus:ring-violet-500 dark:border-slate-800 dark:bg-slate-900/50" />
              </label>
            </div>
            {error ? <div className="crm-alert crm-alert-error mt-4">{error}</div> : null}
          </ModalShell>
        )}
      </AnimatePresence>

      <AnimatePresence>
        {pushConfirmOpen && selectedItem ? (
          <>
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              onClick={() => setPushConfirmOpen(false)}
              className="fixed inset-0 z-[90] bg-slate-900/40 backdrop-blur-sm dark:bg-slate-950/70"
            />
            <motion.div
              initial={{ opacity: 0, y: 24 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: 24 }}
              className="fixed inset-x-0 bottom-0 z-[100] px-3 pb-[calc(0.75rem+env(safe-area-inset-bottom))] pt-3 sm:inset-0 sm:flex sm:items-center sm:justify-center sm:p-6"
            >
              <div className="mx-auto w-full max-w-md rounded-3xl border border-slate-200 bg-white shadow-2xl dark:border-slate-800 dark:bg-slate-900">
                <div className="border-b border-slate-100 px-5 py-4 dark:border-slate-800 sm:px-6">
                  <div className="mx-auto mb-3 h-1.5 w-10 rounded-full bg-slate-200 dark:bg-slate-700 sm:hidden" />
                  <div className="flex items-start gap-3">
                    <div className="crm-tone-warning mt-0.5 flex h-10 w-10 shrink-0 items-center justify-center rounded-2xl">
                      <AlertTriangle className="crm-icon-lg" />
                    </div>
                    <div className="min-w-0">
                      <h3 className="text-base font-semibold text-slate-900 dark:text-white">确认推送 OMS</h3>
                      <p className="mt-1 text-sm leading-6 text-slate-500 dark:text-slate-400">
                        推送 OMS 后不允许修改，是否确认推送？
                      </p>
                    </div>
                  </div>
                </div>
                <div className="px-5 py-4 text-sm text-slate-600 dark:text-slate-300 sm:px-6">
                  <div className="crm-form-section">
                    <p className="text-xs text-slate-400 dark:text-slate-500">当前商机</p>
                    <p className="mt-1 font-medium text-slate-900 dark:text-white">{selectedItem.name || selectedItem.code || `#${selectedItem.id}`}</p>
                  </div>
                </div>
                <div className="flex flex-col-reverse gap-3 px-5 pb-[calc(1rem+env(safe-area-inset-bottom))] pt-1 sm:flex-row sm:justify-end sm:px-6 sm:pb-5">
                  <button
                    type="button"
                    onClick={() => setPushConfirmOpen(false)}
                    className="crm-btn crm-btn-secondary min-h-0 rounded-2xl px-4 py-3"
                  >
                    取消
                  </button>
                  <button
                    type="button"
                    onClick={() => void handleConfirmPushToOms()}
                    disabled={pushingOms}
                    className="crm-btn crm-btn-primary min-h-0 rounded-2xl px-4 py-3 disabled:cursor-not-allowed disabled:opacity-60"
                  >
                    {pushingOms ? "推送中..." : "确认推送"}
                  </button>
                </div>
              </div>
            </motion.div>
          </>
        ) : null}
      </AnimatePresence>

      <AnimatePresence>
        {selectedItem && (
          <>
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              onClick={() => setSelectedItem(null)}
              className={`fixed inset-0 z-40 bg-slate-900/20 backdrop-blur-sm transition-opacity dark:bg-slate-900/60 ${hasForegroundModal ? "pointer-events-none opacity-30" : ""}`}
            />
            <motion.div
              initial={{ x: "100%", y: 0 }}
              animate={{ x: 0, y: 0 }}
              exit={{ x: "100%", y: 0 }}
              transition={{ type: "spring", damping: 25, stiffness: 200 }}
              className={`fixed inset-x-0 bottom-0 z-50 flex h-[90dvh] w-full flex-col rounded-t-3xl border border-slate-200 bg-white shadow-2xl transition-opacity dark:border-slate-800 dark:bg-slate-900 sm:inset-y-0 sm:right-0 sm:left-auto sm:h-full sm:w-[min(840px,92vw)] sm:max-w-none sm:rounded-none sm:rounded-l-3xl sm:border-l lg:w-[min(960px,90vw)] ${hasForegroundModal ? "pointer-events-none opacity-20" : ""}`}
            >
              <div className="flex items-center justify-between border-b border-slate-100 px-5 py-4 dark:border-slate-800 sm:px-6">
                <div className="flex items-center gap-3">
                  <div className="h-1.5 w-10 rounded-full bg-slate-200 sm:hidden dark:bg-slate-700" />
                  <h2 className="text-base font-semibold text-slate-900 dark:text-white sm:text-lg">商机详情</h2>
                </div>
                  <button onClick={() => setSelectedItem(null)} className="rounded-full p-2 text-slate-400 transition-colors hover:bg-slate-100 dark:hover:bg-slate-800">
                    <X className="crm-icon-lg" />
                </button>
              </div>

              <div className="flex-1 overflow-y-auto px-4 py-4 sm:px-6 sm:py-5">
                <div className="crm-modal-stack">
                <div>
                  <div className="mb-2 flex items-center gap-2">
                    <span className="text-xs font-medium text-slate-400 dark:text-slate-500">{selectedItem.code || `#${selectedItem.id}`}</span>
                    {selectedItem.pushedToOms ? <span className="crm-pill crm-pill-violet px-1.5 py-0.5 text-[10px]">已推送 OMS</span> : null}
                  </div>
                  <h3 className="line-clamp-1 text-lg font-bold leading-tight text-slate-900 dark:text-white sm:break-anywhere sm:line-clamp-none sm:text-xl">{selectedItem.name || "未命名商机"}</h3>
                  <div className="mt-3 flex flex-wrap gap-2">
                    <span className="crm-pill crm-pill-neutral">{selectedItem.stage || "初步沟通"}</span>
                    <span className={`rounded-full px-2.5 py-1 text-xs font-medium ${getConfidenceColor(selectedItem.confidence ?? 0)}`}>项目把握度 {getConfidenceLabel(selectedItem.confidence)}</span>
                  </div>
                </div>

                <div className="crm-section-stack">
                  <h4 className="flex items-center gap-2 text-sm font-semibold text-slate-900 dark:text-white">
                    <FileText className="crm-icon-md text-violet-500" />
                    基本信息
                  </h4>
                  <div className="crm-detail-grid text-sm md:grid-cols-2">
                    <DetailItem label="项目地" value={selectedItem.projectLocation || "无"} />
                    <DetailItem label="最终客户" value={selectedItem.client || "无"} icon={<Building className="h-3 w-3" />} />
                    <DetailItem label="运作方" value={selectedItem.operatorName || "无"} />
                    <DetailItem label="新华三负责人" value={selectedSalesExpansionName || "未关联"} icon={<User className="h-3 w-3" />} />
                    <DetailItem label="预计金额（元）" value={<span className="text-rose-600 dark:text-rose-400">¥{formatAmount(selectedItem.amount)}</span>} icon={<DollarSign className="h-3 w-3" />} />
                    <DetailItem label="预计下单时间" value={selectedItem.date || "待定"} icon={<Calendar className="h-3 w-3" />} />
                    <DetailItem label="项目把握度" value={getConfidenceLabel(selectedItem.confidence)} />
                    <DetailItem label="项目阶段" value={selectedItem.stage || "无"} icon={<Activity className="h-3 w-3" />} />
                    <DetailItem label="竞争对手" value={selectedItem.competitorName || "无"} />
                    <DetailItem label="建设类型" value={selectedItem.type || "新建"} icon={<Tag className="h-3 w-3" />} />
                    <DetailItem label="OMS 推送状态" value={selectedItem.pushedToOms ? "已推送 OMS" : "未推送 OMS"} />
                    <DetailItem label="项目最新进展" value={selectedItem.latestProgress || "暂无日报回写进展"} className="md:col-span-2" />
                    <DetailItem label="后续规划" value={selectedItem.nextPlan || "暂无日报回写规划"} className="md:col-span-2" />
                    <DetailItem label="备注说明" value={selectedItem.notes || "无"} className="md:col-span-2" />
                  </div>
                </div>

                <div className="crm-section-stack">
                  <div className="flex rounded-2xl border border-slate-200 bg-slate-50 p-1 dark:border-slate-800 dark:bg-slate-800/40">
                    <button
                      type="button"
                      onClick={() => setDetailTab("sales")}
                      className={`flex-1 rounded-xl px-2 py-2 text-xs font-medium transition-colors sm:px-4 sm:text-sm ${
                        detailTab === "sales"
                          ? "bg-white text-violet-600 shadow-sm dark:bg-slate-900 dark:text-violet-400"
                          : "text-slate-500 dark:text-slate-400"
                      }`}
                    >
                      销售拓展人员
                    </button>
                    <button
                      type="button"
                      onClick={() => setDetailTab("channel")}
                      className={`flex-1 rounded-xl px-2 py-2 text-xs font-medium transition-colors sm:px-4 sm:text-sm ${
                        detailTab === "channel"
                          ? "bg-white text-violet-600 shadow-sm dark:bg-slate-900 dark:text-violet-400"
                          : "text-slate-500 dark:text-slate-400"
                      }`}
                    >
                      拓展渠道
                    </button>
                    <button
                      type="button"
                      onClick={() => setDetailTab("followups")}
                      className={`flex-1 rounded-xl px-2 py-2 text-xs font-medium transition-colors sm:px-4 sm:text-sm ${
                        detailTab === "followups"
                          ? "bg-white text-violet-600 shadow-sm dark:bg-slate-900 dark:text-violet-400"
                          : "text-slate-500 dark:text-slate-400"
                      }`}
                    >
                      跟进记录
                    </button>
                  </div>

                  {detailTab === "sales" ? (
                    selectedSalesExpansion || selectedSalesExpansionName ? (
                      <div className="crm-detail-grid text-sm sm:grid-cols-3">
                        <DetailItem label="姓名" value={selectedSalesExpansion?.name || selectedSalesExpansionName || "无"} />
                        <DetailItem label="合作意向" value={selectedSalesExpansion?.intent || "无"} />
                        <DetailItem label="是否在职" value={selectedSalesExpansion ? (selectedSalesExpansion.active ? "是" : "否") : "待同步"} />
                      </div>
                    ) : (
                      <div className="crm-empty-panel">
                        暂无关联销售拓展人员
                      </div>
                    )
                  ) : null}

                  {detailTab === "channel" ? (
                    selectedChannelExpansion || selectedChannelExpansionName ? (
                      <div className="crm-detail-grid text-sm sm:grid-cols-3">
                        <DetailItem label="渠道名称" value={selectedChannelExpansion?.name || selectedChannelExpansionName || "无"} />
                        <DetailItem label="合作意向" value={selectedChannelExpansion?.intent || "无"} />
                        <DetailItem label="建立联系时间" value={selectedChannelExpansion?.establishedDate || "无"} />
                      </div>
                    ) : (
                      <div className="crm-empty-panel">
                        暂无关联拓展渠道
                      </div>
                    )
                  ) : null}

                  {detailTab === "followups" ? (
                    <div className="crm-section-stack">
                      <p className="crm-field-note">工作台日报里关联到该商机的工作内容，会自动回写到这里。</p>
                      <div className="crm-section-stack">
                        <h4 className="flex items-center gap-2 text-sm font-semibold text-slate-900 dark:text-white">
                          <Clock className="crm-icon-md text-violet-500" />
                          交流记录
                        </h4>
                        {followUpRecords.length > 0 ? (
                          <div className="relative space-y-6 border-l-2 border-slate-100 pl-4 dark:border-slate-800">
                            {followUpRecords.map((record) => {
                              const summary = getOpportunityFollowUpSummary(record);
                              return (
                                <div key={record.id} className="relative">
                                  <div className="absolute -left-[21px] mt-1.5 h-2.5 w-2.5 rounded-full bg-violet-500 ring-4 ring-white dark:ring-slate-900" />
                                  <div className="crm-form-section">
                                    <div className="rounded-xl border border-amber-200 bg-amber-50 p-3 text-sm dark:border-amber-500/20 dark:bg-amber-500/10">
                                      <p className="mb-1 text-xs text-amber-700 dark:text-amber-300">沟通内容</p>
                                      <p className="whitespace-pre-wrap font-medium leading-6 text-slate-900 dark:text-white">{summary.communicationContent}</p>
                                    </div>
                                    <p className="mt-2 text-xs text-slate-400">跟进人: {record.user || "无"}<span className="ml-3">{record.date || "无"}</span></p>
                                  </div>
                                </div>
                              );
                            })}
                          </div>
                        ) : (
                          <div className="crm-empty-panel">
                            暂无交流记录
                          </div>
                        )}
                      </div>
                    </div>
                  ) : null}
                </div>
              </div>
              </div>
              <div className="sticky bottom-0 bg-slate-50/95 px-4 pb-[calc(1rem+env(safe-area-inset-bottom))] pt-4 backdrop-blur sm:static sm:p-4 dark:bg-slate-900/90">
                {error ? <div className="crm-alert crm-alert-error mb-3">{error}</div> : null}
                <div className="grid grid-cols-2 gap-3">
                  <button
                    onClick={handleOpenEdit}
                    disabled={Boolean(selectedItem.pushedToOms)}
                    className="crm-btn crm-btn-secondary inline-flex h-11 items-center justify-center disabled:cursor-not-allowed disabled:opacity-50"
                  >
                    编辑商机
                  </button>
                  <button
                    onClick={handleOpenPushConfirm}
                    disabled={Boolean(selectedItem.pushedToOms) || pushingOms}
                    className="crm-btn-sm crm-btn-primary inline-flex items-center justify-center disabled:cursor-not-allowed disabled:opacity-60"
                  >
                    {selectedItem.pushedToOms ? "已推送 OMS" : pushingOms ? "推送中..." : "推送 OMS"}
                  </button>
                </div>
              </div>
            </motion.div>
          </>
        )}
      </AnimatePresence>
    </div>
  );
}

function getOpportunityFollowUpSummary(record: {
  content?: string;
  latestProgress?: string;
  communicationContent?: string;
  nextAction?: string;
}) {
  const content = record.content || "";
  const parsedLatestProgress = extractOpportunityFollowUpField(content, "项目最新进展");
  const parsedNextPlan = extractOpportunityFollowUpField(content, "后续规划");
  const communicationContent = buildOpportunityCommunicationContent(
    pickOpportunityFollowUpValue(record.latestProgress, parsedLatestProgress),
    pickOpportunityFollowUpValue(record.nextAction, parsedNextPlan),
    pickOpportunityFollowUpValue(
      record.communicationContent,
      extractLegacyOpportunityCommunicationContent(extractOpportunityFollowUpField(content, "交流记录")),
    ),
  );

  return {
    communicationContent: normalizeOpportunityFollowUpDisplayValue(communicationContent),
  };
}

function extractOpportunityFollowUpField(content: string, label: string) {
  const normalized = content.replace(/\r/g, "");
  const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const match = normalized.match(new RegExp(`${escapedLabel}：([^\\n]+)`));
  return match?.[1]?.trim();
}

function extractLegacyOpportunityCommunicationContent(rawValue?: string) {
  const normalized = rawValue?.trim();
  if (!normalized || normalized === "无") {
    return undefined;
  }

  const match = normalized.match(/^(\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2})\s+(.+)$/);
  if (!match) {
    return normalized;
  }

  return match[2].trim();
}

function pickOpportunityFollowUpValue(...values: Array<string | undefined>) {
  return values.find((value) => {
    const normalized = value?.trim();
    return normalized && normalized !== "无";
  });
}

function buildOpportunityCommunicationContent(
  latestProgress?: string,
  nextPlan?: string,
  legacyCommunicationContent?: string,
) {
  const parts = [
    latestProgress?.trim() ? `项目最新进展：${latestProgress.trim()}` : "",
    nextPlan?.trim() ? `后续规划：${nextPlan.trim()}` : "",
  ].filter(Boolean);
  if (parts.length > 0) {
    return parts.join("\n");
  }
  return legacyCommunicationContent?.trim();
}

function normalizeOpportunityFollowUpDisplayValue(value?: string) {
  const normalized = value?.trim();
  return normalized ? normalized : "无";
}
