/* global React, ChevLeft, ChevRight */
const { useState, useMemo, useRef, useEffect } = React;

const WEEKDAYS = ['Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør', 'Søn'];
const MONTH_NAMES = ['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'];

function ymd(y, m, d) {
  return `${y}-${String(m + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
}

// Build a list of ribbon segments per week-row.
// Returns array of { weekRow, startCol, endCol, label, isStart, isEnd }
function buildRibbons(year, month, plannedMap, startOffset, monthDays) {
  // Walk every day in the month; group consecutive same-stay days.
  const ribbons = [];
  if (!plannedMap || plannedMap.size === 0) return ribbons;

  // Determine cell index for each visible day (incl. leading blanks)
  const dayIdx = (d) => startOffset + (d - 1); // 0-indexed cell number

  let i = 1;
  while (i <= monthDays) {
    const key = ymd(year, month, i);
    const ent = plannedMap.get(key);
    if (!ent) { i++; continue; }
    // Find the run of consecutive days with same from/to
    let j = i;
    while (j + 1 <= monthDays) {
      const nxt = plannedMap.get(ymd(year, month, j + 1));
      if (!nxt || nxt.from !== ent.from || nxt.to !== ent.to) break;
      j++;
    }
    // Now [i..j] in this month belong to this stay. Split across weeks.
    let segStart = i;
    while (segStart <= j) {
      const cell = dayIdx(segStart);
      const weekRow = Math.floor(cell / 7);
      const colStart = cell % 7;
      // segment ends at min(j, last cell in this week)
      const lastCellThisWeek = weekRow * 7 + 6;
      const segEndCell = Math.min(dayIdx(j), lastCellThisWeek);
      const segEndDay = segEndCell - startOffset + 1;
      const colEnd = segEndCell % 7;
      ribbons.push({
        weekRow,
        colStart,
        colEnd,
        label: ent.label || 'Planlagt',
        isStart: ymd(year, month, segStart) === ent.from,
        isEnd: ymd(year, month, segEndDay) === ent.to,
      });
      segStart = segEndDay + 1;
    }
    i = j + 1;
  }
  return ribbons;
}

function Calendar({ data, plannedMap, currentYear, currentMonth, setMonth }) {
  const today = new Date();
  const [tip, setTip] = useState(null); // {x, y, key, info}
  const gridRef = useRef(null);
  const [gridDims, setGridDims] = useState({ cellW: 0, cellH: 0, headerH: 0 });
  const dimsRef = useRef({ cellW: 0, cellH: 0, headerH: 0 });
  const rafRef = useRef(null);

  // Measure cells for ribbon positioning. Observes the grid's parent (stable size)
  // and uses RAF + ref-cached values to avoid setState-in-observer feedback loops.
  useEffect(() => {
    const measure = () => {
      rafRef.current = null;
      if (!gridRef.current) return;
      const cells = gridRef.current.querySelectorAll('.cal-day');
      const head = gridRef.current.querySelector('.cal-weekday');
      if (cells.length === 0 || !head) return;
      const first = cells[0].getBoundingClientRect();
      const headRect = head.getBoundingClientRect();
      const next = { cellW: first.width, cellH: first.height, headerH: headRect.height };
      const prev = dimsRef.current;
      if (Math.abs(prev.cellW - next.cellW) < 0.5 &&
          Math.abs(prev.cellH - next.cellH) < 0.5 &&
          Math.abs(prev.headerH - next.headerH) < 0.5) return;
      dimsRef.current = next;
      setGridDims(next);
    };
    const schedule = () => {
      if (rafRef.current != null) return;
      rafRef.current = requestAnimationFrame(measure);
    };
    measure();
    const parent = gridRef.current?.parentElement;
    const ro = new ResizeObserver(schedule);
    if (parent) ro.observe(parent);
    window.addEventListener('resize', schedule);
    return () => {
      ro.disconnect();
      window.removeEventListener('resize', schedule);
      if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
    };
  }, [currentYear, currentMonth]);

  const first = new Date(currentYear, currentMonth, 1);
  const last = new Date(currentYear, currentMonth + 1, 0);
  const startDay = first.getDay();
  const monthDays = last.getDate();
  const startOffset = startDay === 0 ? 6 : startDay - 1;
  const totalCells = startOffset + monthDays;
  const rows = Math.ceil(totalCells / 7);

  const occMap = data?.occupancy?.dates || {};

  const ribbons = useMemo(() =>
    buildRibbons(currentYear, currentMonth, plannedMap, startOffset, monthDays),
    [currentYear, currentMonth, plannedMap, startOffset, monthDays]
  );

  const cells = [];
  for (let i = 0; i < rows * 7; i++) {
    const dayIndex = i - startOffset + 1;
    if (dayIndex < 1 || dayIndex > monthDays) {
      const d = new Date(currentYear, currentMonth, dayIndex);
      cells.push(
        <div className="cal-day other-month" key={`o${i}`}>
          <span className="cal-day-num">{d.getDate()}</span>
        </div>
      );
      continue;
    }
    const key = ymd(currentYear, currentMonth, dayIndex);
    const info = occMap[key] || {};
    const isOccupied = !!info.occupied;
    const isToday = today.getFullYear() === currentYear && today.getMonth() === currentMonth && today.getDate() === dayIndex;
    const isFuture = (currentYear === today.getFullYear() && currentMonth === today.getMonth() && dayIndex > today.getDate())
                  || currentYear > today.getFullYear()
                  || (currentYear === today.getFullYear() && currentMonth > today.getMonth());
    const isPlanned = plannedMap.has(key);
    const score = info.score;

    const cls = ['cal-day'];
    if (isOccupied) cls.push('occupied');
    if (isFuture && !isOccupied) cls.push('future');
    if (isToday) cls.push('today');
    if (isPlanned && !isOccupied) cls.push('planned');

    const onEnter = (e) => {
      if (info.scoreBreakdown || (isPlanned && plannedMap.get(key)?.label)) {
        setTip({
          x: e.clientX,
          y: e.clientY,
          key,
          info,
          plannedLabel: isPlanned ? plannedMap.get(key)?.label : null,
        });
      }
    };
    const onMove = (e) => { if (tip && tip.key === key) setTip((t) => ({ ...t, x: e.clientX, y: e.clientY })); };
    const onLeave = () => setTip(null);

    cells.push(
      <div className={cls.join(' ')} key={key} onMouseEnter={onEnter} onMouseMove={onMove} onMouseLeave={onLeave}>
        <span className="cal-day-num">{dayIndex}</span>
        <span className="cal-day-meta">
          {score != null && (
            <span className={'cal-score-dot ' + (score >= 60 ? 'high' : score >= 30 ? 'med' : 'low')} />
          )}
          {isOccupied && <span>besøk</span>}
        </span>
      </div>
    );
  }

  // Ribbon overlay
  const cellW = gridDims.cellW || 0;
  const cellH = gridDims.cellH || 0;
  const headerH = gridDims.headerH || 0;

  return (
    <div>
      <div className="cal-toolbar">
        <button className="cal-nav-btn" onClick={() => setMonth(currentYear, currentMonth - 1)} aria-label="Forrige">
          <ChevLeft />
        </button>
        <h3>{MONTH_NAMES[currentMonth]} {currentYear}</h3>
        <button className="cal-nav-btn" onClick={() => setMonth(currentYear, currentMonth + 1)} aria-label="Neste">
          <ChevRight />
        </button>
      </div>
      <div className="cal-grid" ref={gridRef}>
        {WEEKDAYS.map((w) => <div className="cal-weekday" key={w}>{w}</div>)}
        {cells}
        {cellW > 0 && (
          <div className="cal-ribbons-layer">
            {ribbons.map((r, idx) => {
              const top = headerH + r.weekRow * cellH + cellH * 0.55;
              const left = r.colStart * cellW + 4;
              const width = (r.colEnd - r.colStart + 1) * cellW - 8;
              return (
                <div className="cal-ribbon" key={idx} style={{
                  top: `${top}px`,
                  left: `${left}px`,
                  width: `${width}px`,
                  height: '18px',
                  borderTopLeftRadius: r.isStart ? 9 : 1,
                  borderBottomLeftRadius: r.isStart ? 9 : 1,
                  borderTopRightRadius: r.isEnd ? 9 : 1,
                  borderBottomRightRadius: r.isEnd ? 9 : 1,
                }}>
                  {r.label}
                </div>
              );
            })}
          </div>
        )}
      </div>
      <div className="cal-legend">
        <span className="cal-legend-item"><span className="cal-legend-swatch occupied" /> I bruk</span>
        <span className="cal-legend-item"><span className="cal-legend-swatch" /> Tom</span>
        <span className="cal-legend-item"><span className="cal-legend-swatch planned" /> Planlagt</span>
      </div>
      {tip && <ScoreTip tip={tip} />}
    </div>
  );
}

const SOURCE_LABELS = {
  doorActivity: 'Dør',
  temp1: 'Temp 1',
  temp2: 'Temp 2',
  bandwidth: 'WAN',
};

function ScoreTip({ tip }) {
  const { x, y, key, info, plannedLabel } = tip;
  const bd = info.scoreBreakdown;
  const style = { left: Math.min(x + 14, window.innerWidth - 280), top: Math.min(y + 14, window.innerHeight - 200) };
  return (
    <div className="score-tip" style={style}>
      <div className="score-tip-date">{key}</div>
      {plannedLabel && <div className="score-tip-label">{plannedLabel}</div>}
      {bd && Object.keys(SOURCE_LABELS).map((k) => {
        const v = bd[k];
        if (v == null) return (
          <div className="score-tip-row" key={k}>
            <span>{SOURCE_LABELS[k]}</span><span style={{ color: 'var(--ink-faint)' }}>—</span>
          </div>
        );
        return (
          <div className="score-tip-row" key={k}>
            <span>{SOURCE_LABELS[k]}</span>
            <span className="score-tip-bar"><div style={{ width: `${v}%`, background: v >= 60 ? 'var(--moss)' : v >= 30 ? 'var(--ember)' : 'var(--ink-faint)' }} /></span>
            <span className="score-tip-val">{Math.round(v)}</span>
          </div>
        );
      })}
      {info.score != null && (
        <>
          <hr />
          <div className="score-tip-total">
            <span>Total</span>
            <span className="score-tip-bar"><div style={{ width: `${info.score}%` }} /></span>
            <span className="score-tip-val">{info.score}</span>
          </div>
        </>
      )}
    </div>
  );
}

window.HyttenCalendar = Calendar;
