import { Svg, G, Rect, Path, Line, Text as SvgText, Circle } from '@react-pdf/renderer';

/**
 * Hand-rolled SVG chart primitives used by the dashboard PDF report.
 *
 * @react-pdf/renderer ships native `<Svg>` + path primitives but no
 * higher-level chart library. Building these by hand (vs. server-side
 * rendering recharts to PNG via a headless browser) keeps the report
 * generator pure-Node, fast, and free of binary dependencies. Charts
 * are intentionally minimal - bars / segments / lines / labels - to
 * stay legible at A4 print scale.
 *
 * Coordinates use the chart's own viewBox so callers don't have to
 * think in points. The caller supplies a `width` (in points) and the
 * component draws inside that box; height scales proportionally.
 */

const CHART_FONT = 9;
const AXIS_COLOR = '#94a3b8'; // slate-400
const GRID_COLOR = '#e2e8f0'; // slate-200
const LABEL_COLOR = '#334155'; // slate-700

interface BarChartSeriesEntry {
  label: string;
  value: number;
  /** Optional second value rendered as a faint background bar - used
   *  for "won vs total" style overlays. */
  secondaryValue?: number;
}

interface HorizontalBarChartProps {
  data: BarChartSeriesEntry[];
  /** Box width in points. */
  width: number;
  /** Box height in points; defaults to a sensible value per row. */
  height?: number;
  /** Accent color for the primary bars; defaults to a CRM brand teal. */
  primaryColor?: string;
  /** Color for the secondary (background) bar. */
  secondaryColor?: string;
  /** Format the trailing per-bar number. Defaults to integer. */
  formatValue?: (n: number) => string;
}

/**
 * Horizontal bar chart. Each row labels its bar on the left, draws
 * the bar in the middle, and prints the value on the right. Used by
 * pipeline funnel + source-conversion chart variants.
 */
export function HorizontalBarChart({
  data,
  width,
  height,
  primaryColor = '#0d9488',
  secondaryColor = '#cbd5e1',
  formatValue = (n) => String(Math.round(n)),
}: HorizontalBarChartProps) {
  const rowHeight = 22;
  const labelWidth = Math.min(140, width * 0.32);
  const valueWidth = 50;
  const innerLeft = labelWidth + 6;
  const innerRight = width - valueWidth - 4;
  const innerWidth = innerRight - innerLeft;
  const max = Math.max(1, ...data.map((d) => Math.max(d.value, d.secondaryValue ?? 0)));
  const h = height ?? data.length * rowHeight + 8;

  return (
    <Svg width={width} height={h}>
      {data.map((row, i) => {
        const y = i * rowHeight + 4;
        const barH = 14;
        const primaryW = (row.value / max) * innerWidth;
        const secondaryW = ((row.secondaryValue ?? 0) / max) * innerWidth;
        return (
          <G key={`${row.label}-${i}`}>
            <SvgText
              x={labelWidth}
              y={y + barH * 0.75}
              style={{ fontSize: CHART_FONT, fill: LABEL_COLOR }}
              textAnchor="end"
            >
              {row.label}
            </SvgText>
            {row.secondaryValue !== undefined ? (
              <Rect
                x={innerLeft}
                y={y}
                width={Math.max(1, secondaryW)}
                height={barH}
                fill={secondaryColor}
              />
            ) : null}
            <Rect
              x={innerLeft}
              y={y}
              width={Math.max(1, primaryW)}
              height={barH}
              fill={primaryColor}
            />
            <SvgText
              x={width - 4}
              y={y + barH * 0.75}
              style={{ fontSize: CHART_FONT, fill: LABEL_COLOR }}
              textAnchor="end"
            >
              {formatValue(row.value)}
            </SvgText>
          </G>
        );
      })}
    </Svg>
  );
}

interface DonutChartProps {
  data: Array<{ label: string; value: number; color?: string }>;
  width: number;
  height?: number;
  /** Inner hole radius as a fraction of the outer (0.5 = donut, 0 = pie). */
  innerRatio?: number;
  /** Centre label override (e.g. total). */
  centerLabel?: string;
  /** Default palette cycled when entries don't carry their own colors. */
  palette?: string[];
}

const DEFAULT_PALETTE = [
  '#0d9488', // teal
  '#0284c7', // sky
  '#7c3aed', // violet
  '#f97316', // orange
  '#dc2626', // red
  '#65a30d', // lime
  '#0891b2', // cyan
  '#7c2d12', // amber-deep
];

/**
 * Donut chart. Renders each slice as an SVG arc path and prints a
 * legend below. Used by berth-status + lead-source-mix.
 */
export function DonutChart({
  data,
  width,
  height,
  innerRatio = 0.6,
  centerLabel,
  palette = DEFAULT_PALETTE,
}: DonutChartProps) {
  const total = data.reduce((s, d) => s + d.value, 0);
  const chartH = height ?? 160;
  const legendRowHeight = 14;
  const legendH = data.length * legendRowHeight + 8;
  const fullH = chartH + legendH;
  const cx = width / 2;
  const cy = chartH / 2;
  const outerR = Math.min(chartH, width) / 2 - 8;
  const innerR = outerR * innerRatio;

  const START_ANGLE = -Math.PI / 2; // start at 12 o'clock
  // Pre-compute cumulative sweep per slice via reduce so we never reassign a
  // bound variable during render (react-hooks/immutability).
  const cumulativeSweeps = data.reduce<number[]>((acc, d) => {
    const sweep = total > 0 ? (d.value / total) * Math.PI * 2 : 0;
    const prev = acc.length === 0 ? 0 : (acc[acc.length - 1] ?? 0);
    acc.push(prev + sweep);
    return acc;
  }, []);
  const slices = data.map((d, i) => {
    const prevCumulative = i === 0 ? 0 : (cumulativeSweeps[i - 1] ?? 0);
    const startA = START_ANGLE + prevCumulative;
    const endA = START_ANGLE + (cumulativeSweeps[i] ?? prevCumulative);
    const sweep = endA - startA;
    const x1 = cx + Math.cos(startA) * outerR;
    const y1 = cy + Math.sin(startA) * outerR;
    const x2 = cx + Math.cos(endA) * outerR;
    const y2 = cy + Math.sin(endA) * outerR;
    const x3 = cx + Math.cos(endA) * innerR;
    const y3 = cy + Math.sin(endA) * innerR;
    const x4 = cx + Math.cos(startA) * innerR;
    const y4 = cy + Math.sin(startA) * innerR;
    const largeArc = sweep > Math.PI ? 1 : 0;
    // SVG path: M outer-start → arc → outer-end → L inner-end → arc-back → close
    const pathD =
      sweep <= 0
        ? ''
        : `M ${x1.toFixed(2)} ${y1.toFixed(2)} ` +
          `A ${outerR.toFixed(2)} ${outerR.toFixed(2)} 0 ${largeArc} 1 ${x2.toFixed(2)} ${y2.toFixed(2)} ` +
          `L ${x3.toFixed(2)} ${y3.toFixed(2)} ` +
          `A ${innerR.toFixed(2)} ${innerR.toFixed(2)} 0 ${largeArc} 0 ${x4.toFixed(2)} ${y4.toFixed(2)} ` +
          'Z';
    const color = d.color ?? palette[i % palette.length] ?? DEFAULT_PALETTE[0]!;
    return { ...d, pathD, color };
  });

  return (
    <Svg width={width} height={fullH}>
      {/* Donut slices */}
      {slices.map((s, i) =>
        s.pathD ? <Path key={`slice-${i}`} d={s.pathD} fill={s.color} /> : null,
      )}
      {/* Centre label */}
      {centerLabel ? (
        <SvgText
          x={cx}
          y={cy + 4}
          style={{ fontSize: 14, fill: LABEL_COLOR, fontWeight: 600 }}
          textAnchor="middle"
        >
          {centerLabel}
        </SvgText>
      ) : null}
      {/* Legend rows below the donut */}
      {slices.map((s, i) => {
        const ly = chartH + i * legendRowHeight + 4;
        const pct = total > 0 ? ` · ${((s.value / total) * 100).toFixed(1)}%` : '';
        return (
          <G key={`legend-${i}`}>
            <Rect x={8} y={ly} width={9} height={9} fill={s.color} />
            <SvgText x={22} y={ly + 8} style={{ fontSize: CHART_FONT, fill: LABEL_COLOR }}>
              {`${s.label} · ${s.value}${pct}`}
            </SvgText>
          </G>
        );
      })}
    </Svg>
  );
}

interface LineChartProps {
  data: Array<{ label: string; value: number }>;
  width: number;
  height?: number;
  /** Format the y-axis tick labels (defaults to "value%" for 0-100). */
  yTickFormat?: (n: number) => string;
  /** Color of the line + filled area. */
  color?: string;
}

/**
 * Simple line chart with a faint filled area below the line and a
 * basic x-axis. Used by occupancy-timeline-chart.
 */
export function LineChart({
  data,
  width,
  height = 140,
  yTickFormat = (n) => `${n.toFixed(0)}%`,
  color = '#0d9488',
}: LineChartProps) {
  const padLeft = 32;
  const padRight = 10;
  const padTop = 8;
  const padBottom = 24;
  const innerW = width - padLeft - padRight;
  const innerH = height - padTop - padBottom;

  const values = data.map((d) => d.value);
  const max = Math.max(1, ...values);
  // Round max up to a "nice" number so the y-axis reads cleanly.
  const niceMax = Math.ceil(max / 10) * 10;

  const xStep = data.length > 1 ? innerW / (data.length - 1) : 0;
  const yFor = (v: number) => padTop + innerH - (v / niceMax) * innerH;
  const xFor = (i: number) => padLeft + i * xStep;

  // Polyline path for the line itself + filled area below.
  const linePath = data
    .map((d, i) => `${i === 0 ? 'M' : 'L'} ${xFor(i).toFixed(2)} ${yFor(d.value).toFixed(2)}`)
    .join(' ');
  const areaPath = data.length
    ? linePath +
      ` L ${xFor(data.length - 1).toFixed(2)} ${(padTop + innerH).toFixed(2)}` +
      ` L ${padLeft.toFixed(2)} ${(padTop + innerH).toFixed(2)} Z`
    : '';

  // X-axis labels: thin out to ~6 labels max so they don't overlap.
  const labelEvery = Math.max(1, Math.ceil(data.length / 6));

  return (
    <Svg width={width} height={height}>
      {/* Gridlines + y-ticks (0 / 50 / 100 for percent scales) */}
      {[0, niceMax / 2, niceMax].map((v, i) => {
        const y = yFor(v);
        return (
          <G key={`grid-${i}`}>
            <Line
              x1={padLeft}
              y1={y}
              x2={width - padRight}
              y2={y}
              stroke={GRID_COLOR}
              strokeWidth={0.5}
            />
            <SvgText
              x={padLeft - 4}
              y={y + 3}
              style={{ fontSize: CHART_FONT - 1, fill: AXIS_COLOR }}
              textAnchor="end"
            >
              {yTickFormat(v)}
            </SvgText>
          </G>
        );
      })}
      {/* Area + line */}
      {areaPath ? <Path d={areaPath} fill={color} fillOpacity={0.12} /> : null}
      {linePath ? <Path d={linePath} stroke={color} strokeWidth={1.4} fill="none" /> : null}
      {/* Data points */}
      {data.map((d, i) => (
        <Circle key={`pt-${i}`} cx={xFor(i)} cy={yFor(d.value)} r={1.4} fill={color} />
      ))}
      {/* X-axis labels */}
      {data.map((d, i) =>
        i % labelEvery === 0 || i === data.length - 1 ? (
          <SvgText
            key={`xl-${i}`}
            x={xFor(i)}
            y={height - 8}
            style={{ fontSize: CHART_FONT - 1, fill: AXIS_COLOR }}
            textAnchor="middle"
          >
            {d.label}
          </SvgText>
        ) : null,
      )}
    </Svg>
  );
}
