/* global React */
/* ============================================================
   Helpers — formatters, hand-rolled SVG charts, primitives
   Exposes globally; loaded before pages.
   ============================================================ */
const { useState, useEffect, useRef, useMemo, useLayoutEffect } = React;

// ------- Formatters -------
const fmtEurM = v => {
  if (v == null || isNaN(v)) return '—';
  const a = Math.abs(v);
  if (a >= 1e9) return '€' + (v/1e9).toFixed(2) + 'B';
  if (a >= 1e6) return '€' + (v/1e6).toFixed(1) + 'M';
  if (a >= 1e3) return '€' + (v/1e3).toFixed(0) + 'K';
  return '€' + Math.round(v);
};
const fmtN = v => v == null ? '—' : Number(v).toLocaleString('en-US');
const fmtK = v => {
  if (v == null) return '—';
  const a = Math.abs(v);
  if (a >= 1e6) return (v/1e6).toFixed(2) + 'M';
  if (a >= 1e3) return (v/1e3).toFixed(1) + 'K';
  return Number(v).toFixed(0);
};
const fmtPct = (v, d=1) => v == null ? '—' : v.toFixed(d) + '%';
const fmtSignPct = (v, d=1) => v == null ? '—' : (v > 0 ? '+' : '') + v.toFixed(d) + '%';
const fmtEur = v => v == null ? '—' : '€' + Number(v).toLocaleString('en-US', {maximumFractionDigits: 0});
const MONTHS = ['', 'Oca','Şub','Mar','Nis','May','Haz','Tem','Ağu','Eyl','Eki','Kas','Ara'];
const MONTHS_FULL = ['', 'Ocak','Şubat','Mart','Nisan','Mayıs','Haziran','Temmuz','Ağustos','Eylül','Ekim','Kasım','Aralık'];

// ------- Delta / change indicator -------
const Delta = ({ value, suffix = '%', digits = 1, inverse = false }) => {
  if (value == null || isNaN(value)) return null;
  const pos = inverse ? value < 0 : value > 0;
  const neg = inverse ? value > 0 : value < 0;
  const cls = pos ? 'text-pos' : neg ? 'text-neg' : 'text-muted';
  const arrow = value > 0 ? '▲' : value < 0 ? '▼' : '·';
  return React.createElement('span', { className: `mono ${cls}`, style: { fontSize: 10.5, fontWeight: 500, letterSpacing: '0.02em' } },
    `${arrow} ${value > 0 ? '+' : ''}${value.toFixed(digits)}${suffix}`
  );
};

// ------- Hand-rolled chart utilities -------
function useSize(ref) {
  const [size, setSize] = useState({ w: 0, h: 0 });
  useLayoutEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(entries => {
      for (const e of entries) setSize({ w: e.contentRect.width, h: e.contentRect.height });
    });
    ro.observe(ref.current);
    setSize({ w: ref.current.clientWidth, h: ref.current.clientHeight });
    return () => ro.disconnect();
  }, [ref]);
  return size;
}

// Sparkline — minimal, in-text size
const Sparkline = ({ data, width = 80, height = 22, color, fill = true }) => {
  if (!data || data.length < 2) return null;
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const dx = width / (data.length - 1);
  const pts = data.map((v, i) => `${i*dx},${height - ((v - min)/range) * height}`).join(' ');
  const c = color || 'var(--ink)';
  return React.createElement('svg', { width, height, className: 'spark' },
    fill && React.createElement('polygon', {
      points: `0,${height} ${pts} ${width},${height}`,
      fill: c, fillOpacity: 0.12
    }),
    React.createElement('polyline', {
      points: pts, fill: 'none', stroke: c, strokeWidth: 1.4
    })
  );
};

// Bar chart — hand-built
// - `valueLabel`: render value above each bar (vertical mode)
// - `tooltipFormat(d, key)`: returns string or ReactNode for tooltip body
const BarChart = ({ data, height = 240, valueKey = 'value', labelKey = 'label',
                   format = fmtN, color, secondaryKey, secondaryColor, showLabels = true,
                   horizontal = false, valueLabel = false, tooltipFormat,
                   barLabel, secondaryLabel, onBarClick, minGroupWidth = 0 }) => {
  const ref = useRef();
  const { w } = useSize(ref);
  const padL = horizontal ? 110 : 36, padR = 12, padT = 14, padB = horizontal ? 22 : 50;
  const innerW = Math.max(0, w - padL - padR);
  const innerH = Math.max(0, height - padT - padB);
  const c1 = color || 'var(--ink)';
  const c2 = secondaryColor || 'var(--red)';
  const grouped = !!secondaryKey;
  const allVals = data.flatMap(d => grouped ? [d[valueKey] || 0, d[secondaryKey] || 0] : [d[valueKey] || 0]);
  const max = Math.max(...allVals, 0) * 1.1 || 1;
  const ticks = 4;
  const [hover, setHover] = useState(null);

  if (horizontal) {
    const rowH = innerH / data.length;
    const barH = Math.min(18, rowH * 0.7);
    return React.createElement('div', { ref, style: { width: '100%', height, position: 'relative' } },
      React.createElement('svg', { width: w, height, style: { display: 'block' } },
        // gridlines
        Array.from({ length: ticks + 1 }, (_, i) => {
          const x = padL + (innerW * i / ticks);
          return React.createElement('g', { key: i },
            React.createElement('line', { x1: x, x2: x, y1: padT, y2: padT + innerH, className: 'grid-line' }),
            React.createElement('text', { x, y: height - 6, textAnchor: 'middle', className: 'axis-text' },
              format(max * i / ticks))
          );
        }),
        data.map((d, i) => {
          const y = padT + i * rowH + (rowH - barH) / 2;
          const v = d[valueKey] || 0;
          const bw = (v / max) * innerW;
          return React.createElement('g', { key: i },
            React.createElement('text', {
              x: padL - 8, y: y + barH/2 + 3, textAnchor: 'end', className: 'axis-text'
            }, (d[labelKey] || '').slice(0, 18)),
            React.createElement('rect', {
              x: padL, y, width: bw, height: barH, fill: c1, rx: 2,
              className: 'bar',
              onMouseEnter: e => setHover({ x: e.clientX, y: e.clientY, d, v }),
              onMouseLeave: () => setHover(null)
            }),
            showLabels && bw > 30 && React.createElement('text', {
              x: padL + bw - 4, y: y + barH/2 + 3, textAnchor: 'end',
              style: { fill: 'var(--paper)', fontWeight: 600,
                fontFamily: 'var(--mono)', fontSize: 9.5 }
            }, format(v))
          );
        })
      ),
      hover && React.createElement('div', {
        className: 'tip',
        style: { left: hover.x - ref.current.getBoundingClientRect().left + 10,
                 top: hover.y - ref.current.getBoundingClientRect().top - 30 }
      }, `${hover.d[labelKey]}: ${format(hover.v)}`)
    );
  }

  // When minGroupWidth is set (mobile), render an SVG wider than the container
  // and let it scroll horizontally, so each group has room and value labels stop
  // overlapping (#8). Desktop passes 0 → vw === w, behaviour unchanged.
  const vw = minGroupWidth ? Math.max(w, data.length * minGroupWidth + padL + padR) : w;
  const vInnerW = Math.max(0, vw - padL - padR);
  const scrollX = minGroupWidth && vw > w;
  const groupW = vInnerW / data.length;
  const barW = grouped ? Math.min(16, groupW * 0.35) : Math.min(28, groupW * 0.65);
  const gap = grouped ? 2 : 0;

  return React.createElement('div', {
      ref,
      className: scrollX ? 'chart-scroll-x' : undefined,
      style: { width: '100%', height, position: 'relative',
               overflowX: scrollX ? 'auto' : 'visible',
               WebkitOverflowScrolling: 'touch' }
    },
    React.createElement('svg', { width: vw, height, style: { display: 'block' } },
      // gridlines
      Array.from({ length: ticks + 1 }, (_, i) => {
        const y = padT + innerH - (innerH * i / ticks);
        return React.createElement('g', { key: i },
          React.createElement('line', { x1: padL, x2: padL + vInnerW, y1: y, y2: y, className: 'grid-line' }),
          React.createElement('text', { x: padL - 6, y: y + 3, textAnchor: 'end', className: 'axis-text' },
            format(max * i / ticks))
        );
      }),
      // bars
      data.map((d, i) => {
        const cx = padL + i * groupW + groupW / 2;
        const v1 = d[valueKey] || 0;
        const v2 = grouped ? (d[secondaryKey] || 0) : 0;
        const h1 = (v1 / max) * innerH;
        const h2 = (v2 / max) * innerH;
        const x1 = grouped ? cx - barW - gap/2 : cx - barW/2;
        const x2 = grouped ? cx + gap/2 : cx;
        const label = (d[labelKey] || '').toString();
        return React.createElement('g', { key: i },
          React.createElement('rect', {
            x: x1, y: padT + innerH - h1, width: barW, height: h1, fill: c1, rx: 2,
            className: 'bar',
            style: onBarClick ? { cursor: 'pointer' } : undefined,
            onMouseEnter: e => setHover({ x: e.clientX, y: e.clientY, d, label, v: v1, k: valueKey }),
            onMouseLeave: () => setHover(null),
            onClick: onBarClick ? () => onBarClick(d) : undefined
          }),
          valueLabel && h1 > 0 && React.createElement('text', {
            x: x1 + barW/2, y: padT + innerH - h1 - 4, textAnchor: 'middle',
            style: { fontFamily: 'var(--font-mono, var(--mono))', fontSize: 10.5,
              fontWeight: 600, fill: 'var(--text-dim)' }
          }, format(v1)),
          grouped && React.createElement('rect', {
            x: x2, y: padT + innerH - h2, width: barW, height: h2, fill: c2, rx: 2,
            className: 'bar',
            onMouseEnter: e => setHover({ x: e.clientX, y: e.clientY, d, label, v: v2, k: secondaryKey }),
            onMouseLeave: () => setHover(null)
          }),
          grouped && valueLabel && h2 > 0 && React.createElement('text', {
            x: x2 + barW/2, y: padT + innerH - h2 - 4, textAnchor: 'middle',
            style: { fontFamily: 'var(--font-mono, var(--mono))', fontSize: 10.5,
              fontWeight: 600, fill: c2 }
          }, format(v2)),
          // x label — rotated if long
          React.createElement('text', {
            x: cx, y: padT + innerH + (label.length > 4 ? 14 : 14),
            textAnchor: label.length > 4 ? 'end' : 'middle',
            className: 'axis-text',
            transform: label.length > 4 ? `rotate(-32 ${cx} ${padT + innerH + 14})` : ''
          }, label.length > 14 ? label.slice(0, 13) + '…' : label)
        );
      })
    ),
    hover && (() => {
      const tipBody = tooltipFormat
        ? tooltipFormat(hover.d, hover.k)
        : `${hover.label} · ${hover.k || ''}: ${format(hover.v)}`;
      return React.createElement('div', {
        className: 'tip',
        style: { left: hover.x - ref.current.getBoundingClientRect().left + 10,
                 top: hover.y - ref.current.getBoundingClientRect().top - 30 }
      }, tipBody);
    })()
  );
};

// Composed: bars + line (e.g. revenue + load factor)
const ComposedChart = ({ data, height = 280, barKey, lineKey, labelKey = 'label',
                        barFormat = fmtEurM, lineFormat = v => v.toFixed(0) + '%',
                        barColor = 'var(--ink)', lineColor = 'var(--red)',
                        barLabel = 'Bar', lineLabel = 'Line' }) => {
  const ref = useRef();
  const { w } = useSize(ref);
  const padL = 48, padR = 48, padT = 18, padB = 28;
  const innerW = Math.max(0, w - padL - padR);
  const innerH = Math.max(0, height - padT - padB);
  const barMax = Math.max(...data.map(d => d[barKey] || 0)) * 1.15 || 1;
  const lineVals = data.map(d => d[lineKey] || 0);
  const lineMin = Math.min(...lineVals) * 0.9;
  const lineMax = Math.max(...lineVals) * 1.05 || 1;
  const lineRange = lineMax - lineMin || 1;
  const groupW = innerW / data.length;
  const barW = Math.min(24, groupW * 0.55);
  const ticks = 4;
  const [hover, setHover] = useState(null);

  const linePts = data.map((d, i) => {
    const x = padL + i * groupW + groupW / 2;
    const v = d[lineKey] || 0;
    const y = padT + innerH - ((v - lineMin)/lineRange) * innerH;
    return { x, y, v, d };
  });

  return React.createElement('div', { ref, style: { width: '100%', height, position: 'relative' } },
    React.createElement('svg', { width: w, height, style: { display: 'block' } },
      // grid + left axis
      Array.from({ length: ticks + 1 }, (_, i) => {
        const y = padT + innerH - (innerH * i / ticks);
        return React.createElement('g', { key: i },
          React.createElement('line', { x1: padL, x2: padL + innerW, y1: y, y2: y, className: 'grid-line' }),
          React.createElement('text', { x: padL - 6, y: y + 3, textAnchor: 'end', className: 'axis-text' },
            barFormat(barMax * i / ticks)),
          React.createElement('text', { x: padL + innerW + 6, y: y + 3, textAnchor: 'start', className: 'axis-text' },
            lineFormat(lineMin + lineRange * i / ticks))
        );
      }),
      // bars
      data.map((d, i) => {
        const cx = padL + i * groupW + groupW / 2;
        const v = d[barKey] || 0;
        // Clamp: a near-zero/negative value would yield a negative <rect> height,
        // which SVG rejects (console error). Revenue bars are never truly < 0.
        const h = Math.max(0, (v / barMax) * innerH);
        return React.createElement('g', { key: 'b' + i },
          React.createElement('rect', {
            x: cx - barW/2, y: padT + innerH - h, width: barW, height: h,
            fill: barColor, rx: 2, className: 'bar', opacity: 0.9,
            onMouseEnter: e => setHover({ x: e.clientX, y: e.clientY, d, i }),
            onMouseLeave: () => setHover(null)
          }),
          React.createElement('text', {
            x: cx, y: padT + innerH + 14, textAnchor: 'middle', className: 'axis-text'
          }, d[labelKey])
        );
      }),
      // line
      React.createElement('polyline', {
        points: linePts.map(p => `${p.x},${p.y}`).join(' '),
        fill: 'none', stroke: lineColor, strokeWidth: 1.8
      }),
      linePts.map((p, i) => React.createElement('circle', {
        key: 'p' + i, cx: p.x, cy: p.y, r: 3, fill: lineColor
      }))
    ),
    hover && React.createElement('div', {
      className: 'tip',
      style: {
        left: Math.max(0, hover.x - ref.current.getBoundingClientRect().left - 60),
        top: hover.y - ref.current.getBoundingClientRect().top - 50
      }
    },
      React.createElement('div', { style: { fontWeight: 600, marginBottom: 4, color: 'var(--paper)' } }, hover.d[labelKey]),
      React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6, color: 'var(--paper)' } },
        React.createElement('span', { style: { width: 10, height: 3, background: barColor, flexShrink: 0 } }),
        `${barLabel}: ${barFormat(hover.d[barKey])}`),
      React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6, color: 'var(--paper)', marginTop: 2 } },
        React.createElement('span', { style: { width: 10, height: 3, background: lineColor, flexShrink: 0 } }),
        `${lineLabel}: ${lineFormat(hover.d[lineKey])}`)
    )
  );
};

// Catmull-Rom-ish smooth path through a list of [x,y] points
function _smoothPath(points, tension = 0.2) {
  if (!points || points.length === 0) return '';
  if (points.length === 1) return `M${points[0][0]},${points[0][1]}`;
  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]) * tension;
    const cp1y = p1[1] + (p2[1] - p0[1]) * tension;
    const cp2x = p2[0] - (p3[0] - p1[0]) * tension;
    const cp2y = p2[1] - (p3[1] - p1[1]) * tension;
    d += ` C${cp1x},${cp1y} ${cp2x},${cp2y} ${p2[0]},${p2[1]}`;
  }
  return d;
}

// Line chart — single or multi-series. Skips null/undefined values per series
// (used by booking curves to truncate "actual" line where data ends).
//
// Optional per-series `tooltipFormat(d)` overrides the default tooltip text.
// `smooth: true` renders curved (cardinal spline) lines instead of straight segments.
const LineChart = ({ data, height = 240, series = [], xKey = 'label',
                    format = fmtN, xFormat = v => v, smooth = false, xTickIndices }) => {
  const ref = useRef();
  const { w } = useSize(ref);
  const padL = 50, padR = 16, padT = 14, padB = 26;
  const innerW = Math.max(0, w - padL - padR);
  const innerH = Math.max(0, height - padT - padB);
  const valid = v => v != null && !isNaN(v);
  const allVals = data.flatMap(d => series.map(s => d[s.key])).filter(valid);
  const min = Math.min(...allVals, 0);
  const max = (allVals.length ? Math.max(...allVals) : 1) * 1.05 || 1;
  const range = max - min || 1;
  const dx = data.length > 1 ? innerW / (data.length - 1) : innerW;
  const ticks = 4;
  const [hover, setHover] = useState(null);

  const yFor = v => padT + innerH - ((v - min)/range) * innerH;
  const xFor = i => padL + i * dx;

  return React.createElement('div', { ref, style: { width: '100%', height, position: 'relative' } },
    React.createElement('svg', { width: w, height, style: { display: 'block' } },
      Array.from({ length: ticks + 1 }, (_, i) => {
        const y = padT + innerH - (innerH * i / ticks);
        return React.createElement('g', { key: i },
          React.createElement('line', { x1: padL, x2: padL + innerW, y1: y, y2: y, className: 'grid-line' }),
          React.createElement('text', { x: padL - 6, y: y + 3, textAnchor: 'end', className: 'axis-text' },
            format(min + range * i / ticks))
        );
      }),
      (() => {
        const tickSet = xTickIndices && xTickIndices.length
          ? new Set(xTickIndices)
          : null;
        return data.map((d, i) => {
          const show = tickSet
            ? tickSet.has(i)
            : i % Math.ceil(data.length / 8) === 0;
          if (!show) return null;
          return React.createElement('text', {
            key: 'x' + i, x: xFor(i), y: padT + innerH + 14,
            textAnchor: 'middle', className: 'axis-text'
          }, xFormat(d[xKey]));
        });
      })(),
      series.map((s, si) => {
        // collect only points with valid values, preserving x position by index
        const validPairs = data
          .map((d, i) => valid(d[s.key]) ? [xFor(i), yFor(d[s.key])] : null)
          .filter(Boolean);
        const ptsStr = validPairs.map(p => p.join(',')).join(' ');
        const lastValidIdx = data.reduce((acc, d, i) => valid(d[s.key]) ? i : acc, -1);
        const firstValidIdx = data.findIndex(d => valid(d[s.key]));
        const useSmooth = smooth && validPairs.length > 1;
        const linePath = useSmooth ? _smoothPath(validPairs) : null;
        const fillD = useSmooth
          ? `${linePath} L${xFor(lastValidIdx)},${padT + innerH} L${xFor(firstValidIdx)},${padT + innerH} Z`
          : null;
        return React.createElement('g', { key: si },
          s.fill && validPairs.length > 0 && (useSmooth
            ? React.createElement('path', {
                d: fillD, fill: s.color, fillOpacity: 0.12, stroke: 'none'
              })
            : React.createElement('polygon', {
                points: `${xFor(firstValidIdx)},${padT + innerH} ${ptsStr} ${xFor(lastValidIdx)},${padT + innerH}`,
                fill: s.color, fillOpacity: 0.12
              })),
          validPairs.length > 1 && (useSmooth
            ? React.createElement('path', {
                d: linePath, fill: 'none', stroke: s.color, strokeWidth: s.width || 1.6,
                strokeDasharray: s.dashed ? '7 4' : '', strokeLinecap: 'round', strokeLinejoin: 'round'
              })
            : React.createElement('polyline', {
                points: ptsStr, fill: 'none', stroke: s.color, strokeWidth: s.width || 1.6,
                strokeDasharray: s.dashed ? '7 4' : ''
              })),
          data.map((d, i) => valid(d[s.key]) && React.createElement('circle', {
            key: i, cx: xFor(i), cy: yFor(d[s.key]), r: 2.5, fill: s.color, opacity: 0
          }))
        );
      }),
      // hover overlay
      data.map((d, i) => React.createElement('rect', {
        key: 'h' + i, x: xFor(i) - dx/2, y: padT, width: dx, height: innerH,
        fill: 'transparent',
        onMouseEnter: e => setHover({ x: e.clientX, y: e.clientY, d, i }),
        onMouseLeave: () => setHover(null)
      }))
    ),
    hover && React.createElement('div', {
      className: 'tip',
      style: { left: xFor(hover.i) - 50, top: padT - 10 }
    },
      React.createElement('div', { style: { fontWeight: 600, marginBottom: 4, color: 'var(--paper)' } }, xFormat(hover.d[xKey])),
      series.map((s, si) => {
        const v = hover.d[s.key];
        const text = valid(v)
          ? (s.tooltipFormat ? s.tooltipFormat(hover.d) : `${s.label}: ${format(v)}`)
          : `${s.label}: —`;
        return React.createElement('div', { key: si,
          style: { display: 'flex', alignItems: 'center', gap: 6, color: 'var(--paper)', marginTop: si === 0 ? 0 : 2 }
        },
          React.createElement('span', { style: { width: 10, height: 3, background: s.color, flexShrink: 0 } }),
          text
        );
      })
    ),
    // Legend — uses inline SVG so dashed series show as dashed in the legend swatch
    React.createElement('div', {
      style: { position: 'absolute', top: 4, right: 12, display: 'flex', gap: 12, fontSize: 10 }
    },
      series.map((s, si) => React.createElement('div', {
        key: si, style: { display: 'flex', alignItems: 'center', gap: 5, color: 'var(--text-dim)' }
      },
        React.createElement('svg', { width: 18, height: 4, style: { flexShrink: 0 } },
          React.createElement('line', {
            x1: 0, y1: 2, x2: 18, y2: 2,
            stroke: s.color, strokeWidth: s.width || 1.6,
            strokeDasharray: s.dashed ? '4 2' : 'none'
          })
        ),
        s.label
      ))
    )
  );
};

// Heatmap cell color helpers
const heatColor = (v, opts = {}) => {
  // Diverging — for price changes, etc. v in roughly -10..+10
  const { mid = 0, max = 10 } = opts;
  const t = Math.max(-1, Math.min(1, (v - mid) / max));
  if (Math.abs(t) < 0.08) return { bg: 'var(--paper-edge)', fg: 'var(--text-dim)' };
  if (t > 0) {
    // Raise — green/positive
    const a = 0.15 + Math.abs(t) * 0.55;
    return { bg: `color-mix(in srgb, var(--green) ${(a*100).toFixed(0)}%, var(--paper-soft))`,
             fg: a > 0.45 ? 'white' : 'var(--green)' };
  } else {
    // Drop — red/negative
    const a = 0.15 + Math.abs(t) * 0.55;
    return { bg: `color-mix(in srgb, var(--red) ${(a*100).toFixed(0)}%, var(--paper-soft))`,
             fg: a > 0.45 ? 'white' : 'var(--red)' };
  }
};
const occColor = v => {
  // occupancy 0–100 — sequential
  if (v == null) return { bg: 'var(--paper-edge)', fg: 'var(--text-dim)' };
  if (v >= 90) return { bg: 'var(--ink)', fg: 'var(--paper)' };
  if (v >= 75) return { bg: 'color-mix(in srgb, var(--ink) 70%, var(--paper))', fg: 'white' };
  if (v >= 55) return { bg: 'color-mix(in srgb, var(--ink) 40%, var(--paper))', fg: 'white' };
  if (v >= 35) return { bg: 'color-mix(in srgb, var(--ink) 18%, var(--paper))', fg: 'var(--ink)' };
  return { bg: 'var(--paper-edge)', fg: 'var(--text-dim)' };
};

// Keyboard shortcut display
const Kbd = ({ children }) => React.createElement('span', {
  style: {
    fontFamily: 'var(--font-mono)', fontSize: 9.5, padding: '1px 5px',
    border: '1px solid var(--line-strong)', borderRadius: 3,
    color: 'var(--text-muted)', background: 'var(--bg-1)'
  }
}, children);

// Stars badge — clean numeric, no glyphs
const Stars = ({ n }) => React.createElement('span', { className: 'stars' }, `${n}★`);

// Panel wrapper
const Panel = ({ title, sub, actions, children, flush = false, className = '' }) =>
  React.createElement('div', { className: `panel ${className}` },
    (title || actions) && React.createElement('div', { className: 'panel-head' },
      React.createElement('div', null,
        title && React.createElement('div', { className: 'panel-head-title' }, title),
        sub && React.createElement('div', { className: 'panel-head-sub' }, sub)
      ),
      actions && React.createElement('div', { className: 'row gap-2' }, actions)
    ),
    React.createElement('div', { className: flush ? '' : 'panel-body' }, children)
  );

// KPI tile
const KPI = ({ label, value, sub, delta, deltaInverse, sparkline, sparklineColor }) =>
  React.createElement('div', { className: 'kpi' },
    React.createElement('div', { className: 'kpi-label' }, label),
    React.createElement('div', { className: 'kpi-value' }, value),
    React.createElement('div', { className: 'kpi-sub' },
      sub && React.createElement('span', null, sub),
      delta != null && React.createElement(Delta, { value: delta, inverse: deltaInverse })
    ),
    sparkline && React.createElement('div', { className: 'kpi-spark' },
      React.createElement(Sparkline, { data: sparkline, width: 80, height: 26, color: sparklineColor || 'var(--red)' })
    )
  );

// ---- Period / date helpers ------------------------------------------------
const _isoDate = (d) => {
  const y = d.getFullYear(), m = String(d.getMonth() + 1).padStart(2, '0'),
        dd = String(d.getDate()).padStart(2, '0');
  return `${y}-${m}-${dd}`;
};
const _monthStartISO = (year, m) => `${year}-${String(m).padStart(2, '0')}-01`;
const _monthEndISO = (year, m) => _isoDate(new Date(year, m, 0));

// Month coverage weights for a date range — each overlapping month tagged with
// the fraction of its days inside [from,to]. Lets monthly-grain segment tables
// respond to a *specific date range* via pro-rata day weighting.
const monthWeights = (from, to) => {
  const f = new Date(from + 'T00:00:00'), t = new Date(to + 'T00:00:00');
  if (isNaN(f) || isNaN(t) || f > t) return [];
  const out = [];
  const cur = new Date(f.getFullYear(), f.getMonth(), 1);
  while (cur <= t) {
    const mStart = new Date(cur.getFullYear(), cur.getMonth(), 1);
    const mEnd = new Date(cur.getFullYear(), cur.getMonth() + 1, 0);
    const lo = f > mStart ? f : mStart;
    const hi = t < mEnd ? t : mEnd;
    const daysCovered = Math.round((hi - lo) / 86400000) + 1;
    out.push({ month: cur.getMonth() + 1, weight: Math.max(0, Math.min(1, daysCovered / mEnd.getDate())) });
    cur.setMonth(cur.getMonth() + 1);
  }
  return out;
};

// Aggregate monthly cross-tab rows for one dimension into YTD-array-shaped
// records (revenue/pax/adr/roomnights/bookings/margin_pct), pro-rated by month
// weight. Output matches the by_<dim>_*_ytd shape SegmentBlock/HotelView expect.
const aggMonthly = (rows, keyName, weights) => {
  const wmap = {};
  weights.forEach(w => { if (w.weight > 0) wmap[w.month] = w.weight; });
  const acc = {};
  (rows || []).forEach(r => {
    const w = wmap[r.CheckInMonth];
    if (!w) return;
    const k = r[keyName];
    let a = acc[k];
    if (!a) a = acc[k] = { [keyName]: k, revenue: 0, pax: 0, roomnights: 0, bookings: 0, margin: 0,
                           _adrNum: 0, _adrDen: 0,
                           Region: r.Region, SubRegion: r.SubRegion, Stars: r.Stars, Concept: r.Concept };
    a.revenue += (r.revenue || 0) * w;
    a.pax += (r.pax || 0) * w;
    a.roomnights += (r.roomnights || 0) * w;
    a.bookings += (r.bookings || 0) * w;
    a.margin += (r.margin || 0) * w;
    a._adrNum += (r.adr || 0) * (r.revenue || 0) * w;
    a._adrDen += (r.revenue || 0) * w;
  });
  return Object.values(acc).map(a => ({
    [keyName]: a[keyName],
    Region: a.Region, SubRegion: a.SubRegion, Stars: a.Stars, Concept: a.Concept,
    revenue: a.revenue, pax: Math.round(a.pax), roomnights: Math.round(a.roomnights),
    bookings: Math.round(a.bookings), margin: a.margin,
    adr: a._adrDen ? a._adrNum / a._adrDen : 0,
    margin_pct: a.revenue ? a.margin / a.revenue * 100 : 0,
  })).sort((x, y) => (y.revenue || 0) - (x.revenue || 0));
};

// Period filter — month-range dropdowns + a precise date-range picker + presets.
// `value` is { from, to } as 'YYYY-MM-DD'; `onChange` receives the same shape.
// Used by Raporlama (Aylık Görünüm + Segment Detay) and Rakip Seti.
const PERIOD_PRESETS = [
  { id: 'season', label: 'Tüm Sezon', from: '2026-04-01', to: '2026-10-31' },
  { id: 'ytd',    label: 'YTD',       from: '2026-01-01', to: '2026-05-05' },
  { id: 'last3',  label: 'Son 3 Ay',  from: '2026-03-01', to: '2026-05-05' },
  { id: 'year',   label: 'Tüm Yıl',   from: '2026-01-01', to: '2026-12-31' },
];

const PeriodFilter = ({ value, onChange, year = 2026, presets = true }) => {
  const { from, to } = value;
  const fm = new Date(from + 'T00:00:00').getMonth() + 1;
  const tm = new Date(to + 'T00:00:00').getMonth() + 1;
  const monthOpts = Array.from({ length: 12 }, (_, i) => i + 1);
  const sameRange = (p) => p.from === from && p.to === to;

  const setStartMonth = (m) => {
    const nf = _monthStartISO(year, m);
    onChange({ from: nf, to: to < nf ? _monthEndISO(year, m) : to });
  };
  const setEndMonth = (m) => {
    const nt = _monthEndISO(year, m);
    onChange({ from: from > nt ? _monthStartISO(year, m) : from, to: nt });
  };
  const setFrom = (v) => { if (v) onChange({ from: v, to: to < v ? v : to }); };
  const setTo = (v) => { if (v) onChange({ from: from > v ? v : from, to: v }); };

  const monthSelect = (val, onSel) => React.createElement('select', {
    className: 'input', value: val, style: { height: 28, padding: '0 8px', minWidth: 92 },
    onChange: e => onSel(Number(e.target.value)),
  }, monthOpts.map(m => React.createElement('option', { key: m, value: m }, MONTHS_FULL[m])));

  const dateInput = (val, onSel) => React.createElement('input', {
    type: 'date', className: 'input', value: val,
    min: `${year}-01-01`, max: `${year}-12-31`, style: { height: 28, padding: '0 8px' },
    onChange: e => onSel(e.target.value),
  });

  const divider = () => React.createElement('span', {
    style: { width: 1, height: 20, background: 'var(--rule-soft)', margin: '0 2px' }
  });

  return React.createElement('div', {
    className: 'panel period-filter',
    style: { padding: '10px 16px', display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }
  },
    React.createElement('span', {
      style: { fontSize: 10, letterSpacing: '0.22em', textTransform: 'uppercase',
        fontWeight: 700, color: 'var(--text-muted)' }
    }, 'DÖNEM'),
    monthSelect(fm, setStartMonth),
    React.createElement('span', { style: { color: 'var(--text-faint)' } }, '–'),
    monthSelect(tm, setEndMonth),
    divider(),
    React.createElement('span', {
      style: { fontSize: 9.5, letterSpacing: '0.16em', textTransform: 'uppercase',
        fontWeight: 700, color: 'var(--text-faint)' }
    }, 'TARİH'),
    dateInput(from, setFrom),
    React.createElement('span', { style: { color: 'var(--text-faint)' } }, '–'),
    dateInput(to, setTo),
    presets && React.createElement('div', {
      style: { display: 'flex', gap: 4, marginLeft: 'auto', flexWrap: 'wrap' }
    },
      PERIOD_PRESETS.map(p => React.createElement('div', {
        key: p.id, className: 'filter-chip' + (sameRange(p) ? ' active' : ''),
        onClick: () => onChange({ from: p.from, to: p.to })
      }, p.label))
    )
  );
};

// Expose
Object.assign(window, {
  fmtEurM, fmtN, fmtK, fmtPct, fmtSignPct, fmtEur, MONTHS, MONTHS_FULL,
  Delta, Sparkline, BarChart, ComposedChart, LineChart, heatColor, occColor,
  Kbd, Stars, Panel, KPI, useSize, PeriodFilter, PERIOD_PRESETS, monthWeights, aggMonthly,
  useState, useEffect, useRef, useMemo, useLayoutEffect
});
