/**
 * Team progress report — visible to the admin only.
 *
 * Reads every learner document from Firestore. The read is refused server-side
 * for anyone who is not on the admin list with a verified email
 * (firestore.rules), so hiding this screen in the UI is a convenience, not the
 * protection.
 *
 * All figures are computed from the current curriculum, so a lesson that was
 * removed from the course no longer counts towards anyone's percentage.
 */

(function () {
'use strict';

const { useState, useEffect, useMemo } = React;

const DAY = 24 * 60 * 60 * 1000;

function curriculumIndex(regions) {
  const lessons = [];
  regions.forEach((region) => {
    region.lessons.forEach((lesson) => {
      lessons.push({
        region,
        lesson,
        hasQuiz: lesson.steps.some((s) => s.type === 'quiz')
      });
    });
  });
  return lessons;
}

/** Turns one learner document into the numbers the report shows. Pure. */
function summarize(learner, regions, index) {
  const p = window.Progress.merge(learner.progress || {}, {});
  const done = index.filter((x) => p.completedLessons[x.lesson.id]).length;

  const quizLessons = index.filter((x) => x.hasQuiz);
  const quizzes = quizLessons.map((x) => ({ ...x, score: p.quizScores[x.lesson.id] || null }));
  const quizzesPassed = quizzes.filter((q) => q.score && (q.score.passedEver || q.score.passed)).length;
  const quizzesTried = quizzes.filter((q) => q.score).length;

  // Only find exercises that are still in the course count — the same rule as
  // lessons and quizzes. The key matches the one FindStep records under.
  const liveFindKeys = new Set();
  index.forEach((x) => x.lesson.steps.forEach((s) => {
    if (s.type === 'find') liveFindKeys.add(`${s.seriesId}:${(s.findingLabels || []).join('+')}`);
  }));
  const finds = Object.entries(p.findResults)
    .filter(([key]) => liveFindKeys.has(key))
    .map(([, v]) => v);
  const findsCorrect = finds.filter((f) => f.correct).length;

  const perRegion = regions.map((region) => ({
    region,
    ...window.Progress.regionProgress(region, p)
  }));
  const regionsDone = perRegion.filter((r) => r.ratio >= 1).length;

  // The lesson they stopped in most recently and have not finished.
  let current = null;
  Object.entries(p.lessonState).forEach(([id, s]) => {
    if (p.completedLessons[id]) return;
    if (!current || (s.at || 0) > (current.at || 0)) current = { id, ...s };
  });
  const currentFound = current ? window.Curriculum.findLesson(current.id) : null;

  const timestamps = [
    learner.updatedAt || 0,
    ...Object.values(p.completedLessons).map((v) => (typeof v === 'number' ? v : 0)),
    ...Object.values(p.lessonState).map((s) => s.at || 0)
  ];
  const lastActive = Math.max(0, ...timestamps);

  return {
    uid: learner.uid,
    name: learner.displayName || '(ללא שם)',
    email: learner.email || '',
    createdAt: learner.createdAt || 0,
    lastActive,
    done,
    total: index.length,
    ratio: index.length ? done / index.length : 0,
    regionsDone,
    perRegion,
    quizzes,
    quizzesPassed,
    quizzesTried,
    quizTotal: quizLessons.length,
    findsCorrect,
    findsTotal: finds.length,
    current: currentFound
      ? {
          title: currentFound.lesson.title,
          region: currentFound.region.title,
          step: (current.cursor || 0) + 1,
          steps: currentFound.lesson.steps.length
        }
      : null
  };
}

/**
 * Firestore rules check only that `progress` is a map, so a learner's
 * document can hold malformed nested values. One such row must not take the
 * whole report down: it is shown with its name and an empty progress row,
 * flagged as damaged.
 */
function summarizeSafe(learner, regions, index) {
  try {
    return summarize(learner, regions, index);
  } catch (err) {
    console.warn('[admin] could not read progress for', learner && learner.uid, err);
    return { ...summarize({ ...learner, progress: {} }, regions, index), corrupt: true };
  }
}

function formatDate(ms) {
  if (!ms) return '—';
  return new Date(ms).toLocaleString('he-IL', { dateStyle: 'short', timeStyle: 'short' });
}

function relative(ms) {
  if (!ms) return 'אף פעם';
  const days = Math.floor((Date.now() - ms) / DAY);
  if (days <= 0) return 'היום';
  if (days === 1) return 'אתמול';
  if (days < 30) return `לפני ${days} ימים`;
  return formatDate(ms);
}

function pct(r) {
  return `${Math.round(r * 100)}%`;
}

function csvCell(v) {
  let s = String(v ?? '');
  // Display names are typed by learners. A cell starting with = + - @ (or a
  // tab / carriage return) is run as a formula by Excel, so a name such as
  // =HYPERLINK(...) would execute on the admin's machine. A leading
  // apostrophe makes Excel treat it as plain text.
  if (/^[=+\-@\t\r]/.test(s)) s = `'${s}`;
  return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
}

function exportCsv(rows, regions) {
  const head = [
    'שם', 'אימייל', 'פעילות אחרונה', 'שיעורים שהושלמו', 'סה"כ שיעורים', 'אחוז השלמה',
    'מודולים שהושלמו', 'מבחנים שעברו', 'מבחנים בסה"כ', 'איתור נכון', 'תרגילי איתור שבוצעו', 'עוצר כעת ב',
    ...regions.map((r) => `${r.title} (הושלמו)`)
  ];
  const lines = rows.map((r) => [
    r.name, r.email, formatDate(r.lastActive), r.done, r.total, pct(r.ratio),
    r.regionsDone, r.quizzesPassed, r.quizTotal, r.findsCorrect, r.findsTotal,
    r.current ? `${r.current.region} / ${r.current.title} (שלב ${r.current.step})` : '',
    ...r.perRegion.map((x) => `${x.done}/${x.total}`)
  ]);
  // The BOM makes Excel open the Hebrew as UTF-8 instead of mojibake.
  const csv = '﻿' + [head, ...lines].map((l) => l.map(csvCell).join(',')).join('\r\n');
  const url = URL.createObjectURL(new Blob([csv], { type: 'text/csv;charset=utf-8' }));
  const a = document.createElement('a');
  a.href = url;
  a.download = `progress-report-${new Date().toISOString().slice(0, 10)}.csv`;
  document.body.appendChild(a);
  a.click();
  a.remove();
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

function Bar({ ratio }) {
  return (
    <span className="meter" aria-hidden="true">
      <span className="meter__fill" style={{ width: pct(ratio) }} />
    </span>
  );
}

function LearnerDetail({ row, onClose }) {
  return (
    <div className="card animate-in" style={{ marginTop: 'var(--space-4)' }}>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 'var(--space-3)', flexWrap: 'wrap' }}>
        <h2 style={{ fontSize: 'var(--text-md)', flex: 1 }}>
          {row.name} <span style={{ color: 'var(--text-muted)', fontWeight: 400 }} dir="ltr">{row.email}</span>
        </h2>
        <button type="button" className="btn btn--ghost btn--sm" onClick={onClose}>סגירה ✕</button>
      </div>
      <p style={{ fontSize: 'var(--text-xs)', color: 'var(--text-muted)', margin: 'var(--space-2) 0 var(--space-4)' }}>
        נרשם/ה: {formatDate(row.createdAt)} · פעילות אחרונה: {formatDate(row.lastActive)}
        {row.current && <> · עוצר/ת כעת ב: {row.current.region} / {row.current.title} (שלב {row.current.step} מתוך {row.current.steps})</>}
      </p>

      <div className="table-scroll">
        <table className="data-table report-table">
          <thead>
            <tr>
              <th scope="col">מודול</th>
              <th scope="col">שיעורים</th>
              <th scope="col">התקדמות</th>
            </tr>
          </thead>
          <tbody>
            {row.perRegion.map((r) => (
              <tr key={r.region.id}>
                <td>{r.region.icon} {r.region.title}</td>
                <td>{r.done}/{r.total}</td>
                <td><Bar ratio={r.ratio} /> {pct(r.ratio)}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <h3 style={{ fontSize: 'var(--text-sm)', margin: 'var(--space-5) 0 var(--space-3)' }}>מבחני מודול</h3>
      <div className="table-scroll">
        <table className="data-table report-table">
          <thead>
            <tr>
              <th scope="col">מבחן</th>
              <th scope="col">ניסיון אחרון</th>
              <th scope="col">ציון טוב ביותר</th>
              <th scope="col">ניסיונות</th>
              <th scope="col">סטטוס</th>
            </tr>
          </thead>
          <tbody>
            {row.quizzes.map((q) => {
              const s = q.score;
              const passed = s && (s.passedEver || s.passed);
              return (
                <tr key={q.lesson.id}>
                  <td>{q.region.title} / {q.lesson.title}</td>
                  <td>{s ? `${s.correct}/${s.total}` : '—'}</td>
                  <td>{s ? `${s.best ?? s.correct}/${s.total}` : '—'}</td>
                  <td>{s ? s.attempts || 1 : 0}</td>
                  <td>
                    {!s ? (
                      <span className="chip chip--neutral">לא נוסה</span>
                    ) : passed ? (
                      <span className="chip chip--normal">עבר</span>
                    ) : (
                      <span className="chip chip--disease">לא עבר</span>
                    )}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function VerifyGate({ user, onVerified }) {
  const [busy, setBusy] = useState(false);
  const [msg, setMsg] = useState('');

  async function resend() {
    setBusy(true);
    try {
      await window.Cloud.sendVerification();
      setMsg(`נשלח מייל אימות אל ${user.email}.`);
    } catch (err) {
      setMsg(window.Cloud.message(err));
    } finally {
      setBusy(false);
    }
  }

  async function check() {
    setBusy(true);
    try {
      const fresh = await window.Cloud.refreshUser();
      if (fresh && fresh.emailVerified) onVerified(fresh);
      else setMsg('האימייל עדיין לא אומת. לחץ על הקישור במייל ונסה שוב.');
    } catch (err) {
      setMsg(window.Cloud.message(err));
    } finally {
      setBusy(false);
    }
  }

  return (
    <div className="card" style={{ maxWidth: 560 }}>
      <h2 style={{ fontSize: 'var(--text-md)', marginBottom: 'var(--space-3)' }}>נדרש אימות אימייל</h2>
      <p className="prose" style={{ fontSize: 'var(--text-sm)' }}>
        דוח הצוות פתוח רק לחשבון המנהל, ורק אחרי שהוכח שהאימייל שלו. אחרת כל אחד יכול היה
        להירשם עם הכתובת שלך ולקרוא את נתוני העובדים.
      </p>
      <div style={{ display: 'flex', gap: 'var(--space-3)', marginTop: 'var(--space-4)', flexWrap: 'wrap' }}>
        <button type="button" className="btn btn--primary btn--sm" onClick={check} disabled={busy}>אימתתי — בדוק שוב</button>
        <button type="button" className="btn btn--secondary btn--sm" onClick={resend} disabled={busy}>שלח מייל אימות</button>
      </div>
      {msg && <p style={{ marginTop: 'var(--space-3)', fontSize: 'var(--text-sm)' }}>{msg}</p>}
    </div>
  );
}

function AdminReport({ user, regions, onUserChange, onExit }) {
  const [rows, setRows] = useState(null);
  const [error, setError] = useState('');
  const [query, setQuery] = useState('');
  const [sort, setSort] = useState('lastActive');
  const [selected, setSelected] = useState(null);
  const [reloadTick, setReloadTick] = useState(0);

  const isAdmin = window.Cloud.isAdmin(user);
  const index = useMemo(() => curriculumIndex(regions), [regions]);

  useEffect(() => {
    if (!isAdmin) return;
    let cancelled = false;
    setError('');
    window.Cloud.listLearners()
      .then((learners) => {
        if (cancelled) return;
        // The admin's own account is a reviewer, not a learner: review mode
        // walks lessons without answering, so counting it would skew the
        // team's averages.
        setRows(
          learners
            .filter((l) => !window.Cloud.isAdminEmail(l.email))
            .map((l) => summarizeSafe(l, regions, index))
        );
      })
      .catch((err) => !cancelled && setError(window.Cloud.message(err)));
    return () => { cancelled = true; };
  }, [isAdmin, regions, index, reloadTick]);

  const shown = useMemo(() => {
    if (!rows) return [];
    const q = query.trim().toLowerCase();
    const filtered = q
      ? rows.filter((r) => r.name.toLowerCase().includes(q) || r.email.includes(q))
      : rows;
    const by = {
      lastActive: (a, b) => b.lastActive - a.lastActive,
      progress: (a, b) => b.ratio - a.ratio,
      name: (a, b) => a.name.localeCompare(b.name, 'he')
    }[sort];
    return [...filtered].sort(by);
  }, [rows, query, sort]);

  if (!isAdmin) {
    return (
      <div className="animate-in">
        <h1 className="lesson__title" style={{ marginBottom: 'var(--space-4)' }}>דוח התקדמות הצוות</h1>
        {window.Cloud.isAdminEmail(user && user.email) ? (
          <VerifyGate user={user} onVerified={onUserChange} />
        ) : (
          <div className="callout callout--warn">אין לך הרשאה לצפות בדוח הזה.</div>
        )}
      </div>
    );
  }

  const activeWeek = rows ? rows.filter((r) => Date.now() - r.lastActive < 7 * DAY).length : 0;
  const avg = rows && rows.length ? rows.reduce((s, r) => s + r.ratio, 0) / rows.length : 0;
  const finished = rows ? rows.filter((r) => r.ratio >= 1).length : 0;
  const selectedRow = rows && selected ? rows.find((r) => r.uid === selected) : null;

  return (
    <div className="animate-in">
      <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-3)', flexWrap: 'wrap', marginBottom: 'var(--space-5)' }}>
        <button type="button" className="btn btn--ghost btn--sm" onClick={onExit}>→ חזרה למודולים</button>
        <h1 className="lesson__title" style={{ flex: 1 }}>דוח התקדמות הצוות</h1>
        <button type="button" className="btn btn--secondary btn--sm" onClick={() => setReloadTick((t) => t + 1)}>רענון</button>
        <button type="button" className="btn btn--primary btn--sm" disabled={!rows || !rows.length} onClick={() => exportCsv(shown, regions)}>
          ייצוא ל-Excel (CSV)
        </button>
      </div>

      {error && <div className="callout callout--warn" role="alert">{error}</div>}
      {!rows && !error && <p style={{ color: 'var(--text-muted)' }}>טוען נתוני לומדים…</p>}

      {rows && (
        <>
          <div className="hero__stats" style={{ marginBottom: 'var(--space-5)' }}>
            <div className="stat"><div className="stat__value">{rows.length}</div><div className="stat__label">לומדים רשומים</div></div>
            <div className="stat"><div className="stat__value">{activeWeek}</div><div className="stat__label">פעילים ב-7 ימים</div></div>
            <div className="stat"><div className="stat__value">{pct(avg)}</div><div className="stat__label">השלמה ממוצעת</div></div>
            <div className="stat"><div className="stat__value">{finished}</div><div className="stat__label">סיימו את כל המערך</div></div>
          </div>

          <div style={{ display: 'flex', gap: 'var(--space-3)', marginBottom: 'var(--space-3)', flexWrap: 'wrap', alignItems: 'end' }}>
            <div className="field" style={{ flex: '1 1 220px' }}>
              <label className="field__label" htmlFor="report-search">חיפוש לפי שם או אימייל</label>
              <input id="report-search" className="field__input" value={query} onChange={(e) => setQuery(e.target.value)} />
            </div>
            <div className="field">
              <label className="field__label" htmlFor="report-sort">מיון</label>
              <select id="report-sort" className="field__input" value={sort} onChange={(e) => setSort(e.target.value)}>
                <option value="lastActive">פעילות אחרונה</option>
                <option value="progress">התקדמות</option>
                <option value="name">שם</option>
              </select>
            </div>
          </div>

          {rows.length === 0 ? (
            <div className="callout callout--note">
              עדיין אין לומדים רשומים. החשבון שלך כמנהל אינו נספר בדוח, כדי שמעבר שלך על התכנים
              לא ישפיע על ממוצעי הצוות.
            </div>
          ) : (
            <div className="table-scroll card card--flush">
              <table className="data-table report-table">
                <thead>
                  <tr>
                    <th scope="col">לומד/ת</th>
                    <th scope="col">פעילות אחרונה</th>
                    <th scope="col">התקדמות</th>
                    <th scope="col">מודולים</th>
                    <th scope="col">מבחנים שעברו</th>
                    <th scope="col">איתור נכון</th>
                    <th scope="col">עוצר/ת כעת ב</th>
                    <th scope="col"><span className="sr-only">פרטים</span></th>
                  </tr>
                </thead>
                <tbody>
                  {shown.map((r) => (
                    <tr key={r.uid} aria-selected={r.uid === selected}>
                      <td>
                        <div style={{ fontWeight: 600, color: 'var(--text-strong)' }}>{r.name}</div>
                        {r.corrupt && (
                          <div style={{ fontSize: 'var(--text-xs)', color: 'var(--warn, #b45309)' }}>
                            ⚠ לא ניתן לקרוא את נתוני ההתקדמות של לומד זה
                          </div>
                        )}
                        <div style={{ fontSize: 'var(--text-xs)', color: 'var(--text-muted)' }} dir="ltr">{r.email}</div>
                      </td>
                      <td title={formatDate(r.lastActive)}>{relative(r.lastActive)}</td>
                      <td>
                        <Bar ratio={r.ratio} /> {pct(r.ratio)}
                        <div style={{ fontSize: 'var(--text-xs)', color: 'var(--text-muted)' }}>{r.done}/{r.total} שיעורים</div>
                      </td>
                      <td>{r.regionsDone}/{regions.length}</td>
                      <td>{r.quizzesPassed}/{r.quizTotal}</td>
                      <td>{r.findsTotal ? `${r.findsCorrect}/${r.findsTotal}` : '—'}</td>
                      <td style={{ fontSize: 'var(--text-xs)' }}>
                        {r.current ? <>{r.current.title}<br /><span style={{ color: 'var(--text-muted)' }}>שלב {r.current.step}/{r.current.steps}</span></> : '—'}
                      </td>
                      <td>
                        <button type="button" className="btn btn--ghost btn--sm" onClick={() => setSelected(r.uid === selected ? null : r.uid)}>
                          {r.uid === selected ? 'הסתר' : 'פרטים'}
                        </button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}

          {selectedRow && <LearnerDetail row={selectedRow} onClose={() => setSelected(null)} />}
        </>
      )}
    </div>
  );
}

window.AdminReport = AdminReport;
window.AdminReportSummarize = summarize;
window.AdminCsvCell = csvCell;
window.AdminReportSummarizeSafe = summarizeSafe;
window.AdminCurriculumIndex = curriculumIndex;

})();
