"use client";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import * as React from "react";
import { useQuery } from "@tanstack/react-query";
import { Button } from "@open-mercato/ui/primitives/button";
import { LoadingMessage, ErrorMessage } from "@open-mercato/ui/backend/detail";
import {
  apiCall,
  readApiResultOrThrow,
  withScopedApiRequestHeaders
} from "@open-mercato/ui/backend/utils/apiCall";
import { useT } from "@open-mercato/shared/lib/i18n/context";
import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
import { FilterBarRow } from "../../pipeline/components/FilterBarRow.js";
import { StatusFilterPopover } from "../../pipeline/components/StatusFilterPopover.js";
import {
  PipelineFilterPopover
} from "../../pipeline/components/PipelineFilterPopover.js";
import {
  EntityFilterPopover
} from "../../pipeline/components/EntityFilterPopover.js";
import {
  CloseDateFilterPopover
} from "../../pipeline/components/CloseDateFilterPopover.js";
import { SortByPopover } from "../../pipeline/components/SortByPopover.js";
import { fetchAssignableStaffMembers } from "../../../../../components/detail/assignableStaff.js";
import { DealsLocationPanel } from "./DealsLocationPanel.js";
import {
  DealsMapCanvas
} from "./DealsMapCanvas.js";
const DEFAULT_SORT = "updated_desc";
const PAGE_SIZE = 100;
const MAX_DEALS = 500;
const FALLBACK_TONES = ["success", "warning", "error", "info", "neutral", "brand"];
const KNOWN_STAGE_TONES = /* @__PURE__ */ new Set([
  "success",
  "warning",
  "info",
  "error",
  "neutral",
  "brand",
  "pink"
]);
const SUPPRESS_AUTH_REDIRECT_HEADERS = {
  "x-om-forbidden-redirect": "0",
  "x-om-unauthorized-redirect": "0"
};
const NO_FILTER_CHIPS = [];
const noopChipClick = (_chipId) => {
};
function normalizeNumber(value) {
  if (typeof value === "number" && Number.isFinite(value)) return value;
  if (typeof value === "string" && value.trim().length) {
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : null;
  }
  return null;
}
function normalizeText(value) {
  return typeof value === "string" && value.trim().length ? value.trim() : null;
}
function normalizeLocation(location) {
  if (!location) return null;
  const latitude = normalizeNumber(location.latitude);
  const longitude = normalizeNumber(location.longitude);
  if (latitude === null || longitude === null) return null;
  return {
    latitude,
    longitude,
    city: normalizeText(location.city),
    region: normalizeText(location.region),
    country: normalizeText(location.country)
  };
}
function mapApiItem(item, fallbackTitle) {
  const id = normalizeText(item.id);
  if (!id) return null;
  const firstCompany = Array.isArray(item.companies) ? item.companies.find((entry) => entry && normalizeText(entry.label)) : null;
  return {
    id,
    title: normalizeText(item.title) ?? fallbackTitle,
    status: normalizeText(item.status),
    pipelineId: normalizeText(item.pipelineId),
    pipelineStageId: normalizeText(item.pipelineStageId),
    valueAmount: normalizeNumber(item.valueAmount),
    valueCurrency: normalizeText(item.valueCurrency)?.toUpperCase() ?? null,
    probability: normalizeNumber(item.probability),
    expectedCloseAt: normalizeText(item.expectedCloseAt),
    ownerUserId: normalizeText(item.ownerUserId),
    companyLabel: firstCompany ? normalizeText(firstCompany.label) : null,
    location: normalizeLocation(item.location)
  };
}
function mapSortOptionToApi(option) {
  switch (option) {
    case "updated_desc":
      return { sortField: "updatedAt", sortDir: "desc" };
    case "updated_asc":
      return { sortField: "updatedAt", sortDir: "asc" };
    case "created_desc":
      return { sortField: "createdAt", sortDir: "desc" };
    case "value_desc":
      return { sortField: "value", sortDir: "desc" };
    case "value_asc":
      return { sortField: "value", sortDir: "asc" };
    case "probability_desc":
      return { sortField: "probability", sortDir: "desc" };
    case "close_asc":
      return { sortField: "expectedCloseAt", sortDir: "asc" };
    case "owner_asc":
    default:
      return null;
  }
}
function buildStageMetaById(stages) {
  const byPipeline = /* @__PURE__ */ new Map();
  for (const stage of stages) {
    const key = stage.pipelineId ?? "";
    const bucket = byPipeline.get(key) ?? [];
    bucket.push(stage);
    byPipeline.set(key, bucket);
  }
  const meta = /* @__PURE__ */ new Map();
  for (const bucket of byPipeline.values()) {
    const sorted = bucket.slice().sort((a, b) => a.order - b.order);
    sorted.forEach((stage, index) => {
      const tone = stage.color && KNOWN_STAGE_TONES.has(stage.color) ? stage.color : FALLBACK_TONES[index % FALLBACK_TONES.length];
      meta.set(stage.id, { label: stage.label, tone });
    });
  }
  return meta;
}
function DealsMapView({ search }) {
  const t = useT();
  const scopeVersion = useOrganizationScopeVersion();
  const [statusFilters, setStatusFilters] = React.useState([]);
  const [selectedPipelineId, setSelectedPipelineId] = React.useState(null);
  const [ownerFilters, setOwnerFilters] = React.useState([]);
  const [peopleFilters, setPeopleFilters] = React.useState([]);
  const [companyFilters, setCompanyFilters] = React.useState([]);
  const [closeDateFilter, setCloseDateFilter] = React.useState({ from: null, to: null });
  const [sortBy, setSortBy] = React.useState(DEFAULT_SORT);
  const [ownerLabels, setOwnerLabels] = React.useState({});
  const [peopleLabels, setPeopleLabels] = React.useState({});
  const [companyLabels, setCompanyLabels] = React.useState({});
  const [selectedDealId, setSelectedDealId] = React.useState(null);
  const [mapCenter, setMapCenter] = React.useState(null);
  const [debouncedSearch, setDebouncedSearch] = React.useState(search);
  React.useEffect(() => {
    const handle = window.setTimeout(() => setDebouncedSearch(search.trim()), 300);
    return () => window.clearTimeout(handle);
  }, [search]);
  const fallbackTitle = translateWithFallback(t, "customers.deals.pipeline.untitled", "Untitled deal");
  const loadErrorLabel = translateWithFallback(
    t,
    "customers.deals.map.loadError",
    "Failed to load deals for the map."
  );
  const apiSort = React.useMemo(() => {
    const mapped = mapSortOptionToApi(sortBy);
    return mapped ?? { sortField: "updatedAt", sortDir: "desc" };
  }, [sortBy]);
  const pipelinesQuery = useQuery({
    queryKey: ["customers", "deals", "map", "pipelines", `scope:${scopeVersion}`],
    staleTime: 6e4,
    queryFn: async () => {
      const call = await apiCall("/api/customers/pipelines", {
        headers: SUPPRESS_AUTH_REDIRECT_HEADERS
      });
      if (!call.ok) return [];
      return Array.isArray(call.result?.items) ? call.result.items : [];
    }
  });
  const stagesQuery = useQuery({
    queryKey: ["customers", "deals", "map", "stages", `scope:${scopeVersion}`],
    staleTime: 6e4,
    queryFn: async () => {
      const call = await apiCall("/api/customers/pipeline-stages", {
        headers: SUPPRESS_AUTH_REDIRECT_HEADERS
      });
      if (!call.ok) return [];
      return Array.isArray(call.result?.items) ? call.result.items : [];
    }
  });
  const staffQuery = useQuery({
    queryKey: ["customers", "deals", "map", "staff", `scope:${scopeVersion}`],
    staleTime: 3e5,
    queryFn: async () => {
      try {
        const members = await withScopedApiRequestHeaders(
          { ...SUPPRESS_AUTH_REDIRECT_HEADERS },
          () => fetchAssignableStaffMembers("", { pageSize: 100 })
        );
        const names = /* @__PURE__ */ new Map();
        for (const member of members) {
          if (member.userId && member.displayName) names.set(member.userId, member.displayName);
        }
        return names;
      } catch {
        return /* @__PURE__ */ new Map();
      }
    }
  });
  const ownerNamesById = staffQuery.data ?? null;
  const dealsQuery = useQuery({
    queryKey: [
      "customers",
      "deals",
      "map",
      "deals",
      `scope:${scopeVersion}`,
      `search:${debouncedSearch}`,
      `status:${statusFilters.slice().sort((a, b) => a.localeCompare(b)).join(",")}`,
      `pipeline:${selectedPipelineId ?? ""}`,
      `owners:${ownerFilters.slice().sort((a, b) => a.localeCompare(b)).join(",")}`,
      `people:${peopleFilters.slice().sort((a, b) => a.localeCompare(b)).join(",")}`,
      `companies:${companyFilters.slice().sort((a, b) => a.localeCompare(b)).join(",")}`,
      `close:${closeDateFilter.from ?? ""}-${closeDateFilter.to ?? ""}`,
      `sort:${apiSort.sortField}:${apiSort.sortDir}`
    ],
    staleTime: 3e4,
    queryFn: async () => {
      const buildParams = (page) => {
        const params = new URLSearchParams();
        params.set("page", String(page));
        params.set("pageSize", String(PAGE_SIZE));
        params.set("sortField", apiSort.sortField);
        params.set("sortDir", apiSort.sortDir);
        if (debouncedSearch.length) params.set("search", debouncedSearch);
        for (const status of statusFilters) params.append("status", status);
        if (selectedPipelineId) params.set("pipelineId", selectedPipelineId);
        for (const ownerId of ownerFilters) params.append("ownerUserId", ownerId);
        for (const personId of peopleFilters) params.append("personId", personId);
        for (const companyId of companyFilters) params.append("companyId", companyId);
        if (closeDateFilter.from) params.set("expectedCloseAtFrom", closeDateFilter.from);
        if (closeDateFilter.to) params.set("expectedCloseAtTo", closeDateFilter.to);
        return params;
      };
      const fetchPage = (page) => readApiResultOrThrow(
        `/api/customers/deals/map?${buildParams(page).toString()}`,
        void 0,
        { errorMessage: loadErrorLabel }
      );
      const collected = [];
      const collectFrom = (payload) => {
        const items = Array.isArray(payload?.items) ? payload.items : [];
        for (const item of items) {
          const mapped = mapApiItem(item, fallbackTitle);
          if (mapped) collected.push(mapped);
        }
      };
      const firstPage = await fetchPage(1);
      collectFrom(firstPage);
      const total = typeof firstPage?.total === "number" ? firstPage.total : collected.length;
      const reportedTotalPages = typeof firstPage?.totalPages === "number" ? firstPage.totalPages : 1;
      const maxPages = Math.max(1, Math.ceil(MAX_DEALS / PAGE_SIZE));
      const lastPage = Math.min(reportedTotalPages, maxPages);
      if (lastPage > 1) {
        const rest = await Promise.all(
          Array.from({ length: lastPage - 1 }, (_unused, index) => fetchPage(index + 2))
        );
        for (const payload of rest) collectFrom(payload);
      }
      const deals2 = collected.slice(0, MAX_DEALS);
      return { deals: deals2, total, truncated: total > deals2.length };
    }
  });
  const stageMetaById = React.useMemo(
    () => buildStageMetaById(stagesQuery.data ?? []),
    [stagesQuery.data]
  );
  const deals = React.useMemo(() => {
    const fetched = dealsQuery.data?.deals ?? [];
    if (sortBy !== "owner_asc" || !ownerNamesById) return fetched;
    return fetched.slice().sort((a, b) => {
      const aLabel = (a.ownerUserId ? ownerNamesById.get(a.ownerUserId) : null) ?? a.ownerUserId ?? "";
      const bLabel = (b.ownerUserId ? ownerNamesById.get(b.ownerUserId) : null) ?? b.ownerUserId ?? "";
      return aLabel.localeCompare(bLabel);
    });
  }, [dealsQuery.data, sortBy, ownerNamesById]);
  const locatedDeals = React.useMemo(
    () => deals.filter((deal) => deal.location !== null),
    [deals]
  );
  const canvasDeals = React.useMemo(
    () => locatedDeals.map((deal) => ({
      id: deal.id,
      latitude: deal.location.latitude,
      longitude: deal.location.longitude,
      tone: deal.pipelineStageId ? stageMetaById.get(deal.pipelineStageId)?.tone ?? null : null
    })),
    [locatedDeals, stageMetaById]
  );
  const legendStages = React.useMemo(() => {
    if (stageMetaById.size === 0) return [];
    const presentStageIds = /* @__PURE__ */ new Set();
    for (const deal of locatedDeals) {
      if (deal.pipelineStageId) presentStageIds.add(deal.pipelineStageId);
    }
    const stages = [];
    for (const stage of stagesQuery.data ?? []) {
      if (!presentStageIds.has(stage.id)) continue;
      const meta = stageMetaById.get(stage.id);
      if (!meta) continue;
      stages.push({ id: stage.id, label: meta.label, tone: meta.tone });
    }
    return stages;
  }, [locatedDeals, stageMetaById, stagesQuery.data]);
  const selectedDeal = React.useMemo(
    () => selectedDealId ? deals.find((deal) => deal.id === selectedDealId) ?? null : null,
    [deals, selectedDealId]
  );
  const preview = React.useMemo(() => {
    if (!selectedDeal) return null;
    const stageMeta = selectedDeal.pipelineStageId ? stageMetaById.get(selectedDeal.pipelineStageId) ?? null : null;
    const locationLine = selectedDeal.location ? [selectedDeal.location.city, selectedDeal.location.region].filter(Boolean).join(", ") || selectedDeal.location.country : null;
    return {
      id: selectedDeal.id,
      title: selectedDeal.title,
      companyLabel: selectedDeal.companyLabel,
      locationLine: locationLine || null,
      valueAmount: selectedDeal.valueAmount,
      valueCurrency: selectedDeal.valueCurrency,
      probability: selectedDeal.probability,
      expectedCloseAt: selectedDeal.expectedCloseAt,
      stageLabel: stageMeta?.label ?? null,
      stageTone: stageMeta?.tone ?? null,
      ownerName: selectedDeal.ownerUserId && ownerNamesById ? ownerNamesById.get(selectedDeal.ownerUserId) ?? null : null
    };
  }, [selectedDeal, stageMetaById, ownerNamesById]);
  const handleSelect = React.useCallback((dealId) => {
    setSelectedDealId(dealId);
  }, []);
  const handleCenterChange = React.useCallback((center) => {
    setMapCenter(center);
  }, []);
  const pipelineFilterOptions = React.useMemo(
    () => (pipelinesQuery.data ?? []).map((pipeline) => ({ id: pipeline.id, name: pipeline.name })),
    [pipelinesQuery.data]
  );
  const loadOwnerOptions = React.useCallback(
    async (query, _signal) => {
      try {
        const members = await withScopedApiRequestHeaders(
          { ...SUPPRESS_AUTH_REDIRECT_HEADERS },
          () => fetchAssignableStaffMembers(query ?? "", { pageSize: 100 })
        );
        const options = members.filter((member) => !!member.userId && !!member.displayName).map((member) => ({ value: member.userId, label: member.displayName }));
        setOwnerLabels((prev) => {
          const next = { ...prev };
          for (const option of options) next[option.value] = option.label;
          return next;
        });
        return options;
      } catch {
        return [];
      }
    },
    []
  );
  const loadPeopleOptions = React.useCallback(
    async (query, signal) => {
      const params = new URLSearchParams();
      params.set("page", "1");
      params.set("pageSize", "50");
      if (query) params.set("search", query);
      params.set("sortField", "displayName");
      params.set("sortDir", "asc");
      const call = await apiCall(`/api/customers/people?${params.toString()}`, {
        signal,
        headers: SUPPRESS_AUTH_REDIRECT_HEADERS
      });
      if (!call.ok) return [];
      const items = call.result?.items ?? [];
      const options = [];
      for (const item of items) {
        if (!item.id) continue;
        const label = item.display_name && item.display_name.trim().length ? item.display_name.trim() : [item.first_name, item.last_name].filter(Boolean).join(" ").trim() || item.id.slice(0, 8);
        options.push({ value: item.id, label });
      }
      setPeopleLabels((prev) => {
        const next = { ...prev };
        for (const option of options) next[option.value] = option.label;
        return next;
      });
      return options;
    },
    []
  );
  const loadCompanyOptions = React.useCallback(
    async (query, signal) => {
      const params = new URLSearchParams();
      params.set("page", "1");
      params.set("pageSize", "50");
      if (query) params.set("search", query);
      params.set("sortField", "display_name");
      params.set("sortDir", "asc");
      const call = await apiCall(
        `/api/customers/companies?${params.toString()}`,
        { signal, headers: SUPPRESS_AUTH_REDIRECT_HEADERS }
      );
      if (!call.ok) return [];
      const items = call.result?.items ?? [];
      const options = [];
      for (const item of items) {
        if (!item.id || !item.display_name) continue;
        options.push({ value: item.id, label: item.display_name });
      }
      setCompanyLabels((prev) => {
        const next = { ...prev };
        for (const option of options) next[option.value] = option.label;
        return next;
      });
      return options;
    },
    []
  );
  const leadingChipsNode = /* @__PURE__ */ jsxs(Fragment, { children: [
    /* @__PURE__ */ jsx(StatusFilterPopover, { values: statusFilters, onApply: setStatusFilters }),
    /* @__PURE__ */ jsx(
      PipelineFilterPopover,
      {
        pipelines: pipelineFilterOptions,
        selectedPipelineId,
        onApply: setSelectedPipelineId
      }
    ),
    /* @__PURE__ */ jsx(
      EntityFilterPopover,
      {
        label: translateWithFallback(t, "customers.deals.kanban.filter.owner", "Owner"),
        anyLabel: translateWithFallback(t, "customers.deals.kanban.filter.all", "All"),
        values: ownerFilters,
        onApply: setOwnerFilters,
        loadOptions: loadOwnerOptions,
        labelById: ownerLabels
      }
    ),
    /* @__PURE__ */ jsx(
      EntityFilterPopover,
      {
        label: translateWithFallback(t, "customers.deals.kanban.filter.people", "People"),
        values: peopleFilters,
        onApply: setPeopleFilters,
        loadOptions: loadPeopleOptions,
        labelById: peopleLabels
      }
    ),
    /* @__PURE__ */ jsx(
      EntityFilterPopover,
      {
        label: translateWithFallback(t, "customers.deals.kanban.filter.companies", "Companies"),
        values: companyFilters,
        onApply: setCompanyFilters,
        loadOptions: loadCompanyOptions,
        labelById: companyLabels
      }
    ),
    /* @__PURE__ */ jsx(CloseDateFilterPopover, { value: closeDateFilter, onApply: setCloseDateFilter })
  ] });
  const sortNode = /* @__PURE__ */ jsx(SortByPopover, { value: sortBy, onApply: setSortBy });
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
    /* @__PURE__ */ jsx(
      FilterBarRow,
      {
        leadingChips: leadingChipsNode,
        chips: NO_FILTER_CHIPS,
        sortNode,
        onChipClick: noopChipClick
      }
    ),
    dealsQuery.isLoading ? /* @__PURE__ */ jsx(
      LoadingMessage,
      {
        label: translateWithFallback(t, "customers.deals.map.canvas.loading", "Loading map\u2026")
      }
    ) : dealsQuery.isError ? /* @__PURE__ */ jsx(
      ErrorMessage,
      {
        label: dealsQuery.error instanceof Error ? dealsQuery.error.message : loadErrorLabel,
        action: /* @__PURE__ */ jsx(
          Button,
          {
            type: "button",
            variant: "outline",
            size: "sm",
            onClick: () => void dealsQuery.refetch(),
            children: translateWithFallback(t, "customers.deals.map.retry", "Retry")
          }
        )
      }
    ) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
      dealsQuery.data?.truncated ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: translateWithFallback(
        t,
        "customers.deals.map.truncated",
        "Showing first {count} of {total} located deals \u2014 refine filters to narrow the map.",
        { count: deals.length, total: dealsQuery.data?.total ?? deals.length }
      ) }) : null,
      /* @__PURE__ */ jsxs("div", { className: "flex min-h-[480px] flex-col gap-4 lg:h-[calc(100vh-360px)] lg:flex-row", children: [
        /* @__PURE__ */ jsx(
          DealsLocationPanel,
          {
            deals,
            locatedCount: dealsQuery.data?.total ?? deals.length,
            stageMetaById,
            mapCenter,
            selectedDealId,
            onSelect: handleSelect
          }
        ),
        /* @__PURE__ */ jsx(
          DealsMapCanvas,
          {
            className: "min-h-[480px] flex-1 overflow-hidden rounded-xl border border-border lg:min-h-0",
            deals: canvasDeals,
            legendStages,
            preview,
            selectedDealId,
            onSelect: handleSelect,
            onCenterChange: handleCenterChange
          }
        )
      ] })
    ] })
  ] });
}
var DealsMapView_default = DealsMapView;
export {
  DealsMapView,
  DealsMapView_default as default
};
//# sourceMappingURL=DealsMapView.js.map
