/**
 * BanTrendChart — area chart showing the number of bans over time.
 *
 * Calls `useBanTrend` internally and handles loading, error, and empty states.
 */

import { useMemo } from "react";
import {
  Area,
  AreaChart,
  CartesianGrid,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from "recharts";
import type { TooltipContentProps } from "recharts/types/component/Tooltip";
import {
  tokens,
  makeStyles,
} from "@fluentui/react-components";
import {
  CHART_AXIS_TEXT_TOKEN,
  CHART_GRID_LINE_TOKEN,
  CHART_PALETTE,
  resolveFluentToken,
} from "../utils/chartTheme";
import { ChartStateWrapper } from "./ChartStateWrapper";
import { useBanTrend } from "../hooks/useBanTrend";
import type { BanOriginFilter, TimeRange } from "../types/ban";

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

/** Minimum chart height in pixels. */
const MIN_CHART_HEIGHT = 220;

/** Number of X-axis ticks to skip per time-range preset. */
const TICK_INTERVAL: Record<TimeRange, number> = {
  "24h": 3,   // show every 4th tick → ~6 visible
  "7d": 3,    // show every 4th tick → 7 visible
  "30d": 4,   // show every 5th tick → 6 visible
  "365d": 7,  // show every 8th tick → ~7 visible
};

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

/** Props for {@link BanTrendChart}. */
interface BanTrendChartProps {
  /** Time-range preset controlling the query window. */
  timeRange: TimeRange;
  /** Origin filter controlling which bans are included. */
  origin: BanOriginFilter;
  /** Data source used for the chart. */
  source?: "fail2ban" | "archive";
}

/** Internal chart data point shape. */
interface TrendEntry {
  /** ISO 8601 UTC timestamp — used by the tooltip. */
  timestamp: string;
  /** Formatted string shown on the X-axis tick. */
  label: string;
  /** Number of bans in this bucket. */
  count: number;
}

// ---------------------------------------------------------------------------
// Styles
// ---------------------------------------------------------------------------

const useStyles = makeStyles({
  chartWrapper: {
    width: "100%",
  },
});

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

/**
 * Format an ISO 8601 timestamp as a compact human-readable X-axis label.
 *
 * @param timestamp - ISO 8601 UTC string.
 * @param range     - Active time-range preset.
 * @returns Label string, e.g. `"Mon 14:00"`, `"Mar 5"`.
 */
function formatXLabel(timestamp: string, range: TimeRange): string {
  const d = new Date(timestamp);
  if (range === "24h") {
    const day = d.toLocaleDateString("en-US", { weekday: "short" });
    const time = d.toLocaleTimeString("en-US", {
      hour: "2-digit",
      minute: "2-digit",
      hour12: false,
    });
    return `${day} ${time}`;
  }
  if (range === "7d") {
    const date = d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
    const time = d.toLocaleTimeString("en-US", {
      hour: "2-digit",
      minute: "2-digit",
      hour12: false,
    });
    return `${date} ${time}`;
  }
  // 30d / 365d
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
}

/**
 * Build the chart data array from the raw bucket list.
 *
 * @param buckets   - Ordered list of `{timestamp, count}` buckets.
 * @param timeRange - Active preset, used to format axis labels.
 * @returns Array of `TrendEntry` objects ready for Recharts.
 */
function buildEntries(
  buckets: Array<{ timestamp: string; count: number }>,
  timeRange: TimeRange,
): TrendEntry[] {
  return buckets.map((b) => ({
    timestamp: b.timestamp,
    label: formatXLabel(b.timestamp, timeRange),
    count: b.count,
  }));
}

// ---------------------------------------------------------------------------
// Custom tooltip
// ---------------------------------------------------------------------------

interface TrendTooltipProps extends Partial<TooltipContentProps> {
  backgroundColor: string;
  borderColor: string;
  textColor: string;
}

function TrendTooltip(props: TrendTooltipProps): React.JSX.Element | null {
  const { active, payload = [], backgroundColor, borderColor, textColor } = props;
  if (!active || payload.length === 0) return null;
  const entry = payload[0];
  if (entry == null) return null;

  const { timestamp, count } = entry.payload as TrendEntry;
  const d = new Date(timestamp);
  const label = d.toLocaleString("en-US", {
    weekday: "short",
    month: "short",
    day: "numeric",
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
  });

  return (
    <div
      style={{
        backgroundColor,
        border: `1px solid ${borderColor}`,
        borderRadius: "4px",
        padding: "8px 12px",
        color: textColor,
        fontSize: "13px",
      }}
    >
      <strong>{label}</strong>
      <br />
      {String(count)} ban{count === 1 ? "" : "s"}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

/**
 * Area chart showing ban counts over time.
 *
 * Fetches data via `useBanTrend` and handles loading, error, and empty states
 * inline so the parent only needs to pass filter props.
 *
/**
 * Area chart showing ban counts over time.
 *
 * Fetches data via `useBanTrend` and delegates loading, error, and empty states
 * to `ChartStateWrapper`.
 *
 * @param props - `timeRange` and `origin` filter props.
 */
export function BanTrendChart({
  timeRange,
  origin,
  source = "fail2ban",
}: BanTrendChartProps): React.JSX.Element {
  const styles = useStyles();
  const { buckets, isLoading, error, reload } = useBanTrend(timeRange, origin, source);

  const isEmpty = buckets.every((b) => b.count === 0);
  const entries = buildEntries(buckets, timeRange);
  const { primaryColour, axisColour, gridColour } = useMemo(
    () => ({
      primaryColour: resolveFluentToken(CHART_PALETTE[0] ?? ""),
      axisColour: resolveFluentToken(CHART_AXIS_TEXT_TOKEN),
      gridColour: resolveFluentToken(CHART_GRID_LINE_TOKEN),
    }),
    [],
  );
  const tickInterval = TICK_INTERVAL[timeRange];

  const tooltipContent = useMemo(
    () => (
      <TrendTooltip
        backgroundColor={resolveFluentToken(tokens.colorNeutralBackground1)}
        borderColor={resolveFluentToken(tokens.colorNeutralStroke2)}
        textColor={resolveFluentToken(tokens.colorNeutralForeground1)}
      />
    ),
    [],
  );

  return (
    <ChartStateWrapper
      isLoading={isLoading}
      error={error}
      onRetry={reload}
      isEmpty={isEmpty}
      emptyMessage="No bans in this time range."
      minHeight={MIN_CHART_HEIGHT}
    >
      <div className={styles.chartWrapper} style={{ height: MIN_CHART_HEIGHT }}>
        <ResponsiveContainer width="100%" height="100%">
          <AreaChart data={entries} margin={{ top: 8, right: 16, left: 0, bottom: 0 }}>
            <defs>
              <linearGradient id="trendAreaFill" x1="0" y1="0" x2="0" y2="1">
                <stop offset="5%" stopColor={primaryColour} stopOpacity={0.4} />
                <stop offset="95%" stopColor={primaryColour} stopOpacity={0.05} />
              </linearGradient>
            </defs>
            <CartesianGrid strokeDasharray="3 3" stroke={gridColour} />
            <XAxis
              dataKey="label"
              tick={{ fill: axisColour, fontSize: 11 }}
              interval={tickInterval}
              tickLine={false}
            />
            <YAxis
              allowDecimals={false}
              tick={{ fill: axisColour, fontSize: 11 }}
              tickLine={false}
              axisLine={false}
            />
            <Tooltip content={tooltipContent} />
            <Area
              type="monotone"
              dataKey="count"
              stroke={primaryColour}
              strokeWidth={2}
              fill="url(#trendAreaFill)"
              dot={false}
              activeDot={{ r: 4, fill: primaryColour }}
            />
          </AreaChart>
        </ResponsiveContainer>
      </div>
    </ChartStateWrapper>
  );
}

