// HCG Curve Chart — smooth exponential curve with fill, normal-range bands,
// actual measurement points, and test-day markers.

const CHART_W = 880;
const CHART_H = 440;
const PAD = { top: 32, right: 28, bottom: 48, left: 68 };

// Singleton HCG reference ranges by days-post-ovulation (dpo) → [low, high] mIU/mL
// Values adapted from broadly-cited IVF clinical ranges; used as a guide only.
const SINGLETON_RANGE = {
  // dpo : [low, high]
  9:  [5, 50],
  10: [8, 72],
  11: [11, 105],
  12: [17, 170],
  13: [24, 260],
  14: [29, 400],
  15: [39, 550],
  16: [68, 780],
  17: [120, 1300],
  18: [220, 2000],
  19: [370, 3100],
  20: [520, 4900],
  21: [750, 7100],
  22: [1050, 9800],
  23: [1400, 13000],
  24: [1830, 17000],
  25: [2400, 22000],
  26: [4200, 26000],
  27: [5400, 31000],
  28: [7100, 36000],
};
const TWIN_MULT = 1.8;

function rangeAt(day, twins) {
  const d = Math.round(day);
  const ref = SINGLETON_RANGE[d];
  if (!ref) return null;
  const m = twins ? TWIN_MULT : 1;
  return [ref[0] * m, ref[1] * m];
}

function exponentialValue(startValue, startDay, day, doublingHrs) {
  // HCG doubles every `doublingHrs` hours.
  // value(day) = startValue * 2 ^ ((day - startDay) * 24 / doublingHrs)
  if (day < startDay) return null;
  const exponent = ((day - startDay) * 24) / doublingHrs;
  return startValue * Math.pow(2, exponent);
}

// Smooth cubic Bézier path through points
function smoothPath(points) {
  if (points.length < 2) return '';
  let d = `M ${points[0][0]} ${points[0][1]}`;
  for (let i = 0; i < points.length - 1; i++) {
    const p0 = points[i - 1] || points[i];
    const p1 = points[i];
    const p2 = points[i + 1];
    const p3 = points[i + 2] || p2;
    const cp1x = p1[0] + (p2[0] - p0[0]) / 6;
    const cp1y = p1[1] + (p2[1] - p0[1]) / 6;
    const cp2x = p2[0] - (p3[0] - p1[0]) / 6;
    const cp2y = p2[1] - (p3[1] - p1[1]) / 6;
    d += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2[0]} ${p2[1]}`;
  }
  return d;
}

function HcgChart({
  embryos,
  startValue,
  startDay,
  doublingHrs,
  dayMax,
  logScale,
  showBands,
  twins,
  actuals,
  testDay,
  accentColor,
  accentSoft,
  onClickDay,
}) {
  const innerW = CHART_W - PAD.left - PAD.right;
  const innerH = CHART_H - PAD.top - PAD.bottom;

  // Sample the curve
  const samples = React.useMemo(() => {
    const out = [];
    const steps = 280;
    for (let i = 0; i <= steps; i++) {
      const day = (i / steps) * dayMax;
      const v = exponentialValue(startValue * embryos, startDay, day, doublingHrs);
      out.push([day, v]);
    }
    return out;
  }, [embryos, startValue, startDay, doublingHrs, dayMax]);

  // Y-axis bounds — take from curve and reference ranges
  const maxY = React.useMemo(() => {
    let m = 100;
    samples.forEach(([d, v]) => { if (v != null && d <= dayMax) m = Math.max(m, v); });
    if (showBands) {
      for (let d = 0; d <= dayMax; d++) {
        const r = rangeAt(d, twins);
        if (r) m = Math.max(m, r[1]);
      }
    }
    actuals.forEach(a => { if (a.value) m = Math.max(m, a.value); });
    return m * 1.08;
  }, [samples, showBands, twins, actuals, dayMax]);

  const minY = logScale ? 1 : 0;

  const xScale = (day) => PAD.left + (day / dayMax) * innerW;
  const yScale = (val) => {
    if (val == null) return null;
    if (logScale) {
      const v = Math.max(val, 0.5);
      const t = (Math.log10(v) - Math.log10(Math.max(minY, 0.5))) /
                (Math.log10(maxY) - Math.log10(Math.max(minY, 0.5)));
      return PAD.top + innerH - t * innerH;
    }
    const t = (val - minY) / (maxY - minY);
    return PAD.top + innerH - t * innerH;
  };

  // Build curve points
  const curvePts = samples
    .filter(([d, v]) => v != null)
    .map(([d, v]) => [xScale(d), yScale(v)]);

  const curveD = smoothPath(curvePts);
  const areaD = curvePts.length
    ? `${curveD} L ${curvePts[curvePts.length - 1][0]} ${PAD.top + innerH} L ${curvePts[0][0]} ${PAD.top + innerH} Z`
    : '';

  // Normal range band polygon
  const bandLow = [];
  const bandHigh = [];
  if (showBands) {
    for (let d = 0; d <= dayMax; d += 0.5) {
      const r = rangeAt(d, twins);
      if (r) {
        bandLow.push([xScale(d), yScale(r[0])]);
        bandHigh.push([xScale(d), yScale(r[1])]);
      }
    }
  }
  const bandPath = bandLow.length
    ? `M ${bandLow[0][0]} ${bandLow[0][1]} ` +
      bandLow.slice(1).map(p => `L ${p[0]} ${p[1]}`).join(' ') +
      ' ' + bandHigh.reverse().map(p => `L ${p[0]} ${p[1]}`).join(' ') + ' Z'
    : '';

  // X-axis ticks
  const xTicks = [];
  const xStep = dayMax <= 14 ? 2 : dayMax <= 21 ? 3 : 4;
  for (let d = 0; d <= dayMax; d += xStep) xTicks.push(d);
  if (xTicks[xTicks.length - 1] !== dayMax) xTicks.push(dayMax);

  // Y-axis ticks
  const yTicks = React.useMemo(() => {
    if (logScale) {
      const ticks = [];
      let v = 1;
      while (v <= maxY) { ticks.push(v); v *= 10; }
      return ticks;
    } else {
      const ticks = [];
      const step = niceStep(maxY / 5);
      for (let v = 0; v <= maxY; v += step) ticks.push(v);
      return ticks;
    }
  }, [maxY, logScale]);

  // Hover state
  const [hover, setHover] = React.useState(null);
  const svgRef = React.useRef(null);

  const onMove = (e) => {
    const rect = svgRef.current.getBoundingClientRect();
    const scale = CHART_W / rect.width;
    const x = (e.clientX - rect.left) * scale;
    if (x < PAD.left || x > PAD.left + innerW) { setHover(null); return; }
    const day = ((x - PAD.left) / innerW) * dayMax;
    const v = exponentialValue(startValue * embryos, startDay, day, doublingHrs);
    setHover({ day, value: v });
  };

  const valueAtTestDay = exponentialValue(startValue * embryos, startDay, testDay, doublingHrs);

  return (
    <div style={{ width: '100%', position: 'relative' }}>
      <svg
        ref={svgRef}
        viewBox={`0 0 ${CHART_W} ${CHART_H}`}
        preserveAspectRatio="xMidYMid meet"
        style={{ width: '100%', height: 'auto', display: 'block', touchAction: 'none' }}
        onMouseMove={onMove}
        onMouseLeave={() => setHover(null)}
      >
        <defs>
          <linearGradient id="areaGrad" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor={accentColor} stopOpacity="0.38" />
            <stop offset="100%" stopColor={accentColor} stopOpacity="0.02" />
          </linearGradient>
          <pattern id="bandPattern" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
            <rect width="6" height="6" fill="var(--sage-soft)" opacity="0.55" />
            <line x1="0" y1="0" x2="0" y2="6" stroke="var(--sage)" strokeOpacity="0.2" strokeWidth="1" />
          </pattern>
        </defs>

        {/* grid — horizontal */}
        {yTicks.map((v, i) => {
          const y = yScale(v);
          if (y == null) return null;
          return (
            <g key={`yt-${i}`}>
              <line x1={PAD.left} y1={y} x2={CHART_W - PAD.right} y2={y}
                stroke="var(--line-soft)" strokeWidth="1"
                strokeDasharray={v === 0 ? '0' : '2 4'}
              />
              <text x={PAD.left - 10} y={y + 3} textAnchor="end"
                fontSize="11" fill="var(--ink-faint)"
                fontFamily="'JetBrains Mono', monospace"
              >
                {formatVal(v)}
              </text>
            </g>
          );
        })}

        {/* grid — vertical */}
        {xTicks.map((d, i) => {
          const x = xScale(d);
          return (
            <g key={`xt-${i}`}>
              <line x1={x} y1={PAD.top} x2={x} y2={PAD.top + innerH}
                stroke="var(--line-soft)" strokeDasharray="2 4" strokeWidth="1" />
              <text x={x} y={CHART_H - PAD.bottom + 20} textAnchor="middle"
                fontSize="11" fill="var(--ink-faint)"
                fontFamily="'JetBrains Mono', monospace"
              >
                D{d}
              </text>
            </g>
          );
        })}

        {/* Normal range band */}
        {showBands && bandPath && (
          <>
            <path d={bandPath} fill="url(#bandPattern)" />
            <text x={CHART_W - PAD.right - 8}
              y={PAD.top + 16}
              textAnchor="end"
              fontSize="10.5"
              fill="var(--sage)"
              fontFamily="'JetBrains Mono', monospace"
              letterSpacing="0.05em"
            >
              {twins ? 'TWIN RANGE' : 'SINGLETON RANGE'}
            </text>
          </>
        )}

        {/* Curve area */}
        {areaD && <path d={areaD} fill="url(#areaGrad)" />}
        {curveD && <path d={curveD} fill="none" stroke={accentColor} strokeWidth="2.5" strokeLinejoin="round" />}

        {/* Start marker */}
        {(() => {
          const x = xScale(startDay);
          const y = yScale(startValue * embryos);
          if (y == null) return null;
          return (
            <g>
              <circle cx={x} cy={y} r="5" fill="var(--card)" stroke={accentColor} strokeWidth="2" />
              <circle cx={x} cy={y} r="2" fill={accentColor} />
            </g>
          );
        })()}

        {/* Test day marker */}
        {testDay > 0 && testDay <= dayMax && (() => {
          const x = xScale(testDay);
          const y = yScale(valueAtTestDay);
          return (
            <g>
              <line x1={x} y1={PAD.top} x2={x} y2={PAD.top + innerH}
                stroke="var(--rose)" strokeWidth="1" strokeDasharray="3 3" opacity="0.7" />
              <rect x={x - 38} y={PAD.top - 6} width="76" height="20" rx="10"
                fill="var(--card)" stroke="var(--rose)" strokeWidth="1" />
              <text x={x} y={PAD.top + 7} textAnchor="middle" fontSize="10.5"
                fill="var(--rose)" fontFamily="'JetBrains Mono', monospace" letterSpacing="0.04em">
                TEST · D{testDay}
              </text>
              {y != null && (
                <>
                  <circle cx={x} cy={y} r="6" fill="var(--card)" stroke="var(--rose)" strokeWidth="2" />
                  <circle cx={x} cy={y} r="2.5" fill="var(--rose)" />
                </>
              )}
            </g>
          );
        })()}

        {/* Actual measurement points */}
        {actuals.map((a, i) => {
          if (!a.day && a.day !== 0) return null;
          if (!a.value) return null;
          const x = xScale(a.day);
          const y = yScale(a.value);
          if (y == null) return null;
          const predicted = exponentialValue(startValue * embryos, startDay, a.day, doublingHrs);
          const deviation = predicted ? ((a.value - predicted) / predicted) * 100 : 0;
          return (
            <g key={`act-${i}`}>
              <line x1={x} y1={yScale(predicted) || y} x2={x} y2={y}
                stroke="var(--ink-faint)" strokeDasharray="2 2" opacity="0.4" />
              <circle cx={x} cy={y} r="6.5" fill="var(--butter)" stroke="var(--ink)" strokeWidth="1.5" />
              <text x={x} y={y - 12} textAnchor="middle" fontSize="10.5"
                fill="var(--ink)" fontFamily="'JetBrains Mono', monospace" fontWeight="500">
                {formatVal(a.value)}
              </text>
              <text x={x} y={y + 20} textAnchor="middle" fontSize="9.5"
                fill={deviation >= 0 ? 'var(--sage)' : 'var(--rose)'}
                fontFamily="'JetBrains Mono', monospace">
                {deviation >= 0 ? '+' : ''}{deviation.toFixed(0)}%
              </text>
            </g>
          );
        })}

        {/* Axes */}
        <line x1={PAD.left} y1={PAD.top} x2={PAD.left} y2={PAD.top + innerH}
          stroke="var(--line)" strokeWidth="1" />
        <line x1={PAD.left} y1={PAD.top + innerH} x2={CHART_W - PAD.right} y2={PAD.top + innerH}
          stroke="var(--line)" strokeWidth="1" />

        {/* Axis labels */}
        <text x={PAD.left - 50} y={PAD.top + innerH / 2}
          fontSize="10.5" fill="var(--ink-soft)"
          fontFamily="'JetBrains Mono', monospace"
          letterSpacing="0.1em"
          transform={`rotate(-90 ${PAD.left - 50} ${PAD.top + innerH / 2})`}
          textAnchor="middle"
        >
          HCG · mIU/mL {logScale ? '(log)' : ''}
        </text>
        <text x={PAD.left + innerW / 2} y={CHART_H - 10}
          textAnchor="middle"
          fontSize="10.5" fill="var(--ink-soft)"
          fontFamily="'JetBrains Mono', monospace"
          letterSpacing="0.1em"
        >
          DAYS POST TRANSFER
        </text>

        {/* Hover readout */}
        {hover && hover.value != null && (() => {
          const x = xScale(hover.day);
          const y = yScale(hover.value);
          if (y == null) return null;
          return (
            <g pointerEvents="none">
              <line x1={x} y1={PAD.top} x2={x} y2={PAD.top + innerH}
                stroke={accentColor} strokeWidth="1" opacity="0.3" />
              <circle cx={x} cy={y} r="4" fill={accentColor} />
              <circle cx={x} cy={y} r="8" fill={accentColor} opacity="0.2" />
              {/* tooltip */}
              {(() => {
                const tx = Math.min(x + 12, CHART_W - PAD.right - 120);
                const ty = Math.max(y - 38, PAD.top + 8);
                return (
                  <g transform={`translate(${tx} ${ty})`}>
                    <rect width="110" height="34" rx="6" fill="var(--card)" stroke="var(--line)" />
                    <text x="8" y="14" fontSize="10.5" fill="var(--ink-faint)"
                      fontFamily="'JetBrains Mono', monospace">
                      Day {hover.day.toFixed(1)}
                    </text>
                    <text x="8" y="28" fontSize="12" fill="var(--ink)"
                      fontFamily="'JetBrains Mono', monospace" fontWeight="500">
                      {formatVal(hover.value)} mIU/mL
                    </text>
                  </g>
                );
              })()}
            </g>
          );
        })()}
      </svg>
    </div>
  );
}

function niceStep(rough) {
  const pow = Math.pow(10, Math.floor(Math.log10(rough)));
  const n = rough / pow;
  let nice;
  if (n < 1.5) nice = 1;
  else if (n < 3) nice = 2;
  else if (n < 7) nice = 5;
  else nice = 10;
  return nice * pow;
}

function formatVal(v) {
  if (v == null) return '—';
  if (v >= 10000) return (v / 1000).toFixed(1) + 'k';
  if (v >= 1000) return (v / 1000).toFixed(2) + 'k';
  if (v >= 100) return Math.round(v).toString();
  if (v >= 10) return v.toFixed(1);
  return v.toFixed(2);
}

Object.assign(window, { HcgChart, exponentialValue, rangeAt, formatVal });
