"use client";

import * as React from "react";

import { useRouter } from "next/navigation";
import { ExternalLink, FileSearch, Loader2 } from "lucide-react";

import { Button } from "@/components/ui/button";
import {
  Command,
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command";
import { useDebouncedValue } from "@/hooks/use-debounced-value";
import { cn } from "@/lib/utils";
import { buildCustomerSidebarPanelsFromNavigation, type CustomerNavigationPayload } from "@/navigation/sidebar/dynamic-sidebar";
import type { NavMainItem } from "@/navigation/sidebar/frontend-items";
import type { ConsolidatedHolding } from "@/app/customer/[tenant]/reports/consolidated-holdings-report/_components/consolidated-holdings-report/schema";
import { formatMoney } from "@/app/customer/[tenant]/reports/consolidated-holdings-report/_components/consolidated-holdings-report/utils";
import {
  ConsolidatedIndividualDialog,
  type ConsolidatedIndividualTarget,
} from "@/app/customer/[tenant]/reports/consolidated-holdings-report/_components/consolidated-holdings-report/consolidated-individual-dialog";

import { setChrHoldingsSearchHandoff } from "./holdings-search-handoff";
import {
  type CollapsedHoldingHit,
  clearCachedHoldings,
  collapsedSubtitle,
  collapseHoldingsByIsin,
  consolidatedReportHref,
  countCollapsedByAssetClass,
  getCachedHoldings,
  hasConsolidatedHoldingsAccess,
  holdingMatchesQuery,
  holdingsListErrorMessage,
  HOLDINGS_SEARCH_LOAD_TIMEOUT_MS,
  HOLDINGS_SEARCH_MAX_RESULTS,
  parseHoldingsListResponse,
  setCachedHoldings,
  unpopulatedFilterKeys,
} from "./holdings-search-lib";

const FILTER_KEY_LABELS: Record<string, string> = {
  country: "Country",
  sector: "Sector",
  industry: "Industry",
};

type HoldingsSearchDialogContentProps = {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  tenant: string;
  navigation?: CustomerNavigationPayload | null;
  allowedRoutes?: readonly string[];
};

function flattenNavUrls(
  tenant: string,
  navigation: CustomerNavigationPayload | null | undefined,
  allowedRoutes?: readonly string[],
): string[] {
  const panels = buildCustomerSidebarPanelsFromNavigation(tenant, navigation, allowedRoutes);
  const urls: string[] = [];

  const walk = (item: NavMainItem) => {
    if (item.subItems?.length) {
      for (const sub of item.subItems) {
        if (!sub.comingSoon) urls.push(sub.url);
      }
      return;
    }
    if (!item.comingSoon) urls.push(item.url);
  };

  for (const panel of panels) {
    for (const group of panel.groups) {
      for (const item of group.items) walk(item);
    }
  }
  return urls;
}

export function HoldingsSearchDialogContent({
  open,
  onOpenChange,
  tenant,
  navigation = null,
  allowedRoutes,
}: HoldingsSearchDialogContentProps) {
  const [query, setQuery] = React.useState("");
  const [classFilter, setClassFilter] = React.useState<string | null>(null);
  const [allHoldings, setAllHoldings] = React.useState<ConsolidatedHolding[]>([]);
  const [assetClasses, setAssetClasses] = React.useState<string[]>([]);
  const [loading, setLoading] = React.useState(false);
  const [loadError, setLoadError] = React.useState<string | null>(null);
  const [reloadNonce, setReloadNonce] = React.useState(0);
  const [individualOpen, setIndividualOpen] = React.useState(false);
  const [individualTarget, setIndividualTarget] =
    React.useState<ConsolidatedIndividualTarget | null>(null);
  const router = useRouter();
  const debouncedQuery = useDebouncedValue(query, 200);

  const canConsolidated = React.useMemo(
    () => hasConsolidatedHoldingsAccess(flattenNavUrls(tenant, navigation, allowedRoutes)),
    [tenant, navigation, allowedRoutes],
  );

  React.useEffect(() => {
    if (!open) {
      setLoading(false);
      return;
    }
    if (!canConsolidated) {
      setLoading(false);
      setLoadError(null);
      return;
    }

    const cached = getCachedHoldings(tenant);
    if (cached) {
      setAllHoldings(cached.holdings);
      setAssetClasses(cached.assetClasses);
      setLoadError(null);
      setLoading(false);
      return;
    }

    let cancelled = false;
    const controller = new AbortController();
    const timeoutId = window.setTimeout(() => controller.abort(), HOLDINGS_SEARCH_LOAD_TIMEOUT_MS);

    setLoading(true);
    setLoadError(null);

    void (async () => {
      try {
        const response = await fetch(
          `/customer/${encodeURIComponent(tenant)}/pdf/consolidated/list`,
          {
            method: "GET",
            credentials: "same-origin",
            headers: { Accept: "application/json" },
            cache: "no-store",
            signal: controller.signal,
          },
        );
        const data = (await response.json().catch(() => null)) as {
          status?: string;
          message?: string;
        } | null;

        if (cancelled) return;

        if (!response.ok || !data || data.status === "error") {
          throw new Error(holdingsListErrorMessage(response, data));
        }

        const parsed = parseHoldingsListResponse(data);
        setCachedHoldings(tenant, parsed.holdings, parsed.assetClasses);
        setAllHoldings(parsed.holdings);
        setAssetClasses(parsed.assetClasses);
        setLoadError(null);
      } catch (error) {
        if (cancelled) return;
        setAllHoldings([]);
        setAssetClasses([]);
        if (controller.signal.aborted) {
          setLoadError("Holdings took too long to load. Try again.");
        } else {
          setLoadError(error instanceof Error ? error.message : "Failed to load holdings.");
        }
      } finally {
        window.clearTimeout(timeoutId);
        if (!cancelled) setLoading(false);
      }
    })();

    return () => {
      cancelled = true;
      window.clearTimeout(timeoutId);
      controller.abort();
      setLoading(false);
    };
  }, [open, canConsolidated, tenant, reloadNonce]);

  const trimmed = debouncedQuery.trim();

  React.useEffect(() => {
    setClassFilter(null);
  }, [trimmed]);

  const matchedHoldings = React.useMemo(() => {
    if (!trimmed) return [];
    return allHoldings.filter((h) => {
      if (classFilter && h.assetClass !== classFilter) return false;
      return holdingMatchesQuery(h, trimmed);
    });
  }, [allHoldings, trimmed, classFilter]);

  const collapsedHits = React.useMemo(
    () => collapseHoldingsByIsin(matchedHoldings),
    [matchedHoldings],
  );

  const classCounts = React.useMemo(
    () => (trimmed ? countCollapsedByAssetClass(allHoldings, trimmed) : new Map<string, number>()),
    [allHoldings, trimmed],
  );

  const totalUnique = React.useMemo(
    () => Array.from(classCounts.values()).reduce((sum, n) => sum + n, 0),
    [classCounts],
  );

  const filterChips = React.useMemo(() => {
    const source = assetClasses.length > 0 ? assetClasses : Array.from(classCounts.keys());
    return source.filter(
      (assetClass) => (classCounts.get(assetClass) ?? 0) > 0 || classFilter === assetClass,
    );
  }, [assetClasses, classCounts, classFilter]);

  const missingFilterFields = React.useMemo(
    () =>
      trimmed && allHoldings.length > 0
        ? unpopulatedFilterKeys(allHoldings, trimmed).map((key) => FILTER_KEY_LABELS[key] ?? key)
        : [],
    [allHoldings, trimmed],
  );

  const visibleHits = collapsedHits.slice(0, HOLDINGS_SEARCH_MAX_RESULTS);

  const resultSections = React.useMemo(() => {
    if (classFilter) return [{ heading: classFilter, hits: visibleHits }];
    const map = new Map<string, CollapsedHoldingHit[]>();
    for (const hit of visibleHits) {
      const list = map.get(hit.assetClass) ?? [];
      list.push(hit);
      map.set(hit.assetClass, list);
    }
    return Array.from(map.entries()).map(([heading, hits]) => ({ heading, hits }));
  }, [visibleHits, classFilter]);

  const handleOpenChange = (value: boolean) => {
    onOpenChange(value);
    if (!value) {
      setQuery("");
      setClassFilter(null);
      setIndividualOpen(false);
      setIndividualTarget(null);
    }
  };

  const openReport = (assetClass: string | null) => {
    const q = query.trim();
    if (!q || !canConsolidated) return;
    setChrHoldingsSearchHandoff({ q, assetClass });
    handleOpenChange(false);
    router.push(consolidatedReportHref(tenant));
  };

  const openHolding = (hit: CollapsedHoldingHit) => {
    const holding = hit.primary;
    setIndividualTarget({
      isin: holding.ticker,
      assetType: holding.assetTypeTitle || holding.assetType,
      assetClass: holding.fType || holding.assetClass,
      name: holding.name || hit.name,
    });
    setIndividualOpen(true);
  };

  const showResults = canConsolidated && !loading && !loadError && Boolean(trimmed);

  return (
    <>
      <CommandDialog
        open={open}
        onOpenChange={handleOpenChange}
        title="Holdings search"
        description="Paste an ISIN or type a name to see matching holdings"
        className="top-1/2 -translate-y-1/2 sm:max-w-xl"
      >
        <Command shouldFilter={false} className="rounded-xl">
          <div className="border-b px-3 pb-3 pt-3">
            <p className="mb-2 px-0.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
              Holdings search
            </p>
            <CommandInput
              placeholder="Search by name, ISIN, country:USA, AssetType:Digital, AND/OR..."
              value={query}
              onValueChange={setQuery}
            />
          </div>

          {showResults && filterChips.length > 0 ? (
            <div className="border-b px-3 py-2.5">
              <div className="flex gap-1.5 overflow-x-auto pb-0.5 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
                <button
                  type="button"
                  onClick={() => setClassFilter(null)}
                  className={cn(
                    "inline-flex shrink-0 items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs transition-colors",
                    classFilter === null
                      ? "border-foreground/15 bg-foreground text-background"
                      : "border-border bg-background text-muted-foreground hover:bg-muted hover:text-foreground",
                  )}
                >
                  All
                  <span className={cn("tabular-nums", classFilter === null ? "text-background/70" : "text-muted-foreground")}>
                    {totalUnique}
                  </span>
                </button>
                {filterChips.map((assetClass) => {
                  const count = classCounts.get(assetClass) ?? 0;
                  const active = classFilter === assetClass;
                  return (
                    <button
                      key={assetClass}
                      type="button"
                      onClick={() => setClassFilter(active ? null : assetClass)}
                      className={cn(
                        "inline-flex shrink-0 items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs transition-colors",
                        active
                          ? "border-foreground/15 bg-foreground text-background"
                          : "border-border bg-background text-muted-foreground hover:bg-muted hover:text-foreground",
                      )}
                    >
                      {assetClass}
                      <span className={cn("tabular-nums", active ? "text-background/70" : "text-muted-foreground")}>
                        {count}
                      </span>
                    </button>
                  );
                })}
              </div>
            </div>
          ) : null}

          <CommandList className="max-h-[min(28rem,55vh)]">
            {!canConsolidated ? (
              <div className="flex flex-col items-center gap-2 px-6 py-10 text-center">
                <FileSearch className="size-5 text-muted-foreground" />
                <p className="text-sm text-muted-foreground">
                  Consolidated Holdings is not available for this account
                </p>
              </div>
            ) : null}

            {canConsolidated && loading ? (
              <div className="flex flex-col items-center gap-3 px-6 py-12 text-center">
                <Loader2 className="size-5 animate-spin text-muted-foreground" />
                <div>
                  <p className="text-sm font-medium">Loading holdings</p>
                  <p className="mt-1 text-xs text-muted-foreground">
                    First open can take a few seconds
                  </p>
                </div>
              </div>
            ) : null}

            {canConsolidated && loadError ? (
              <div className="flex flex-col items-center gap-3 px-6 py-10 text-center">
                <FileSearch className="size-5 text-muted-foreground" />
                <p className="max-w-sm text-sm text-muted-foreground">{loadError}</p>
                <Button
                  type="button"
                  size="sm"
                  variant="outline"
                  onClick={() => {
                    clearCachedHoldings(tenant);
                    setLoadError(null);
                    setReloadNonce((n) => n + 1);
                  }}
                >
                  Retry
                </Button>
              </div>
            ) : null}

            {canConsolidated && !loading && !loadError && !trimmed ? (
              <div className="flex flex-col items-center gap-3 px-6 py-8 text-center">
                <p className="text-sm font-medium">Find a holding</p>
                <p className="max-w-md text-xs leading-relaxed text-muted-foreground">
                  Search by name/ISIN, or layer criteria using Overall Summary column names with <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px]">:</code> — country, Asset Class, AssetType, sector, industry, currency, ISIN, name. Combine them with <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px]">AND</code> / <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px]">OR</code> (AND binds first).
                </p>

                <div className="mt-1 flex flex-wrap justify-center gap-1.5 max-w-lg">
                  <span className="w-full text-[10px] font-medium text-muted-foreground/70 uppercase tracking-wider mb-0.5">Example searches</span>
                  {[
                    { label: "country:USA", q: "country:USA" },
                    { label: 'AssetType:"Digital assets"', q: 'AssetType:"Digital assets"' },
                    { label: "Asset Class:Stock", q: "Asset Class:Stock" },
                    { label: "country:USA AND Asset Class:Stock", q: "country:USA AND Asset Class:Stock" },
                    { label: "country:India OR country:China", q: "country:India OR country:China" },
                    { label: "sector:Technology AND currency:USD", q: "sector:Technology AND currency:USD" },
                  ].map((ex) => (
                    <button
                      key={ex.label}
                      type="button"
                      onClick={() => setQuery(ex.q)}
                      className="rounded-lg border border-border/80 bg-muted/40 px-2.5 py-1 text-xs font-mono text-muted-foreground transition-colors hover:border-primary/50 hover:bg-accent hover:text-foreground"
                    >
                      {ex.label}
                    </button>
                  ))}
                </div>
              </div>
            ) : null}

            {showResults && missingFilterFields.length > 0 ? (
              <div className="border-b px-3 py-2 text-xs text-muted-foreground">
                {missingFilterFields.join(" and ")} data is not available for these holdings, so that
                filter cannot match.
              </div>
            ) : null}

            {showResults ? (
              <>
                {visibleHits.length === 0 ? (
                  <CommandEmpty className="py-10">No matching holdings.</CommandEmpty>
                ) : (
                  resultSections.map((section) => (
                    <CommandGroup
                      key={section.heading}
                      heading={classFilter ? undefined : section.heading}
                      className="px-1"
                    >
                      {section.hits.map((hit) => (
                        <CommandItem
                          key={hit.key}
                          value={`holding ${hit.ticker} ${hit.name} ${hit.assetClass} ${hit.assetTypes.join(" ")}`}
                          onSelect={() => openHolding(hit)}
                          className="items-start gap-3 rounded-lg px-3 py-2.5 aria-selected:bg-muted"
                        >
                          <div className="min-w-0 flex-1">
                            <div className="truncate text-sm font-medium leading-snug">
                              {hit.name || "Untitled"}
                            </div>
                            <div className="mt-0.5 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-muted-foreground">
                              <span className="font-mono tabular-nums">{hit.ticker || "—"}</span>
                              <span className="text-border">·</span>
                              <span className="truncate">{collapsedSubtitle(hit)}</span>
                            </div>
                          </div>
                          {hit.marketValueRc !== 0 ? (
                            <span className="shrink-0 pt-0.5 text-xs font-medium tabular-nums">
                              {formatMoney(hit.marketValueRc, hit.currency || "USD")}
                            </span>
                          ) : null}
                        </CommandItem>
                      ))}
                    </CommandGroup>
                  ))
                )}

                {collapsedHits.length > 0 ? (
                  <div className="px-3 pb-2 pt-1">
                    <button
                      type="button"
                      onClick={() => openReport(classFilter)}
                      className="flex w-full items-center justify-center gap-1.5 rounded-lg border border-dashed px-3 py-2 text-xs text-muted-foreground transition-colors hover:border-border hover:bg-muted/50 hover:text-foreground"
                    >
                      <ExternalLink className="size-3.5" />
                      {collapsedHits.length > HOLDINGS_SEARCH_MAX_RESULTS
                        ? `View all ${collapsedHits.length} in Consolidated Report`
                        : "Open in Consolidated Report"}
                    </button>
                  </div>
                ) : null}
              </>
            ) : null}
          </CommandList>

          <div className="flex items-center justify-between border-t px-3 py-2 text-[10px] text-muted-foreground">
            <div className="flex gap-3">
              <span>
                <kbd className="rounded border bg-muted px-1 font-mono">↵</kbd> detail
              </span>
              <span>
                <kbd className="rounded border bg-muted px-1 font-mono">esc</kbd> close
              </span>
            </div>
            <span>⌘⇧J</span>
          </div>
        </Command>
      </CommandDialog>

      <ConsolidatedIndividualDialog
        open={individualOpen}
        onOpenChange={setIndividualOpen}
        target={individualTarget}
      />
    </>
  );
}
