// GanttChart — timeline visualization aligned with WBS table rows.
// Renders a date axis, task bars with progress fill, dependency arrows,
// milestone diamonds, today line, and critical path highlight.

const ROW_H = 36;
const DAY_W_BY_SCALE = { Day: 28, Week: 14, Month: 5 };
const TODAY = new Date("2026-05-01"); // pinned to project header date

const parseDate = (s) => new Date(s + "T00:00:00");
const dayDiff = (a, b) => Math.round((b - a) / 86400000);
const fmtMonth = (d) => d.toLocaleDateString("en-US", { month: "short" });
const fmtDay = (d) => d.getDate();

// Build month/week tick array between two dates
const buildTicks = (start, end, scale) => {
  const ticks = [];
  const cur = new Date(start);
  if (scale === "Day") {
    while (cur <= end) {
      ticks.push({ date: new Date(cur), label: fmtDay(cur), major: cur.getDate() === 1 });
      cur.setDate(cur.getDate() + 1);
    }
  } else if (scale === "Week") {
    // align to monday
    cur.setDate(cur.getDate() - ((cur.getDay() + 6) % 7));
    while (cur <= end) {
      ticks.push({ date: new Date(cur), label: `${fmtMonth(cur)} ${fmtDay(cur)}`, major: cur.getDate() <= 7 });
      cur.setDate(cur.getDate() + 7);
    }
  } else {
    cur.setDate(1);
    while (cur <= end) {
      ticks.push({ date: new Date(cur), label: cur.toLocaleDateString("en-US", { month: "short", year: cur.getMonth() === 0 ? "2-digit" : undefined }), major: true });
      cur.setMonth(cur.getMonth() + 1);
    }
  }
  return ticks;
};

const GanttChart = ({ rows, rowMap, expanded, projectStart, projectEnd, scale, selectedId, onSelect, hoveredId, onHover, criticalOnly, showDeps }) => {
  const dayW = DAY_W_BY_SCALE[scale];
  const start = parseDate(projectStart);
  const end = parseDate(projectEnd);
  // pad with 7 days on each side
  const padStart = new Date(start); padStart.setDate(padStart.getDate() - 7);
  const padEnd = new Date(end); padEnd.setDate(padEnd.getDate() + 7);
  const totalDays = dayDiff(padStart, padEnd);
  const totalW = totalDays * dayW;

  const ticks = buildTicks(padStart, padEnd, scale);
  const todayX = dayDiff(padStart, TODAY) * dayW;

  // visible row -> y position
  const rowY = (idx) => idx * ROW_H + ROW_H / 2;
  const xOf = (date) => dayDiff(padStart, parseDate(date)) * dayW;
  const wOf = (s, e) => Math.max(dayW, (dayDiff(parseDate(s), parseDate(e)) + 1) * dayW);

  // Build dep line list — only between visible rows
  const visibleSet = new Set(rows.map(r => r.id));
  const depLines = [];
  if (showDeps) {
    rows.forEach((row, idx) => {
      (row.deps || []).forEach(depId => {
        if (!visibleSet.has(depId)) return;
        const fromIdx = rows.findIndex(r => r.id === depId);
        if (fromIdx < 0) return;
        const from = rows[fromIdx];
        const x1 = xOf(from.end) + wOf(from.start, from.end) - dayDiff(parseDate(from.start), parseDate(from.end)) * dayW; // end edge
        const x1End = xOf(from.start) + wOf(from.start, from.end);
        const y1 = rowY(fromIdx);
        const x2 = xOf(row.start);
        const y2 = rowY(idx);
        const isCrit = from.isCritical && row.isCritical;
        depLines.push({ id: `${from.id}->${row.id}`, x1: x1End, y1, x2, y2, isCrit });
      });
    });
  }

  return (
    <div style={{ position: "relative", flex: 1, overflowX: "auto", overflowY: "visible", background: "#fff" }}>
      <div style={{ position: "relative", width: totalW, minHeight: rows.length * ROW_H + 56 }}>

        {/* Date axis header */}
        <div style={{ background: "#fff", borderBottom: "1px solid rgb(227,229,232)", height: 57, boxSizing: "border-box" }}>
          {/* major row (months) */}
          <div style={{ position: "relative", height: 28, borderBottom: "1px solid rgb(239,240,242)", boxSizing: "border-box" }}>
            {(() => {
              // group ticks into months for a major row
              const months = [];
              const cursor = new Date(padStart); cursor.setDate(1);
              while (cursor <= padEnd) {
                const next = new Date(cursor); next.setMonth(next.getMonth() + 1);
                const x = dayDiff(padStart, cursor) * dayW;
                const w = dayDiff(cursor, next < padEnd ? next : padEnd) * dayW;
                months.push({ x, w, label: cursor.toLocaleDateString("en-US", { month: "long", year: "numeric" }), key: cursor.toISOString() });
                cursor.setMonth(cursor.getMonth() + 1);
              }
              return months.map(m => (
                <div key={m.key} style={{
                  position: "absolute", left: m.x, width: m.w, top: 0, bottom: 0,
                  borderRight: "1px solid rgb(239,240,242)",
                  display: "flex", alignItems: "center", paddingLeft: 8,
                  fontSize: 11, fontWeight: 600, color: "rgb(65,69,82)", letterSpacing: 0.2,
                }}>{m.label}</div>
              ));
            })()}
          </div>
          {/* minor row (ticks) */}
          <div style={{ position: "relative", height: 28 }}>
            {ticks.map((t, i) => {
              const x = dayDiff(padStart, t.date) * dayW;
              return (
                <div key={i} style={{
                  position: "absolute", left: x, top: 0, bottom: 0,
                  fontSize: 10, color: t.major ? "rgb(65,69,82)" : "rgb(140,148,162)",
                  fontWeight: t.major ? 600 : 400,
                  paddingLeft: 4, paddingTop: 8,
                  borderLeft: t.major ? "1px solid rgb(213,219,225)" : "1px solid rgb(243,244,247)",
                  width: dayW * (scale === "Day" ? 1 : scale === "Week" ? 7 : 30),
                  boxSizing: "border-box",
                }}>{t.label}</div>
              );
            })}
          </div>
        </div>

        {/* Body */}
        <div style={{ position: "relative" }}>
          {/* Vertical grid lines */}
          {ticks.map((t, i) => {
            const x = dayDiff(padStart, t.date) * dayW;
            return (
              <div key={i} style={{
                position: "absolute", left: x, top: 0, bottom: 0, width: 1,
                background: t.major ? "rgb(227,229,232)" : "rgb(243,244,247)", pointerEvents: "none",
              }}/>
            );
          })}

          {/* Weekend shading (only at Day scale) */}
          {scale === "Day" && ticks.map((t, i) => {
            if (t.date.getDay() !== 0 && t.date.getDay() !== 6) return null;
            const x = dayDiff(padStart, t.date) * dayW;
            return <div key={`we-${i}`} style={{ position: "absolute", left: x, top: 0, bottom: 0, width: dayW, background: "rgba(243,244,247,0.5)", pointerEvents: "none" }}/>;
          })}

          {/* Row backgrounds (alternating + selected) */}
          {rows.map((r, idx) => (
            <div key={r.id}
              onMouseEnter={() => onHover(r.id)}
              onMouseLeave={() => onHover(null)}
              onClick={() => onSelect(r.id)}
              style={{
                position: "absolute", left: 0, right: 0, top: idx * ROW_H, height: ROW_H,
                background: selectedId === r.id ? "rgba(232,233,246,0.6)"
                          : hoveredId === r.id ? "rgba(243,244,247,0.7)"
                          : (r.type === "phase" ? "rgba(247,247,250,0.6)" : "transparent"),
                borderBottom: "1px solid rgb(247,248,250)",
                cursor: "pointer",
              }}
            />
          ))}

          {/* Today line */}
          {todayX >= 0 && todayX <= totalW && (
            <>
              <div style={{ position: "absolute", left: todayX, top: 0, height: rows.length * ROW_H, width: 2,
                background: "rgb(220,72,72)", zIndex: 3 }}/>
              <div style={{ position: "absolute", left: todayX - 22, top: -2, fontSize: 10, fontWeight: 700,
                color: "#fff", background: "rgb(220,72,72)", padding: "2px 6px", borderRadius: 4, zIndex: 4 }}>TODAY</div>
            </>
          )}

          {/* Dependency lines (SVG overlay) */}
          <svg style={{ position: "absolute", left: 0, top: 0, width: totalW, height: rows.length * ROW_H, pointerEvents: "none", zIndex: 2 }}>
            <defs>
              <marker id="depArrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
                <path d="M0,0 L10,5 L0,10 z" fill="rgb(140,148,162)"/>
              </marker>
              <marker id="depArrowCrit" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
                <path d="M0,0 L10,5 L0,10 z" fill="rgb(220,72,72)"/>
              </marker>
            </defs>
            {depLines.map(d => {
              // simple right-angle: out-right, down/up, in-left
              const midX = Math.max(d.x1 + 8, d.x2 - 8);
              const path = `M${d.x1},${d.y1} L${midX},${d.y1} L${midX},${d.y2} L${d.x2 - 2},${d.y2}`;
              const stroke = d.isCrit ? "rgb(220,72,72)" : "rgb(140,148,162)";
              return (
                <path key={d.id} d={path} fill="none" stroke={stroke}
                  strokeWidth={d.isCrit ? 1.5 : 1}
                  strokeDasharray={d.isCrit ? "" : "3,2"}
                  markerEnd={d.isCrit ? "url(#depArrowCrit)" : "url(#depArrow)"}
                  opacity={0.85}/>
              );
            })}
          </svg>

          {/* Bars */}
          {rows.map((row, idx) => {
            const x = xOf(row.start);
            const w = wOf(row.start, row.end);
            const y = idx * ROW_H + (ROW_H - 18) / 2;
            const tone = STATUS_TONES[row.status] || STATUS_TONES["Not Started"];
            const dim = criticalOnly && !row.isCritical;

            if (row.type === "phase") {
              const phaseY = idx * ROW_H + 6;
              return (
                <div key={row.id} style={{
                  position: "absolute", left: x, top: phaseY, width: w, height: 24,
                  pointerEvents: "none", opacity: dim ? 0.25 : 1,
                }}>
                  <div style={{
                    position: "absolute", inset: 0, background: "rgb(38,40,52)", borderRadius: 3,
                    boxShadow: "0 1px 0 rgba(0,0,0,0.04)",
                  }}/>
                  {/* End caps */}
                  <div style={{ position: "absolute", left: -3, top: 0, width: 6, height: 24, background: "rgb(38,40,52)",
                    clipPath: "polygon(50% 0, 100% 0, 100% 100%, 50% 100%, 0 50%)"}}/>
                  <div style={{ position: "absolute", right: -3, top: 0, width: 6, height: 24, background: "rgb(38,40,52)",
                    clipPath: "polygon(0 0, 50% 0, 100% 50%, 50% 100%, 0 100%)"}}/>
                  {/* progress fill */}
                  <div style={{
                    position: "absolute", left: 0, top: 0, height: 24, width: `${row.pct}%`,
                    background: "rgb(83,74,255)", borderRadius: 3, opacity: 0.55,
                  }}/>
                  <div style={{
                    position: "absolute", left: 8, top: 0, height: 24, display: "flex", alignItems: "center",
                    fontSize: 11, fontWeight: 600, color: "#fff", letterSpacing: 0.2, whiteSpace: "nowrap",
                  }}>{row.pct}%</div>
                </div>
              );
            }

            if (row.type === "scope") {
              return (
                <div key={row.id} style={{
                  position: "absolute", left: x, top: y, width: w, height: 18,
                  opacity: dim ? 0.25 : 1, pointerEvents: "none",
                }}>
                  <div style={{
                    position: "absolute", inset: 0,
                    background: tone.bg, border: `1px solid ${tone.bar}`,
                    borderRadius: 5,
                  }}/>
                  <div style={{
                    position: "absolute", left: 0, top: 0, height: "100%", width: `${row.pct}%`,
                    background: tone.bar, borderRadius: "5px 0 0 5px",
                  }}/>
                  {w > 60 && (
                    <div style={{ position: "absolute", left: 8, top: 0, height: "100%", display: "flex", alignItems: "center",
                      fontSize: 10, fontWeight: 600, color: row.pct > 30 ? "#fff" : tone.fg, mixBlendMode: row.pct > 30 ? "normal" : "normal" }}>
                      {row.pct}%
                    </div>
                  )}
                </div>
              );
            }

            // Task bar
            const isHover = hoveredId === row.id;
            const isSel = selectedId === row.id;
            return (
              <div key={row.id}
                onMouseEnter={() => onHover(row.id)}
                onMouseLeave={() => onHover(null)}
                onClick={(e) => { e.stopPropagation(); onSelect(row.id); }}
                style={{
                  position: "absolute", left: x, top: y + 1, width: w, height: 16,
                  opacity: dim ? 0.25 : 1, cursor: "pointer", zIndex: isHover ? 20 : isSel ? 3 : 1,
                }}>
                <div style={{
                  position: "absolute", inset: 0,
                  background: tone.bg, border: `1px solid ${tone.bar}`,
                  borderRadius: 4,
                  boxShadow: row.isCritical ? `0 0 0 1px rgb(220,72,72)` : (isSel ? `0 0 0 2px rgb(83,74,255)` : "none"),
                }}/>
                <div style={{
                  position: "absolute", left: 0, top: 0, height: "100%", width: `${row.pct}%`,
                  background: tone.bar, borderRadius: "3px 0 0 3px",
                }}/>
                {w > 50 && (
                  <div style={{ position: "absolute", left: 6, top: 0, height: "100%", display: "flex", alignItems: "center",
                    fontSize: 10, fontWeight: 600, color: row.pct > 35 ? "#fff" : tone.fg, whiteSpace: "nowrap", maxWidth: w - 12, overflow: "hidden", textOverflow: "ellipsis" }}>
                    {row.pct}%
                  </div>
                )}
                {/* drag handles on hover/select */}
                {(isHover || isSel) && (
                  <>
                    <div style={{ position: "absolute", left: -3, top: 2, width: 4, height: 12, borderRadius: 2, background: "rgb(83,74,255)", cursor: "ew-resize" }}/>
                    <div style={{ position: "absolute", right: -3, top: 2, width: 4, height: 12, borderRadius: 2, background: "rgb(83,74,255)", cursor: "ew-resize" }}/>
                  </>
                )}
                {/* tooltip */}
                {isHover && (
                  <div style={{
                    position: "absolute", left: 0, top: 22, zIndex: 10,
                    background: "rgb(26,27,37)", color: "#fff", padding: "8px 10px",
                    borderRadius: 6, fontSize: 11, lineHeight: 1.5, whiteSpace: "nowrap",
                    boxShadow: "0 6px 20px rgba(0,0,0,0.18)",
                  }}>
                    <div style={{ fontWeight: 600, marginBottom: 2 }}>{row.name}</div>
                    <div style={{ opacity: 0.7 }}>{row.start} → {row.end} · {row.pct}% · {row.status}</div>
                    {row.isCritical && <div style={{ color: "rgb(255,140,140)", marginTop: 2, fontWeight: 600 }}>● Critical path</div>}
                  </div>
                )}
              </div>
            );
          })}

          {/* Milestone diamonds — drawn at top above all rows */}
          {WBS_MILESTONES.map(m => {
            const mx = xOf(m.date);
            const tone = STATUS_TONES[m.tone === "success" ? "Complete" : m.tone === "warning" ? "At Risk" : "Not Started"];
            return (
              <div key={m.id} title={m.label} style={{
                position: "absolute", left: mx - 7, top: -8, width: 14, height: 14,
                background: m.tone === "success" ? "rgb(34,134,58)" : m.tone === "warning" ? "rgb(245,166,35)" : "rgb(140,148,162)",
                transform: "rotate(45deg)", border: "2px solid #fff", boxShadow: "0 1px 3px rgba(0,0,0,0.2)", zIndex: 6,
              }}/>
            );
          })}

          {/* Spacer to give body height */}
          <div style={{ height: rows.length * ROW_H }}/>
        </div>
      </div>
    </div>
  );
};

Object.assign(window, { GanttChart, ROW_H, TODAY });
