"use client";

import * as React from "react";
import { Cell, Pie, PieChart } from "recharts";
import { Maximize2, Minimize2, X } from "lucide-react";

import { Button } from "@/components/ui/button";
import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart";
import { Skeleton } from "@/components/ui/skeleton";
import { buildPositivePieSlices, pieGeometryForSliceCount } from "@/lib/chart/positive-pie-slices";
import { cn } from "@/lib/utils";

import { DashboardWidgetBody } from "@/app/customer/_components/dashboard-charts/dashboard-widget-body";
import { WidgetChartLegend } from "@/app/customer/_components/dashboard-charts/widget-chart-legend";
import { DashboardWidgetShell } from "@/app/customer/_components/dashboard-widget-shell";
import type { WidgetLayoutTier } from "@/app/customer/_lib/use-widget-container-size";

import { formatChartSliceLabel, formatCompactMarketValue, formatPct, resolveAssetClassColor } from "./holdings-widget-utils";

export type HoldingsDonutSlice = {
  name: string;
  mv: number;
  mv_fmt: string;
  pct: number;
  color?: string;
  drillable?: boolean;
};

type ChartSlice = HoldingsDonutSlice & { fill: string; chartLabel: string | null };

const chartConfig = {
  mv: { label: "Market value" },
} satisfies ChartConfig;

const RADIAN = Math.PI / 180;

function polarToCartesian(cx: number, cy: number, radius: number, angle: number) {
  return {
    x: cx + Math.cos(-RADIAN * angle) * radius,
    y: cy + Math.sin(-RADIAN * angle) * radius,
  };
}

function ArcValueLabel(props: {
  cx?: number;
  cy?: number;
  midAngle?: number;
  innerRadius?: number;
  outerRadius?: number;
  payload?: ChartSlice;
}) {
  const label = props.payload?.chartLabel;
  if (!label) return null;

  const { cx = 0, cy = 0, midAngle = 0, innerRadius = 0, outerRadius = 0 } = props;
  const radius = (innerRadius + outerRadius) / 2;
  const { x, y } = polarToCartesian(cx, cy, radius, midAngle);

  return (
    <text
      x={x}
      y={y}
      textAnchor="middle"
      dominantBaseline="central"
      className="fill-white text-[9px] font-semibold tabular-nums"
      style={{ pointerEvents: "none" }}
    >
      {label}
    </text>
  );
}

const renderArcLabel = (props: React.ComponentProps<typeof ArcValueLabel>) => <ArcValueLabel {...props} />;

const ResponsiveHoldingsPie = React.memo(function ResponsiveHoldingsPie({
  chartData,
  currency,
  drillHint,
  onDrill,
  maxSize = 280,
  minSize = 160,
}: {
  chartData: ChartSlice[];
  currency: string;
  drillHint?: string | null;
  onDrill?: (slice: HoldingsDonutSlice) => void;
  maxSize?: number;
  minSize?: number;
}) {
  const hostRef = React.useRef<HTMLDivElement>(null);
  const [size, setSize] = React.useState(240);
  const onDrillRef = React.useRef(onDrill);
  onDrillRef.current = onDrill;

  React.useEffect(() => {
    const el = hostRef.current;
    if (!el) return;

    let frame = 0;
    const update = () => {
      cancelAnimationFrame(frame);
      frame = requestAnimationFrame(() => {
        const box = el.getBoundingClientRect();
        const next = Math.max(minSize, Math.floor(Math.min(box.width, box.height || box.width, maxSize)));
        setSize((prev) => (prev === next ? prev : next));
      });
    };

    update();
    const ro = new ResizeObserver(update);
    ro.observe(el);
    return () => {
      cancelAnimationFrame(frame);
      ro.disconnect();
    };
  }, [maxSize, minSize]);

  const sliceCount = chartData.length;
  const outerRadius = Math.max(58, Math.floor(size * 0.44));
  const innerRadius = Math.max(38, Math.floor(outerRadius * 0.72));
  const thickness = outerRadius - innerRadius;
  const { paddingAngle, cornerRadius: baseCorner } = pieGeometryForSliceCount(sliceCount);
  const cornerRadius = Math.min(baseCorner, Math.max(4, Math.floor(thickness * 0.45)));

  const handleClick = React.useCallback(
    (_: unknown, index: number) => {
      const slice = chartData[index];
      if (slice?.drillable && onDrillRef.current) onDrillRef.current(slice);
    },
    [chartData],
  );

  return (
    <div
      ref={hostRef}
      className="relative flex aspect-square w-full max-w-[280px] items-center justify-center"
      style={{ minHeight: minSize }}
    >
      <ChartContainer
        config={chartConfig}
        className="aspect-square shrink-0 [&_.recharts-pie-sector]:outline-none"
        style={{ height: size, width: size }}
      >
        <PieChart>
          <ChartTooltip
            cursor={false}
            isAnimationActive={false}
            content={
              <ChartTooltipContent
                hideLabel
                formatter={(_value, _name, item) => {
                  const payload = item.payload as ChartSlice;
                  return (
                    <div className="space-y-0.5">
                      <p className="font-semibold">{payload.name}</p>
                      <p className="tabular-nums">
                        {currency} {payload.mv_fmt}
                      </p>
                      <p className="text-muted-foreground text-xs">{payload.pct.toFixed(2)}%</p>
                      {payload.drillable && drillHint ? (
                        <p className="pt-0.5 text-[10px] text-muted-foreground">{drillHint}</p>
                      ) : null}
                    </div>
                  );
                }}
              />
            }
          />
          <Pie
            data={chartData}
            dataKey="mv"
            nameKey="name"
            innerRadius={innerRadius}
            outerRadius={outerRadius}
            paddingAngle={paddingAngle}
            cornerRadius={cornerRadius}
            stroke="hsl(var(--card))"
            strokeWidth={2}
            onClick={handleClick}
            className={cn(onDrill && "cursor-pointer")}
            isAnimationActive={false}
            labelLine={false}
            label={renderArcLabel}
          >
            {chartData.map((slice) => (
              <Cell key={slice.name} fill={slice.fill} className="outline-none" />
            ))}
          </Pie>
        </PieChart>
      </ChartContainer>
    </div>
  );
});

type HoldingsDonutWidgetProps = {
  title: string;
  subtitle?: string;
  currency: string;
  totalMv: number;
  totalMvFmt: string;
  slices: HoldingsDonutSlice[];
  loading?: boolean;
  refreshing?: boolean;
  /** True while local drill state has changed but fetched data has not caught up yet. */
  drillPending?: boolean;
  error?: string | null;
  settingsHref?: string;
  onSettingsClick?: () => void;
  reportHref?: string;
  drillHint?: string | null;
  canDrillUp?: boolean;
  onRefresh?: () => void;
  onDrill?: (slice: HoldingsDonutSlice) => void;
  onDrillUp?: () => void;
  className?: string;
};

function HoldingsDonutLegend({
  chartData,
  onDrill,
  maxItems,
  className,
}: {
  chartData: ChartSlice[];
  onDrill?: (slice: HoldingsDonutSlice) => void;
  maxItems?: number;
  className?: string;
}) {
  if (maxItems != null) {
    const legendItems = chartData.map((slice) => ({
      name: slice.name,
      fill: slice.fill,
      pct: slice.pct,
    }));
    return <WidgetChartLegend items={legendItems} maxItems={maxItems} className={className} />;
  }

  return (
    <ul className={cn("max-h-full min-h-0 space-y-1.5 overflow-y-auto pr-1 text-xs", className)}>
      {chartData.map((slice) => (
        <li key={slice.name}>
          <button
            type="button"
            disabled={!slice.drillable || !onDrill}
            onClick={() => {
              if (slice.drillable && onDrill) onDrill(slice);
            }}
            className={cn(
              "flex w-full items-center gap-2.5 rounded-lg border border-border/40 bg-card/60 px-2.5 py-1.5 text-left transition-colors",
              slice.drillable && onDrill ? "cursor-pointer hover:bg-muted/40" : "cursor-default disabled:opacity-100",
            )}
          >
            <span className="size-2.5 shrink-0 rounded-full" style={{ backgroundColor: slice.fill }} />
            <span className="min-w-0 flex-1 truncate font-medium text-foreground/85" title={slice.name}>
              {slice.name}
            </span>
            <span className="shrink-0 text-[11px] font-semibold tabular-nums text-foreground">
              {formatPct(slice.pct)}
            </span>
          </button>
        </li>
      ))}
    </ul>
  );
}

function HoldingsDonutContent({
  tier,
  subtitle,
  currency,
  totalMv,
  totalMvFmt,
  chartData,
  mounted,
  drillHint,
  canDrillUp,
  onDrill,
  onDrillUp,
}: {
  tier: WidgetLayoutTier;
  subtitle: string;
  currency: string;
  totalMv: number;
  totalMvFmt: string;
  chartData: ChartSlice[];
  mounted: boolean;
  drillHint?: string | null;
  canDrillUp?: boolean;
  onDrill?: (slice: HoldingsDonutSlice) => void;
  onDrillUp?: () => void;
}) {
  const summaryBlock = (
    <div className="rounded-lg border border-border/50 bg-muted/30 px-3.5 py-2.5">
      <div className="flex items-center justify-between gap-2">
        <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
          {canDrillUp && onDrillUp ? (
            <button
              type="button"
              onClick={onDrillUp}
              className="relative z-20 inline-flex items-center gap-1 rounded-md text-muted-foreground transition-colors hover:text-foreground"
            >
              <X className="size-3" />
              Back
            </button>
          ) : (
            subtitle
          )}
        </p>
        <span className="text-[10px] tabular-nums text-muted-foreground">{currency}</span>
      </div>
      <p
        className={cn(
          "mt-0.5 truncate font-bold tabular-nums text-primary",
          tier === "compact" ? "text-lg" : "text-xl",
        )}
      >
        {formatCompactMarketValue(totalMv)}
      </p>
      {tier !== "compact" ? (
        <p className="mt-0.5 text-[10px] tabular-nums text-muted-foreground">
          {currency} {totalMvFmt}
        </p>
      ) : null}
    </div>
  );

  const pie = mounted ? (
    <ResponsiveHoldingsPie
      chartData={chartData}
      currency={currency}
      drillHint={drillHint}
      onDrill={onDrill}
      maxSize={tier === "compact" ? 140 : 280}
      minSize={tier === "compact" ? 100 : 160}
    />
  ) : (
    <div
      className="aspect-square rounded-full border border-dashed border-border/50 bg-muted/20"
      style={{ width: tier === "compact" ? 120 : 220, height: tier === "compact" ? 120 : 220 }}
    />
  );

  if (tier === "compact") {
    return (
      <div className="grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_minmax(0,0.95fr)] items-center gap-3 p-4">
        <div className="flex min-h-0 flex-col gap-3">
          {summaryBlock}
          <HoldingsDonutLegend chartData={chartData} onDrill={onDrill} maxItems={3} />
        </div>
        <div className="flex items-center justify-center">{pie}</div>
      </div>
    );
  }

  return (
    <div className="grid min-h-0 flex-1 gap-4 p-4 sm:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)] sm:items-center">
      <div className="flex min-h-0 flex-col gap-3 self-stretch">
        {summaryBlock}
        <HoldingsDonutLegend chartData={chartData} onDrill={onDrill} />
      </div>
      <div className="flex h-full min-h-[200px] w-full items-center justify-center self-center">{pie}</div>
    </div>
  );
}

export function HoldingsDonutWidget({
  title,
  subtitle = "TOTAL MARKET VALUE",
  currency,
  totalMv,
  totalMvFmt,
  slices,
  loading,
  refreshing,
  drillPending,
  error,
  settingsHref,
  onSettingsClick,
  reportHref,
  drillHint,
  canDrillUp,
  onRefresh,
  onDrill,
  onDrillUp,
  className,
}: HoldingsDonutWidgetProps) {
  const [expanded, setExpanded] = React.useState(false);
  const [mounted, setMounted] = React.useState(false);

  React.useEffect(() => {
    setMounted(true);
  }, []);

  const chartData = React.useMemo<ChartSlice[]>(() => {
    // Drop Cash/Leverage (and any other) negatives — pies cannot render them.
    const positive = buildPositivePieSlices(
      slices.map((slice, index) => ({
        name: slice.name,
        value: slice.mv,
        fill: resolveAssetClassColor(slice.name, index),
      })),
    );

    return positive.map((slice) => {
      const source = slices.find((row) => row.name === slice.name);
      return {
        name: slice.name,
        mv: slice.value,
        mv_fmt: source?.mv_fmt ?? formatCompactMarketValue(slice.value),
        pct: slice.pct,
        drillable: drillPending ? false : source?.drillable,
        fill: slice.fill ?? resolveAssetClassColor(slice.name),
        chartLabel: slice.pct >= 14 ? formatChartSliceLabel(slice.value) : null,
      };
    });
  }, [slices, drillPending]);

  const hasData = chartData.length > 0;
  // drillPending covers the gap before the fetch effect flips refreshing/loading.
  const showRefreshOverlay = Boolean(refreshing || drillPending || (loading && hasData));

  const shell = (
    <DashboardWidgetShell
      title={title}
      subtitle={subtitle}
      settingsHref={settingsHref}
      onSettingsClick={onSettingsClick}
      reportHref={reportHref}
      loading={Boolean(loading) && !hasData}
      refreshing={showRefreshOverlay}
      error={error}
      onRefresh={onRefresh}
      headerActions={
        <Button
          type="button"
          variant="outline"
          size="icon"
          className="size-8"
          onClick={() => setExpanded((value) => !value)}
          title={expanded ? "Collapse" : "Expand"}
        >
          {expanded ? <Minimize2 className="size-4" /> : <Maximize2 className="size-4" />}
          <span className="sr-only">{expanded ? "Collapse" : "Expand"}</span>
        </Button>
      }
      className={cn(expanded && "fixed inset-4 z-50 max-h-none shadow-2xl", className)}
      loadingContent={
        <div className="grid flex-1 gap-4 p-4 sm:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]">
          <div className="space-y-3">
            <Skeleton className="h-16 w-full rounded-lg" />
            <div className="space-y-2">
              {Array.from({ length: 5 }).map((_, i) => (
                <Skeleton key={i} className="h-8 w-full rounded-lg" />
              ))}
            </div>
          </div>
          <div className="flex items-center justify-center">
            <Skeleton className="aspect-square h-52 w-52 rounded-full" />
          </div>
        </div>
      }
      emptyContent={
        <div className="flex min-h-[200px] flex-1 items-center justify-center p-6 text-center text-sm text-muted-foreground">
          No holdings data available.
        </div>
      }
    >
      {hasData ? (
        <DashboardWidgetBody className="min-h-0 flex-1">
          {({ tier }) => (
            <HoldingsDonutContent
              tier={tier}
              subtitle={subtitle}
              currency={currency}
              totalMv={totalMv}
              totalMvFmt={totalMvFmt}
              chartData={chartData}
              mounted={mounted}
              drillHint={drillHint}
              canDrillUp={canDrillUp}
              onDrill={onDrill}
              onDrillUp={onDrillUp}
            />
          )}
        </DashboardWidgetBody>
      ) : null}
    </DashboardWidgetShell>
  );

  if (expanded) {
    return (
      <>
        <button
          type="button"
          aria-label="Close expanded widget"
          className="fixed inset-0 z-40 bg-black/40"
          onClick={() => setExpanded(false)}
        />
        {shell}
      </>
    );
  }

  return shell;
}
