"use client";

import * as React from "react";
import { Search, X } from "lucide-react";

import {
  SortableTh,
  useTableSort,
} from "@/app/customer/[tenant]/reports/_shared/components/sortable-table";
import { ReportLoadingPanel } from "@/app/customer/[tenant]/reports/_shared/components/report-loading-panel";
import { ErrorBanner } from "@/components/shared/error-banner";
import { Input } from "@/components/ui/input";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { EM_DASH, formatAmount, formatPercentFixed, formatPlainAmount } from "@/lib/format/numbers";
import { cn } from "@/lib/utils";

import type {
  StructuredProductBreakdown,
  StructuredProductBreakdownGroup,
  StructuredProductCurrencyTotalBreakdown,
} from "../../_lib/structured-product-types";

type BreakdownMode = "bank" | "user";

const MODE_COPY = {
  bank: {
    group: "Bank",
    groups: "Banks",
    related: "Owners",
    unknown: "Unknown bank",
    search: "Search banks",
    largest: "Largest bank",
  },
  user: {
    group: "Owner",
    groups: "Owners",
    related: "Banks",
    unknown: "Unassigned",
    search: "Search name, short name or client no.",
    largest: "Largest owner",
  },
} as const;

/**
 * Reference categorical slots 1–5 (validated light + dark, adjacent CVD ΔE ≥ 8.4). Groups past
 * the fifth fold into a neutral "Other"; identity is never colour-alone — the legend and the
 * table both name every group.
 */
const SERIES_CLASSES = [
  "bg-[#2a78d6] dark:bg-[#3987e5]",
  "bg-[#eb6834] dark:bg-[#d95926]",
  "bg-[#1baf7a] dark:bg-[#199e70]",
  "bg-[#eda100] dark:bg-[#c98500]",
  "bg-[#e87ba4] dark:bg-[#d55181]",
] as const;
const OTHER_CLASS = "bg-slate-300 dark:bg-slate-600";

const AMOUNT_2DP = { minimumFractionDigits: 2, maximumFractionDigits: 2 } as const;
const AMOUNT_0DP = { minimumFractionDigits: 0, maximumFractionDigits: 0 } as const;

const SORT_ACCESSORS = {
  name: (row: StructuredProductBreakdownGroup) => row.name || row.shortName,
  structures: (row: StructuredProductBreakdownGroup) => row.structures,
  related: (row: StructuredProductBreakdownGroup) => row.related,
  amount: (row: StructuredProductBreakdownGroup) => row.convertedTotal,
};

const HEADER_CLASS = "bg-muted text-muted-foreground border-b border-border";

function displayName(row: StructuredProductBreakdownGroup, mode: BreakdownMode): string {
  return row.name || row.shortName || MODE_COPY[mode].unknown;
}

function matchesSearch(row: StructuredProductBreakdownGroup, needle: string): boolean {
  if (!needle) return true;
  return [row.name, row.shortName, row.clientNumber].some((value) =>
    value.toLowerCase().includes(needle),
  );
}

function shareOf(value: number, total: number): number {
  return total > 0 ? (value / total) * 100 : 0;
}

function KpiCell({
  label,
  value,
  children,
  className,
}: {
  label: string;
  value: React.ReactNode;
  children?: React.ReactNode;
  className?: string;
}) {
  return (
    <div className={cn("min-w-0 px-4 py-3 sm:px-5", className)}>
      <div className="font-medium text-muted-foreground text-xs">{label}</div>
      <div className="mt-1 truncate font-semibold text-foreground text-xl tabular-nums tracking-tight">
        {value}
      </div>
      {children ? <div className="mt-1.5 min-w-0">{children}</div> : null}
    </div>
  );
}

function CurrencyChips({
  notional,
  className,
}: {
  notional: StructuredProductCurrencyTotalBreakdown[];
  className?: string;
}) {
  if (notional.length === 0) return <span className="text-muted-foreground">{EM_DASH}</span>;
  return (
    <div className={cn("flex flex-wrap gap-1", className)}>
      {notional.map((entry) => (
        <span
          key={entry.currency}
          className="inline-flex items-baseline gap-1 whitespace-nowrap rounded-md bg-muted px-1.5 py-0.5 text-[11px] tabular-nums"
        >
          <span className="font-semibold text-muted-foreground">{entry.currency}</span>
          <span className="text-foreground">{formatAmount(entry.total, AMOUNT_0DP)}</span>
        </span>
      ))}
    </div>
  );
}

type Slice = { key: string; label: string; value: number; pct: number; colorClass: string; count: string };

/** Part-to-whole: one horizontal stacked bar, top five named + "Other", legend always shown. */
function DistributionBar({ slices, title }: { slices: Slice[]; title: string }) {
  if (slices.length === 0) return null;
  return (
    <div className="rounded-xl border border-border/80 bg-card px-4 py-3 shadow-xs sm:px-5">
      <div className="mb-2.5 font-medium text-muted-foreground text-xs">{title}</div>
      <div className="flex h-2.5 w-full gap-[2px]" role="img" aria-label={title}>
        {slices.map((slice) => (
          <Tooltip key={slice.key}>
            <TooltipTrigger asChild>
              <div
                className={cn(
                  "h-full min-w-[3px] cursor-default first:rounded-l-[4px] last:rounded-r-[4px] hover:opacity-80",
                  slice.colorClass,
                )}
                style={{ flexGrow: slice.value, flexBasis: 0 }}
              />
            </TooltipTrigger>
            <TooltipContent side="top" className="text-xs">
              <div className="font-semibold">{slice.label}</div>
              <div className="tabular-nums">
                {formatPercentFixed(slice.pct, 1)} · {formatAmount(slice.value, AMOUNT_0DP)} · {slice.count}
              </div>
            </TooltipContent>
          </Tooltip>
        ))}
      </div>
      <ul className="mt-2.5 flex flex-wrap gap-x-4 gap-y-1">
        {slices.map((slice) => (
          <li key={slice.key} className="flex min-w-0 items-center gap-1.5 text-xs">
            <span className={cn("size-2 shrink-0 rounded-[2px]", slice.colorClass)} aria-hidden />
            <span className="max-w-[14rem] truncate text-foreground">{slice.label}</span>
            <span className="text-muted-foreground tabular-nums">{formatPercentFixed(slice.pct, 1)}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}

function ShareCell({ pct, colorClass }: { pct: number; colorClass: string }) {
  return (
    <div className="flex items-center gap-2.5">
      <div className="h-1.5 min-w-16 flex-1 overflow-hidden rounded-full bg-muted" aria-hidden>
        <div
          className={cn("h-full rounded-full", colorClass)}
          style={{ width: `${Math.min(100, Math.max(pct, pct > 0 ? 1.5 : 0))}%` }}
        />
      </div>
      <span className="w-12 shrink-0 text-right text-muted-foreground tabular-nums">
        {formatPercentFixed(pct, 1)}
      </span>
    </div>
  );
}

function GroupLabel({
  row,
  mode,
  colorClass,
}: {
  row: StructuredProductBreakdownGroup;
  mode: BreakdownMode;
  colorClass: string;
}) {
  const isUnknown = !row.name && !row.shortName;
  const subtitle =
    mode === "user" ? [row.name ? row.shortName : "", row.clientNumber].filter(Boolean).join(" · ") : "";
  return (
    <div className="flex min-w-0 items-center gap-2.5">
      <span className={cn("size-2.5 shrink-0 rounded-[3px]", colorClass)} aria-hidden />
      <div className="min-w-0">
        <div className={cn("truncate font-medium", isUnknown && "text-muted-foreground italic")}>
          {displayName(row, mode)}
        </div>
        {subtitle ? <div className="truncate text-muted-foreground text-xs">{subtitle}</div> : null}
      </div>
    </div>
  );
}

/**
 * By Bank / By User: notional grouped over every structure matching the current filters
 * (server-side, one count per structure — not the per-underlying rows on the loaded page).
 */
export function StructuredProductOwnershipBreakdown({
  breakdown,
  mode,
  isLoading,
  errorMessage,
}: {
  breakdown: StructuredProductBreakdown | null;
  mode: BreakdownMode;
  isLoading: boolean;
  errorMessage: string | null;
}) {
  const copy = MODE_COPY[mode];
  const [search, setSearch] = React.useState("");
  const groups = React.useMemo(
    () => (breakdown ? (mode === "bank" ? breakdown.byBank : breakdown.byUser) : []),
    [breakdown, mode],
  );
  const currency = breakdown?.reportingCurrency ?? "";
  const grandTotal = breakdown?.convertedTotal ?? 0;
  const relatedLabel = copy.related.toLowerCase();

  // Colour follows the group across search/sort: assigned once from the server's notional order.
  const seriesByKey = React.useMemo(() => {
    const map = new Map<string, { rank: number; colorClass: string }>();
    groups.forEach((row, index) =>
      map.set(row.key, { rank: index + 1, colorClass: SERIES_CLASSES[index] ?? OTHER_CLASS }),
    );
    return map;
  }, [groups]);

  const slices = React.useMemo<Slice[]>(() => {
    const named = groups.slice(0, SERIES_CLASSES.length).map((row, index): Slice => ({
      key: row.key,
      label: displayName(row, mode),
      value: row.convertedTotal,
      pct: shareOf(row.convertedTotal, grandTotal),
      colorClass: SERIES_CLASSES[index],
      count: `${formatPlainAmount(row.structures)} structures`,
    }));
    const rest = groups.slice(SERIES_CLASSES.length);
    if (rest.length > 0) {
      const value = rest.reduce((sum, row) => sum + row.convertedTotal, 0);
      named.push({
        key: "__other__",
        label: `Other ${formatPlainAmount(rest.length)} ${copy.groups.toLowerCase()}`,
        value,
        pct: shareOf(value, grandTotal),
        colorClass: OTHER_CLASS,
        count: `${formatPlainAmount(rest.reduce((sum, row) => sum + row.structures, 0))} structures`,
      });
    }
    return named.filter((slice) => slice.value > 0);
  }, [groups, grandTotal, mode, copy.groups]);

  const needle = search.trim().toLowerCase();
  const filtered = React.useMemo(
    () => groups.filter((row) => matchesSearch(row, needle)),
    [groups, needle],
  );
  const filteredStructures = filtered.reduce((sum, row) => sum + row.structures, 0);
  const { rows: sortedRows, toggle, directionFor } = useTableSort(filtered, SORT_ACCESSORS);
  const top = groups[0];

  return (
    <ReportLoadingPanel
      loading={isLoading}
      className="flex min-h-0 flex-1 flex-col"
      minHeightClassName="min-h-[320px]"
    >
      <div className="flex min-h-0 flex-1 flex-col gap-3">
        <ErrorBanner message={errorMessage} />

        {breakdown ? (
          // gap-px over a border-coloured backdrop draws hairline dividers at every breakpoint.
          <div className="grid grid-cols-2 gap-px overflow-hidden rounded-xl border border-border/80 bg-border/70 shadow-xs lg:grid-cols-4 [&>*]:bg-card">
            <KpiCell label="Structures" value={formatPlainAmount(breakdown.structureCount)} />
            <KpiCell label={`Total notional${currency ? ` · ${currency}` : ""}`} value={formatAmount(grandTotal, AMOUNT_2DP)}>
              {breakdown.notional.length > 1 ? (
                <Tooltip>
                  <TooltipTrigger asChild>
                    <span
                      tabIndex={0}
                      className="cursor-help text-muted-foreground text-xs underline decoration-dotted underline-offset-2"
                    >
                      Across {breakdown.notional.length} currencies
                    </span>
                  </TooltipTrigger>
                  <TooltipContent side="bottom" align="start" className="text-xs">
                    <table className="tabular-nums">
                      <tbody>
                        {breakdown.notional.map((entry) => (
                          <tr key={entry.currency}>
                            <td className="pr-3 font-semibold">{entry.currency}</td>
                            <td className="pr-3 text-right">{formatAmount(entry.total, AMOUNT_0DP)}</td>
                            <td className="text-right opacity-80">
                              ≈ {formatAmount(entry.convertedTotal ?? 0, AMOUNT_0DP)} {currency}
                            </td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </TooltipContent>
                </Tooltip>
              ) : null}
            </KpiCell>
            <KpiCell label={copy.groups} value={formatPlainAmount(groups.length)} />
            <KpiCell label={copy.largest} value={top ? displayName(top, mode) : EM_DASH}>
              {top ? (
                <div className="truncate text-muted-foreground text-xs tabular-nums">
                  {formatPercentFixed(shareOf(top.convertedTotal, grandTotal), 1)} of notional ·{" "}
                  {formatPlainAmount(top.structures)} structures
                </div>
              ) : null}
            </KpiCell>
          </div>
        ) : null}

        {breakdown ? (
          <DistributionBar
            slices={slices}
            title={`Notional by ${copy.group.toLowerCase()}${currency ? ` (${currency})` : ""}`}
          />
        ) : null}

        {breakdown && groups.length === 0 ? (
          <p className="rounded-xl border border-dashed px-4 py-10 text-center text-muted-foreground text-sm">
            No structures match the current filters.
          </p>
        ) : null}

        {breakdown && groups.length > 0 ? (
          <div className="flex min-h-[280px] flex-1 flex-col overflow-hidden rounded-xl border border-border/80 bg-card shadow-xs">
            <div className="flex flex-wrap items-center justify-between gap-2 border-border/70 border-b px-3 py-2.5">
              <div className="relative w-full sm:w-80">
                <Search className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" />
                <Input
                  value={search}
                  onChange={(event) => setSearch(event.target.value)}
                  placeholder={copy.search}
                  className="h-8 pr-8 pl-8 text-sm"
                  aria-label={copy.search}
                />
                {search ? (
                  <button
                    type="button"
                    onClick={() => setSearch("")}
                    className="absolute top-1/2 right-2 -translate-y-1/2 rounded p-0.5 text-muted-foreground hover:text-foreground"
                    aria-label="Clear search"
                  >
                    <X className="size-3.5" />
                  </button>
                ) : null}
              </div>
              <p className="text-muted-foreground text-xs tabular-nums">
                {needle
                  ? `${formatPlainAmount(filtered.length)} of ${formatPlainAmount(groups.length)} ${copy.groups.toLowerCase()} · ${formatPlainAmount(filteredStructures)} structures`
                  : `${formatPlainAmount(groups.length)} ${copy.groups.toLowerCase()} · all ${formatPlainAmount(breakdown.structureCount)} filtered structures`}
              </p>
            </div>

            <div className="min-h-0 flex-1 overflow-auto">
              <table className="w-full min-w-[820px] border-collapse text-sm">
                <thead>
                  <tr>
                    <SortableTh
                      label="#"
                      align="right"
                      className={cn(HEADER_CLASS, "w-10 normal-case")}
                    />
                    <SortableTh
                      label={copy.group}
                      direction={directionFor("name")}
                      onSort={() => toggle("name")}
                      className={HEADER_CLASS}
                    />
                    <SortableTh
                      label="Structures"
                      align="right"
                      direction={directionFor("structures")}
                      onSort={() => toggle("structures")}
                      className={HEADER_CLASS}
                    />
                    <SortableTh
                      label={copy.related}
                      align="right"
                      direction={directionFor("related")}
                      onSort={() => toggle("related")}
                      className={HEADER_CLASS}
                    />
                    <SortableTh
                      label={`Notional${currency ? ` (${currency})` : ""}`}
                      align="right"
                      direction={directionFor("amount")}
                      onSort={() => toggle("amount")}
                      className={HEADER_CLASS}
                    />
                    <SortableTh label="Share" className={cn(HEADER_CLASS, "w-[18%] min-w-40")} />
                    <SortableTh label="Currency mix" className={HEADER_CLASS} />
                  </tr>
                </thead>
                <tbody className="divide-y divide-border/60">
                  {sortedRows.map((row) => {
                    const series = seriesByKey.get(row.key);
                    const colorClass = series?.colorClass ?? OTHER_CLASS;
                    return (
                      <tr key={row.key} className="transition-colors hover:bg-muted/40">
                        <td className="px-3 py-3 text-right text-muted-foreground text-xs tabular-nums">
                          {series?.rank ?? ""}
                        </td>
                        <td className="max-w-[300px] px-3 py-3">
                          <GroupLabel row={row} mode={mode} colorClass={colorClass} />
                        </td>
                        <td className="px-3 py-3 text-right font-medium tabular-nums">
                          {formatPlainAmount(row.structures)}
                        </td>
                        <td
                          className="px-3 py-3 text-right text-muted-foreground tabular-nums"
                          title={row.related > 0 ? `${row.related} ${relatedLabel}` : undefined}
                        >
                          {row.related > 0 ? formatPlainAmount(row.related) : EM_DASH}
                        </td>
                        <td className="whitespace-nowrap px-3 py-3 text-right font-semibold tabular-nums">
                          {formatAmount(row.convertedTotal, AMOUNT_2DP)}
                        </td>
                        <td className="px-3 py-3">
                          <ShareCell pct={shareOf(row.convertedTotal, grandTotal)} colorClass={colorClass} />
                        </td>
                        <td className="px-3 py-3">
                          <CurrencyChips notional={row.notional} className="max-w-[380px]" />
                        </td>
                      </tr>
                    );
                  })}
                  {sortedRows.length === 0 ? (
                    <tr>
                      <td colSpan={7} className="px-3 py-10 text-center text-muted-foreground text-sm">
                        No {copy.groups.toLowerCase()} match “{search.trim()}”.
                      </td>
                    </tr>
                  ) : null}
                </tbody>
              </table>
            </div>
          </div>
        ) : null}
      </div>
    </ReportLoadingPanel>
  );
}
