"use client";

import * as React from "react";
import { format, parseISO } from "date-fns";
import {
  CartesianGrid,
  Line,
  LineChart as RechartsLineChart,
  XAxis,
  YAxis,
} from "recharts";

import { ArrowLeftRight } from "lucide-react";

import { Button } from "@/components/ui/button";
import {
  ChartContainer,
  ChartTooltip,
  type ChartConfig,
} from "@/components/ui/chart";
import { cn } from "@/lib/utils";

import { ReportLoadingPanel } from "@/app/customer/[tenant]/reports/_shared/components/report-loading-panel";
import {
  buildChartData,
  explainSnapshotShift,
  formatCompact,
  resolvePortfolioAnalyticsSeriesColor,
  tightYDomain,
  type ChartSeriesPoint,
} from "./utils";

export type PortfolioAnalyticsChartSeries = { id: string; name: string };

function formatSignedCompact(value: number) {
  const abs = formatCompact(Math.abs(value));
  if (value > 0) return `+${abs}`;
  if (value < 0) return `−${abs}`;
  return abs;
}

/** Always-visible snapshot marker (no white stroke — that punched holes in the line). */
function SnapshotDot({
  cx,
  cy,
  value,
  fill,
}: {
  cx?: number;
  cy?: number;
  value?: number | null;
  fill: string;
}) {
  if (cx == null || cy == null || value == null || !Number.isFinite(value)) {
    return null;
  }
  return <circle cx={cx} cy={cy} r={3.5} fill={fill} stroke="none" />;
}

export function PortfolioAnalyticsChartPanel({
  isLoading,
  chartData,
  chartSeriesConfig,
  visibleChartSeries,
  legendSeries,
  focusedSeries,
  onLegendClick,
  onClearFocus,
  onDayClick,
  onPeriodChangesClick,
  emptyLabel = "No OS as-of snapshots in this range",
}: {
  isLoading: boolean;
  chartData: ReturnType<typeof buildChartData>;
  chartSeriesConfig: ChartConfig;
  visibleChartSeries: PortfolioAnalyticsChartSeries[];
  legendSeries?: PortfolioAnalyticsChartSeries[];
  focusedSeries: string | null;
  onLegendClick: (seriesId: string) => void;
  onClearFocus: () => void;
  onDayClick?: (date: string, previousDate: string | null) => void;
  onPeriodChangesClick?: (endDate: string, startDate: string) => void;
  emptyLabel?: string;
}) {
  const legendItems = legendSeries ?? visibleChartSeries;
  const seriesIds = React.useMemo(
    () => visibleChartSeries.map((series) => series.id),
    [visibleChartSeries],
  );
  // Only real OS as-of points (skip empty calendar days) so the line stays continuous.
  const plotData = React.useMemo(
    () =>
      chartData.filter((point) =>
        seriesIds.some((id) => typeof point[id] === "number" && Number.isFinite(point[id] as number)),
      ),
    [chartData, seriesIds],
  );

  const yDomain = React.useMemo(() => tightYDomain(plotData, seriesIds), [plotData, seriesIds]);

  const hasPlottedValues = plotData.length > 0;

  const plotIndexByDate = React.useMemo(() => {
    const map = new Map<string, number>();
    plotData.forEach((point, index) => {
      if (typeof point.date === "string") map.set(point.date, index);
    });
    return map;
  }, [plotData]);

  const focusedLabel = focusedSeries
    ? focusedSeries === "ALL"
      ? "Total AUM"
      : (chartSeriesConfig[focusedSeries]?.label ?? focusedSeries)
    : null;

  const periodEnds = React.useMemo(() => {
    if (plotData.length < 2) return null;
    const first = plotData[0]?.date;
    const last = plotData[plotData.length - 1]?.date;
    if (typeof first !== "string" || typeof last !== "string" || first === last) return null;
    return { start: first, end: last };
  }, [plotData]);

  return (
    <section className="overflow-hidden rounded-xl border border-border/60 bg-card">
      <div className="flex items-center justify-between gap-3 border-border/50 border-b px-4 py-3 sm:px-5">
        <div className="min-w-0">
          <h2 className="font-medium text-sm tracking-tight">
            {focusedLabel ?? "Macro AUM"}
          </h2>
          <p className="text-muted-foreground text-xs">
            Absolute AUM
            {plotData.length > 0 ? ` · ${plotData.length} snapshots` : ""}
            {onDayClick ? " · Click a day for opens / closes" : ""}
          </p>
        </div>
        <div className="flex shrink-0 items-center gap-1">
          {onPeriodChangesClick && periodEnds ? (
            <Button
              variant="outline"
              size="sm"
              className="h-7 gap-1.5 px-2.5 text-xs"
              onClick={() => onPeriodChangesClick(periodEnds.end, periodEnds.start)}
            >
              <ArrowLeftRight className="size-3.5" />
              Period changes
            </Button>
          ) : null}
          {focusedSeries !== "ALL" ? (
            <Button
              variant="ghost"
              size="sm"
              className="h-7 px-2 text-muted-foreground text-xs"
              onClick={onClearFocus}
            >
              Back
            </Button>
          ) : null}
        </div>
      </div>

      <div className="min-w-0 w-full px-2 pt-3 pb-1 sm:px-4">
        <ReportLoadingPanel
          loading={isLoading}
          label="Loading…"
          minHeightClassName="min-h-[min(520px,calc(100dvh-16rem))]"
          className={cn("min-w-0 w-full", isLoading && "pointer-events-none")}
        >
          {chartData.length === 0 || !hasPlottedValues ? (
            !isLoading ? (
              <div className="flex h-[min(520px,calc(100dvh-16rem))] items-center justify-center text-muted-foreground text-sm">
                {emptyLabel}
              </div>
            ) : (
              <div className="h-[min(520px,calc(100dvh-16rem))]" aria-hidden />
            )
          ) : (
            <ChartContainer
              config={chartSeriesConfig}
              className="aspect-auto h-[min(520px,calc(100dvh-16rem))] w-full min-w-0 justify-stretch [&_.recharts-responsive-container]:!w-full"
            >
              <RechartsLineChart
                data={plotData}
                margin={{ top: 8, right: 12, left: 0, bottom: 0 }}
                style={onDayClick ? { cursor: "pointer" } : undefined}
                onClick={(state) => {
                  if (!onDayClick) return;
                  const label = state?.activeLabel != null ? String(state.activeLabel) : "";
                  if (!label || !plotIndexByDate.has(label)) return;
                  const index = plotIndexByDate.get(label) ?? -1;
                  const prev =
                    index > 0 && typeof plotData[index - 1]?.date === "string"
                      ? String(plotData[index - 1].date)
                      : null;
                  onDayClick(label, prev);
                }}
              >
                <CartesianGrid strokeDasharray="3 3" vertical={false} className="stroke-border/40" />
                <XAxis
                  dataKey="date"
                  tickLine={false}
                  axisLine={false}
                  tickMargin={8}
                  minTickGap={28}
                  tickFormatter={(value) => format(parseISO(String(value)), "d MMM")}
                  className="text-[11px] text-muted-foreground"
                />
                <YAxis
                  domain={yDomain}
                  tickLine={false}
                  axisLine={false}
                  tickMargin={8}
                  tickCount={5}
                  width={56}
                  tickFormatter={(value) => formatCompact(Number(value))}
                  className="text-[11px] text-muted-foreground tabular-nums"
                />
                <ChartTooltip
                  cursor={{ stroke: "hsl(var(--border))", strokeWidth: 1 }}
                  content={({ active, payload, label }) => {
                    if (!active || !payload?.length) return null;
                    const point = payload[0]?.payload as ChartSeriesPoint | undefined;
                    const dateKey = String(point?.date ?? label ?? "");
                    const index = plotIndexByDate.get(dateKey) ?? -1;
                    const primaryId = seriesIds[0] ?? String(payload[0]?.name ?? "");
                    const shift =
                      index >= 0
                        ? explainSnapshotShift(plotData, index, primaryId, legendItems)
                        : null;
                    const largeMove =
                      shift?.pct != null && Number.isFinite(shift.pct) && Math.abs(shift.pct) >= 5;

                    return (
                      <div className="min-w-[11rem] rounded-lg border border-border/60 bg-background px-2.5 py-2 text-xs shadow-md">
                        <div className="mb-1.5 font-medium text-foreground">
                          {dateKey ? format(parseISO(dateKey), "d MMM yyyy") : ""}
                        </div>
                        {payload.map((item) => {
                          if (item.value == null || item.value === "") return null;
                          const id = String(item.name ?? "");
                          const rowLabel = String(chartSeriesConfig[id]?.label ?? id);
                          return (
                            <div
                              key={id}
                              className="flex items-baseline justify-between gap-4 tabular-nums"
                            >
                              <span className="text-muted-foreground">{rowLabel}</span>
                              <span className="font-medium text-foreground">
                                {formatCompact(Number(item.value))}
                              </span>
                            </div>
                          );
                        })}
                        {shift?.delta != null ? (
                          <div className="mt-1.5 border-border/50 border-t pt-1.5 text-muted-foreground">
                            <div className="flex items-baseline justify-between gap-4 tabular-nums">
                              <span>{largeMove ? "Large move" : "vs prior snapshot"}</span>
                              <span
                                className={cn(
                                  "font-medium",
                                  shift.delta > 0
                                    ? "text-emerald-600 dark:text-emerald-400"
                                    : shift.delta < 0
                                      ? "text-red-600 dark:text-red-400"
                                      : "text-foreground",
                                )}
                              >
                                {formatSignedCompact(shift.delta)}
                                {shift.pct != null && Number.isFinite(shift.pct)
                                  ? ` (${shift.pct > 0 ? "+" : ""}${shift.pct.toFixed(1)}%)`
                                  : ""}
                              </span>
                            </div>
                            {shift.drivers.length > 0 ? (
                              <div className="mt-1 space-y-0.5">
                                <div className="text-[10px] uppercase tracking-wide text-muted-foreground/80">
                                  Driven by
                                </div>
                                {shift.drivers.map((driver) => (
                                  <div
                                    key={driver.id}
                                    className="flex items-baseline justify-between gap-4 tabular-nums"
                                  >
                                    <span>{driver.label}</span>
                                    <span className="font-medium text-foreground">
                                      {formatSignedCompact(driver.delta)}
                                    </span>
                                  </div>
                                ))}
                              </div>
                            ) : null}
                          </div>
                        ) : null}
                      </div>
                    );
                  }}
                />
                {visibleChartSeries.map((series) => {
                  // Prefer concrete color — CSS vars for ids like ALL can fail to paint the path.
                  const stroke =
                    chartSeriesConfig[series.id]?.color ??
                    resolvePortfolioAnalyticsSeriesColor(series, 0);
                  return (
                    <Line
                      key={series.id}
                      type="linear"
                      dataKey={series.id}
                      name={series.id}
                      stroke={stroke}
                      strokeWidth={focusedSeries ? 2.25 : 1.75}
                      dot={(props) => {
                        const raw =
                          typeof props.value === "number"
                            ? props.value
                            : typeof props.payload?.[series.id] === "number"
                              ? props.payload[series.id]
                              : null;
                        return (
                          <SnapshotDot cx={props.cx} cy={props.cy} value={raw} fill={stroke} />
                        );
                      }}
                      activeDot={{ r: 5, fill: stroke, stroke: "none" }}
                      connectNulls
                      isAnimationActive={false}
                    />
                  );
                })}
              </RechartsLineChart>
            </ChartContainer>
          )}
        </ReportLoadingPanel>
      </div>

      <div className="flex flex-wrap items-center gap-1 border-border/50 border-t px-3 py-2.5 sm:px-4">
        {legendItems.map((series) => {
          const color =
            chartSeriesConfig[series.id]?.color ?? resolvePortfolioAnalyticsSeriesColor(series, 0);
          const isolated = focusedSeries === series.id;
          const inactive = focusedSeries !== null && !isolated;
          const label = series.id === "ALL" ? "Total" : series.name;
          return (
            <button
              key={series.id}
              type="button"
              onClick={() => onLegendClick(series.id)}
              className={cn(
                "inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-xs transition-colors",
                isolated
                  ? "bg-muted font-medium text-foreground"
                  : "text-muted-foreground hover:bg-muted/50 hover:text-foreground",
                inactive && "opacity-45",
              )}
            >
              <span className="size-1.5 shrink-0 rounded-full" style={{ backgroundColor: color }} />
              {label}
            </button>
          );
        })}
      </div>
    </section>
  );
}
