// COACH — rule engine + UI for intelligent recommendations and replay
// All purely derived from sets/athletes — no extra DB.

const COACH_ZONE_MAP = {
  chest: "PUSH", front_delt: "PUSH", side_delt: "PUSH", triceps: "PUSH",
  upper_back: "PULL", lats: "PULL", rear_delt: "PULL", traps: "PULL", biceps: "PULL", forearms: "PULL",
  quads: "LEGS", glutes: "LEGS", hamstrings: "LEGS", calves: "LEGS", adductors: "LEGS",
  abs: "CORE", obliques: "CORE", lower_back: "CORE",
};
const ZONE_COLORS = { PUSH: "#FF5B3A", PULL: "#3AA8FF", LEGS: "#FFB730", CORE: "#9D6BFF" };

const SEVERITY_ORDER = { alert: 4, warn: 3, cheer: 2, tip: 1 };
const SEVERITY_COLORS = {
  alert: "#FF2D6F",
  warn:  "#FFB800",
  cheer: "#D4FF3F",
  tip:   "#3AA8FF",
};
const SEVERITY_LABEL = {
  alert: "ALERT",
  warn:  "FOCUS",
  cheer: "PR READY",
  tip:   "TIP",
};

// ===== HELPERS =====

const dateLabel = (date, today) => {
  const d = new Date(date);
  const t = new Date(today);
  const diffDays = Math.round((t - d) / 86400000);
  if (diffDays === 0)  return "TODAY";
  if (diffDays === 1)  return "YESTERDAY";
  if (diffDays < 7)    return d.toLocaleDateString("en-US", { weekday: "long" }).toUpperCase();
  if (diffDays < 14)   return "LAST " + d.toLocaleDateString("en-US", { weekday: "short" }).toUpperCase();
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric" }).toUpperCase();
};

const exZones = (ex) => {
  const out = new Set();
  (ex.primary || []).forEach(m => { const z = COACH_ZONE_MAP[m]; if (z) out.add(z); });
  return [...out];
};

const computeZoneVolume = (sets, athleteId, days, today, exercises, bodyweight) => {
  const cutoff = new Date(today);
  cutoff.setDate(cutoff.getDate() - days);
  const cutoffStr = cutoff.toISOString().slice(0, 10);
  const relevant = sets.filter(s => s.athlete === athleteId && s.date >= cutoffStr);
  const zones = { PUSH: 0, PULL: 0, LEGS: 0, CORE: 0 };
  for (const s of relevant) {
    const ex = exercises.find(e => e.id === s.exercise);
    if (!ex) continue;
    const w = s.weight || bodyweight || 70;
    const vol = w * s.reps;
    exZones(ex).forEach(z => { zones[z] += vol; });
  }
  return zones;
};

// Group sets into workout sessions (by date)
const groupWorkouts = (sets, athleteId, today, days = 14) => {
  const cutoff = new Date(today);
  cutoff.setDate(cutoff.getDate() - days);
  const cutoffStr = cutoff.toISOString().slice(0, 10);
  const mine = sets.filter(s => s.athlete === athleteId && s.date >= cutoffStr);
  const byDate = {};
  for (const s of mine) {
    if (!byDate[s.date]) byDate[s.date] = [];
    byDate[s.date].push(s);
  }
  const dates = Object.keys(byDate).sort().reverse();
  return dates.map(date => {
    const daySets = byDate[date];
    const exMap = {};
    let totalVolume = 0;
    for (const s of daySets) {
      const w = s.weight || 0;
      totalVolume += w * s.reps;
      if (!exMap[s.exercise]) exMap[s.exercise] = { id: s.exercise, sets: [], topWeight: 0, totalReps: 0 };
      exMap[s.exercise].sets.push(s);
      exMap[s.exercise].topWeight = Math.max(exMap[s.exercise].topWeight, w);
      exMap[s.exercise].totalReps += s.reps;
    }
    const exercises = Object.values(exMap);
    return {
      date,
      label: dateLabel(date, today),
      totalVolume,
      sets: daySets,
      exercises,
      uniqueExerciseCount: exercises.length,
      totalSets: daySets.length,
    };
  });
};

const computeStreak = (sets, athleteId, today) => {
  const dates = new Set(sets.filter(s => s.athlete === athleteId).map(s => s.date));
  let days = 0;
  let cursor = new Date(today);
  const todayDone = dates.has(today);
  if (todayDone) { days++; cursor.setDate(cursor.getDate() - 1); }
  else cursor.setDate(cursor.getDate() - 1);
  while (true) {
    const ds = cursor.toISOString().slice(0, 10);
    if (dates.has(ds)) { days++; cursor.setDate(cursor.getDate() - 1); }
    else break;
    if (days > 365) break; // safety
  }
  return { days, todayDone };
};

// PR Ready: lifts where last 2-3 sessions show non-decreasing top weight
const computePRReady = (sets, athleteId, exercises) => {
  const mine = sets.filter(s => s.athlete === athleteId && s.weight > 0);
  const byEx = {};
  for (const s of mine) {
    if (!byEx[s.exercise]) byEx[s.exercise] = [];
    byEx[s.exercise].push(s);
  }
  const candidates = [];
  for (const [exId, exSets] of Object.entries(byEx)) {
    const byDate = {};
    for (const s of exSets) byDate[s.date] = Math.max(byDate[s.date] || 0, s.weight);
    const sortedDates = Object.keys(byDate).sort().reverse();
    if (sortedDates.length < 2) continue;
    const recent = sortedDates.slice(0, 3).map(d => byDate[d]);
    const lastTop = recent[0];
    if (lastTop <= 0) continue;
    // last 2 sessions had same or progressing top weight
    const isReady = recent.length >= 2 && recent[0] >= recent[1] * 0.95;
    if (isReady) {
      const ex = exercises.find(e => e.id === exId);
      if (!ex) continue;
      const isLower = exZones(ex).includes("LEGS");
      const target = lastTop + (isLower ? 2.5 : 1.25);
      candidates.push({
        exerciseId: exId,
        exerciseName: ex.name,
        currentBest: lastTop,
        target,
        history: recent.map(w => `${w}kg`).reverse().join(" → "),
      });
    }
  }
  // Pick the most-recent candidate (sorted by exercise's last session date)
  candidates.sort((a, b) => b.currentBest - a.currentBest);
  return candidates[0] || null;
};

const computeHoursSinceZone = (sets, athleteId, exercises) => {
  const result = {};
  const mine = sets.filter(s => s.athlete === athleteId);
  for (const s of mine) {
    const ex = exercises.find(e => e.id === s.exercise);
    if (!ex) continue;
    const sMs = Date.parse(s.date + "T" + (s.time || "00:00") + ":00");
    if (isNaN(sMs)) continue;
    const hoursAgo = (Date.now() - sMs) / 3600000;
    if (hoursAgo < 0) continue;
    exZones(ex).forEach(z => {
      if (!(z in result) || hoursAgo < result[z]) result[z] = hoursAgo;
    });
  }
  return result;
};

const exerciseSuggestionsForZone = (zone, exercises) => {
  return exercises.filter(ex => exZones(ex).includes(zone)).slice(0, 5);
};

// ===== INSIGHT ENGINE =====
const computeInsights = ({ sets, whoami, athletes, today, exercises }) => {
  const me = athletes[whoami];
  if (!me || me.placeholder) return [];

  const insights = [];
  const mySets = sets.filter(s => s.athlete === whoami);

  // 1. STREAK STATUS
  const streak = computeStreak(sets, whoami, today);
  if (streak.days >= 3 && !streak.todayDone) {
    insights.push({
      id: "streak-at-risk",
      severity: "warn",
      icon: "🔥",
      headline: `${streak.days}-DAY STREAK ON THE LINE`,
      detail: `You haven't logged today. Don't break the chain.`,
    });
  } else if (streak.days >= 7 && streak.todayDone) {
    insights.push({
      id: "streak-active",
      severity: "cheer",
      icon: "🔥",
      headline: `DAY ${streak.days} STREAK · 🔒 LOCKED IN`,
      detail: `Today's session keeps the chain alive.`,
    });
  }

  // 2. IMBALANCE — weakest zone is <30% of dominant zone
  const zones = computeZoneVolume(sets, whoami, 7, today, exercises, me.bodyweight);
  const sorted = Object.entries(zones).sort((a, b) => b[1] - a[1]);
  const top = sorted[0];
  const last = sorted[sorted.length - 1];
  if (top[1] > 0 && (last[1] === 0 || last[1] < top[1] * 0.3)) {
    const suggestions = exerciseSuggestionsForZone(last[0], exercises);
    const detail = last[1] === 0
      ? `0 kg of ${last[0]} this week vs ${top[0]} ${(top[1]/1000).toFixed(1)}T. Try ${suggestions.slice(0,2).map(e => e.name).join(" or ")}.`
      : `${(last[1]/1000).toFixed(1)}T vs ${top[0]} ${(top[1]/1000).toFixed(1)}T (${Math.round(last[1]/top[1]*100)}%). Add ${suggestions[0]?.name}.`;
    insights.push({
      id: `imbalance-${last[0].toLowerCase()}`,
      severity: "warn",
      icon: "⚠",
      headline: `${last[0]} IS LIGHT THIS WEEK`,
      detail,
      action: suggestions[0] ? { type: "exercise", exerciseId: suggestions[0].id, label: `LOG ${suggestions[0].name.toUpperCase()}` } : null,
      zone: last[0],
    });
  }

  // 3. PR READY
  const prReady = computePRReady(mySets, whoami, exercises);
  if (prReady) {
    insights.push({
      id: `pr-ready-${prReady.exerciseId}`,
      severity: "cheer",
      icon: "🎯",
      headline: `${prReady.exerciseName.toUpperCase()} → ${prReady.target}KG`,
      detail: `${prReady.history}. Add a plate, push for it.`,
      action: { type: "exercise", exerciseId: prReady.exerciseId, weight: prReady.target, reps: 5, label: `OPEN ${prReady.exerciseName.toUpperCase()}` },
    });
  }

  // 4. FAMILY PR ALERT — someone else just PR'd today
  const recentFamilyPR = sets
    .filter(s => s.athlete !== whoami && s.isPR && s.date === today)
    .sort((a, b) => (b.time || "").localeCompare(a.time || ""))[0];
  if (recentFamilyPR) {
    const them = athletes[recentFamilyPR.athlete];
    const ex = exercises.find(e => e.id === recentFamilyPR.exercise);
    if (them && ex) {
      insights.push({
        id: `family-pr-${recentFamilyPR.id || recentFamilyPR.time}`,
        severity: "alert",
        icon: "👀",
        headline: `${them.short} JUST PR'D`,
        detail: `${ex.name} ${recentFamilyPR.weight}kg × ${recentFamilyPR.reps}. Your move.`,
      });
    }
  }

  // 5. RECOVERY READY — zone untouched 48-168 hrs
  const hoursSince = computeHoursSinceZone(sets, whoami, exercises);
  const fresh = Object.entries(hoursSince).filter(([z, h]) => h >= 48 && h < 168).sort((a, b) => b[1] - a[1]);
  if (fresh.length > 0 && !insights.some(i => i.severity === "warn" && i.zone === fresh[0][0])) {
    const [zone, hours] = fresh[0];
    const suggestions = exerciseSuggestionsForZone(zone, exercises);
    insights.push({
      id: `recovery-${zone}`,
      severity: "tip",
      icon: "💪",
      headline: `${zone} IS FRESH`,
      detail: `Last hit ${Math.round(hours/24)}d ago. ${suggestions[0]?.name || "Train it"} is fully recovered.`,
      action: suggestions[0] ? { type: "exercise", exerciseId: suggestions[0].id, label: `START ${suggestions[0].name.toUpperCase()}` } : null,
      zone,
    });
  }

  // 6. WARMUP REMINDER — only if user opens a heavy lift cold
  // (handled in picker, not here)

  return insights
    .sort((a, b) => SEVERITY_ORDER[b.severity] - SEVERITY_ORDER[a.severity])
    .slice(0, 4);
};

// ===== UI: COACH CARDS =====
const CoachCard = ({ insights, onAction }) => {
  if (!insights || insights.length === 0) return null;
  return (
    <div className="cc-strip">
      <div className="cc-head">
        <div className="cc-head-l">▮ COACH</div>
        <div className="cc-head-r">{insights.length} insight{insights.length > 1 ? "s" : ""} · live</div>
      </div>
      <div className="cc-scroll">
        {insights.map(i => (
          <div key={i.id} className={`cc-card cc-card--${i.severity}`} style={{ "--cc": SEVERITY_COLORS[i.severity] }}>
            <div className="cc-card-top">
              <span className="cc-card-icon">{i.icon}</span>
              <span className="cc-card-tag">{SEVERITY_LABEL[i.severity]}</span>
            </div>
            <div className="cc-card-headline">{i.headline}</div>
            <div className="cc-card-detail">{i.detail}</div>
            {i.action && (
              <button className="cc-card-action" onClick={() => onAction(i.action)}>
                {i.action.label} ▸
              </button>
            )}
          </div>
        ))}
      </div>
    </div>
  );
};

// ===== UI: RECENT WORKOUTS =====
const RecentWorkouts = ({ sets, athleteId, today, exercises, athletes, onReplaySet }) => {
  const workouts = React.useMemo(
    () => groupWorkouts(sets, athleteId, today, 14),
    [sets, athleteId, today]
  );
  const [expanded, setExpanded] = React.useState(null);

  const me = athletes[athleteId];
  if (!me || me.placeholder) return null;
  if (workouts.length === 0) {
    return (
      <div className="rw-strip rw-strip--empty">
        <div className="rw-empty">
          <div className="rw-empty-icon">▮</div>
          <div className="rw-empty-text">No history yet — tap any lift below to log your first set.</div>
        </div>
      </div>
    );
  }

  const nextDay = workouts[0]?.exercises[0];

  return (
    <>
      <div className="rw-strip">
        <div className="rw-head">
          <div className="rw-head-l">▮ RECENT WORKOUTS</div>
          <div className="rw-head-r">{workouts.length} session{workouts.length > 1 ? "s" : ""} · last 14 days · tap to replay</div>
        </div>
        <div className="rw-scroll">
          {workouts.map(w => {
            const topZones = (() => {
              const z = { PUSH: 0, PULL: 0, LEGS: 0, CORE: 0 };
              w.sets.forEach(s => {
                const ex = exercises.find(e => e.id === s.exercise);
                if (!ex) return;
                exZones(ex).forEach(zo => { z[zo] += (s.weight || me.bodyweight || 70) * s.reps; });
              });
              return Object.entries(z).filter(([k, v]) => v > 0).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([k]) => k);
            })();
            return (
              <button key={w.date} className="rw-card" style={{ "--c": me.color }}
                onClick={() => setExpanded(w)}>
                <div className="rw-card-top">
                  <div className="rw-card-day">{w.label}</div>
                  <div className="rw-card-date">{w.date.slice(5)}</div>
                </div>
                <div className="rw-card-zones">
                  {topZones.length > 0
                    ? topZones.map(z => (
                      <span key={z} className="rw-card-zone" style={{ background: ZONE_COLORS[z] }}>{z}</span>
                    ))
                    : <span className="rw-card-zone rw-card-zone--bw">BODYWEIGHT</span>
                  }
                </div>
                <div className="rw-card-stats">
                  <div className="rw-card-stat">
                    <span className="rw-card-stat-v">{w.uniqueExerciseCount}</span>
                    <span className="rw-card-stat-l">lifts</span>
                  </div>
                  <div className="rw-card-stat">
                    <span className="rw-card-stat-v">{w.totalSets}</span>
                    <span className="rw-card-stat-l">sets</span>
                  </div>
                  <div className="rw-card-stat">
                    <span className="rw-card-stat-v rw-card-stat-v--accent" style={{ color: me.color }}>
                      {w.totalVolume === 0 ? "BW" : `${(w.totalVolume/1000).toFixed(1)}T`}
                    </span>
                    <span className="rw-card-stat-l">volume</span>
                  </div>
                </div>
                <div className="rw-card-cta">REPLAY ▸</div>
              </button>
            );
          })}
        </div>
      </div>

      {expanded && ReactDOM.createPortal(
        <div className="rw-modal" onClick={() => setExpanded(null)}>
          <div className="rw-detail" onClick={e => e.stopPropagation()} style={{ "--c": me.color }}>
            <div className="rw-detail-head">
              <div>
                <div className="rw-detail-day">{expanded.label}</div>
                <div className="rw-detail-date">{expanded.date}</div>
              </div>
              <button className="rw-detail-close" onClick={() => setExpanded(null)} aria-label="Close">×</button>
            </div>
            <div className="rw-detail-stats">
              <div className="rw-detail-stat"><div className="rw-detail-stat-v">{expanded.uniqueExerciseCount}</div><div className="rw-detail-stat-l">EXERCISES</div></div>
              <div className="rw-detail-stat"><div className="rw-detail-stat-v">{expanded.totalSets}</div><div className="rw-detail-stat-l">TOTAL SETS</div></div>
              <div className="rw-detail-stat"><div className="rw-detail-stat-v" style={{color: me.color}}>{expanded.totalVolume === 0 ? "BW" : `${(expanded.totalVolume/1000).toFixed(1)}T`}</div><div className="rw-detail-stat-l">VOLUME</div></div>
            </div>
            <div className="rw-detail-list">
              <div className="rw-detail-list-head">EXERCISES — TAP REPLAY TO PRE-FILL</div>
              {expanded.exercises.map(exGroup => {
                const ex = exercises.find(e => e.id === exGroup.id);
                if (!ex) return null;
                const last = exGroup.sets[exGroup.sets.length - 1];
                return (
                  <div key={exGroup.id} className="rw-ex">
                    <div className="rw-ex-head">
                      <div className="rw-ex-name">{ex.name}</div>
                      <button className="rw-ex-replay" onClick={() => {
                        onReplaySet(ex, last.weight, last.reps);
                        setExpanded(null);
                      }} style={{ "--c": me.color }}>
                        REPLAY ▸
                      </button>
                    </div>
                    <div className="rw-ex-sets">
                      {exGroup.sets.map((s, i) => (
                        <span key={i} className={`rw-ex-chip ${s.isPR ? "rw-ex-chip--pr" : ""}`}>
                          {s.weight ? `${s.weight}×${s.reps}` : `BW×${s.reps}`}
                          {s.isPR && <span className="rw-ex-pr">PR</span>}
                        </span>
                      ))}
                    </div>
                  </div>
                );
              })}
            </div>
            <div className="rw-detail-foot">
              {expanded.exercises[0] && (
                <button className="rw-replay-all" style={{ "--c": me.color }}
                  onClick={() => {
                    const ex = exercises.find(e => e.id === expanded.exercises[0].id);
                    if (ex) {
                      const last = expanded.exercises[0].sets[expanded.exercises[0].sets.length - 1];
                      onReplaySet(ex, last.weight, last.reps);
                      setExpanded(null);
                    }
                  }}>
                  ▸ START FROM TOP — {exercises.find(e => e.id === expanded.exercises[0].id)?.name?.toUpperCase()}
                </button>
              )}
            </div>
          </div>
        </div>,
        document.body
      )}
    </>
  );
};

// Expose helpers globally so tracker.jsx can use them
window.coach = {
  computeInsights,
  computePRReady,
  groupWorkouts,
  computeStreak,
  computeZoneVolume,
};
