const db     = require('../db/db');
const crypto = require('crypto');
const { onClassJoined } = require('./gamificationController');
const { pushNotif } = require('../utils/notifications');
const { stripTags } = require('../utils/sanitize');

function genCode() {
  return crypto.randomBytes(4).toString('hex').toUpperCase();
}

/* ── Prepared statements (module-level to avoid re-parsing per request) ── */
const stmts = {
  getClassOwner:      db.prepare('SELECT id, teacher_id FROM classes WHERE id = ?'),
  getClassWithName:   db.prepare('SELECT id, name, teacher_id FROM classes WHERE id = ?'),
  getClassByCode:     db.prepare('SELECT id, name FROM classes WHERE invite_code = ?'),
  checkCodeExists:    db.prepare('SELECT id FROM classes WHERE invite_code = ?'),
  getMemberCheck:     db.prepare('SELECT 1 FROM class_members WHERE class_id = ? AND user_id = ?'),
  getClassMembers:    db.prepare('SELECT user_id FROM class_members WHERE class_id = ?'),
  insertMember:       db.prepare('INSERT INTO class_members (class_id, user_id) VALUES (?, ?)'),
  deleteMember:       db.prepare('DELETE FROM class_members WHERE class_id = ? AND user_id = ?'),
  deleteClass:        db.prepare('DELETE FROM classes WHERE id = ?'),
  updateClassCode:    db.prepare('UPDATE classes SET invite_code = ? WHERE id = ?'),
  updateClassName:    db.prepare('UPDATE classes SET name = ? WHERE id = ?'),
  updateClassDesc:    db.prepare('UPDATE classes SET description = ? WHERE id = ?'),
  updateClassFeats:   db.prepare('UPDATE classes SET features = ? WHERE id = ?'),
  getClassUpdated:    db.prepare('SELECT id, name, description, invite_code, features, cover_emoji FROM classes WHERE id = ?'),
  getClassTeacherId:  db.prepare('SELECT teacher_id FROM classes WHERE id = ?'),
  getStudentById:     db.prepare("SELECT id, name FROM users WHERE id = ? AND role = 'student'"),
  getStudentByEmail:  db.prepare("SELECT id, name FROM users WHERE email = ? AND role = 'student'"),
  insertAnnouncement: db.prepare('INSERT INTO announcements (class_id, author_id, text) VALUES (?, ?, ?)'),
  deleteAnnouncement: db.prepare('DELETE FROM announcements WHERE id = ? AND class_id = ?'),
};

/* ── GET /api/classes ── teacher: own classes; admin: all ─────────────── */
function listClasses(req, res) {
  const { role, id: uid } = req.user;
  const rows = role === 'admin'
    ? db.prepare(`
        SELECT c.id, c.name, c.description, c.invite_code, c.cover_emoji, c.created_at,
               u.name AS teacher_name,
               COUNT(DISTINCT cm.user_id) AS member_count,
               COUNT(DISTINCT a.id)       AS assignment_count
        FROM classes c
        JOIN users u ON u.id = c.teacher_id
        LEFT JOIN class_members cm ON cm.class_id = c.id
        LEFT JOIN assignments a    ON a.class_id  = c.id
        GROUP BY c.id ORDER BY c.created_at DESC
        LIMIT 500
      `).all()
    : db.prepare(`
        SELECT c.id, c.name, c.description, c.invite_code, c.cover_emoji, c.created_at,
               COUNT(DISTINCT cm.user_id) AS member_count,
               COUNT(DISTINCT a.id)       AS assignment_count
        FROM classes c
        LEFT JOIN class_members cm ON cm.class_id = c.id
        LEFT JOIN assignments a    ON a.class_id  = c.id
        WHERE c.teacher_id = ?
        GROUP BY c.id ORDER BY c.created_at DESC
      `).all(uid);
  res.json(rows);
}

/* ── POST /api/classes ── create ───────────────────────────────────────── */
function createClass(req, res) {
  const { name, description, cover_emoji } = req.body;
  if (!name?.trim()) return res.status(400).json({ error: 'name required' });
  let invite_code, attempts = 0;
  while (attempts++ < 10) {
    invite_code = genCode();
    if (!stmts.checkCodeExists.get(invite_code)) break;
    invite_code = null;
  }
  if (!invite_code) return res.status(500).json({ error: 'Не удалось сгенерировать уникальный код — попробуйте ещё раз' });
  const cleanName = stripTags(name.trim());
  const cleanDesc = description ? stripTags(description.trim()) : null;
  const emoji = cover_emoji ? stripTags(String(cover_emoji).trim()).slice(0, 50) : '';
  const r = db.prepare(
    'INSERT INTO classes (name, description, teacher_id, invite_code, cover_emoji) VALUES (?, ?, ?, ?, ?)'
  ).run(cleanName, cleanDesc, req.user.id, invite_code, emoji);
  res.status(201).json({ id: r.lastInsertRowid, name: cleanName, invite_code, cover_emoji: emoji });
}

/* ── GET /api/classes/:id ── detail (teacher/admin) ────────────────────── */
function getClass(req, res) {
  const cls = db.prepare(`
    SELECT c.*, u.name AS teacher_name
    FROM classes c JOIN users u ON u.id = c.teacher_id
    WHERE c.id = ?
  `).get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Class not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });

  if (cls.features) {
    try { cls.features = JSON.parse(cls.features); } catch { cls.features = null; }
  }

  const members = db.prepare(`
    SELECT u.id, u.name, u.email, cm.joined_at
    FROM class_members cm
    JOIN users u ON u.id = cm.user_id
    WHERE cm.class_id = ?
    ORDER BY cm.joined_at DESC
  `).all(req.params.id);

  // Load per-member stats in one query instead of N+1 LEFT JOIN
  if (members.length > 0) {
    const uIds = members.map(m => m.id);
    const ph   = uIds.map(() => '?').join(',');
    const stats = db.prepare(`
      SELECT user_id,
             COUNT(*)                                                    AS tests_count,
             ROUND(AVG(CAST(score AS REAL) / total * 100), 1)           AS avg_pct
      FROM test_sessions
      WHERE user_id IN (${ph}) AND status = 'completed'
      GROUP BY user_id
    `).all(...uIds);
    const statsMap = {};
    for (const s of stats) statsMap[s.user_id] = s;
    for (const m of members) {
      m.tests_count = statsMap[m.id]?.tests_count || 0;
      m.avg_pct     = statsMap[m.id]?.avg_pct     || 0;
    }
  }

  const assignments = db.prepare(`
    SELECT a.id, a.title, a.subject_slug, a.mode, a.count, a.deadline, a.created_at,
           a.file_id, f.title AS file_title, a.test_id, a.max_attempts,
           (SELECT COUNT(*) FROM class_members WHERE class_id = a.class_id) AS total_members,
           COUNT(DISTINCT CASE WHEN ts.status = 'completed' THEN ases.user_id END) AS completed_count
    FROM assignments a
    LEFT JOIN files f ON f.id = a.file_id
    LEFT JOIN assignment_sessions ases ON ases.assignment_id = a.id
    LEFT JOIN test_sessions ts ON ts.id = ases.session_id
    WHERE a.class_id = ?
    GROUP BY a.id ORDER BY a.created_at DESC
  `).all(req.params.id);

  res.json({ ...cls, members, assignments });
}

/* ── PATCH /api/classes/:id ── rename / update description ─────────────── */
function updateClass(req, res) {
  const cls = stmts.getClassOwner.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });
  const { name, description, features, cover_emoji } = req.body;
  if (name?.trim()) stmts.updateClassName.run(stripTags(name.trim()), cls.id);
  if (description !== undefined) stmts.updateClassDesc.run(description?.trim() || null, cls.id);
  if (cover_emoji !== undefined) {
    const emoji = cover_emoji ? stripTags(String(cover_emoji).trim()).slice(0, 50) : '';
    db.prepare('UPDATE classes SET cover_emoji = ? WHERE id = ?').run(emoji, cls.id);
  }
  if (features !== undefined) stmts.updateClassFeats.run(features !== null ? JSON.stringify(features) : null, cls.id);
  const updated = stmts.getClassUpdated.get(cls.id);
  if (updated.features) {
    try { updated.features = JSON.parse(updated.features); } catch { updated.features = null; }
  }
  res.json(updated);
}

/* ── POST /api/classes/:id/new-code ── regenerate invite code ───────────── */
function regenerateCode(req, res) {
  const cls = stmts.getClassOwner.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });
  let code, attempts = 0;
  while (attempts++ < 10) {
    code = genCode();
    if (!stmts.checkCodeExists.get(code)) break;
    code = null;
  }
  if (!code) return res.status(500).json({ error: 'Не удалось сгенерировать уникальный код — попробуйте ещё раз' });
  stmts.updateClassCode.run(code, cls.id);
  res.json({ invite_code: code });
}

/* ── GET /api/classes/:id/journal ── grade matrix for teacher ────────────── */
function classJournal(req, res) {
  const cls = stmts.getClassWithName.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });

  const members = db.prepare(`
    SELECT u.id, u.name, u.email FROM class_members cm
    JOIN users u ON u.id = cm.user_id WHERE cm.class_id = ? ORDER BY u.name
  `).all(req.params.id);

  const assignments = db.prepare(`
    SELECT id, title, subject_slug, deadline, is_homework, created_at
    FROM assignments WHERE class_id = ? AND user_id IS NULL ORDER BY created_at ASC
  `).all(req.params.id);

  const results = db.prepare(`
    SELECT ases.user_id, ases.assignment_id,
           MAX(ts.score) AS score,
           ts.total,
           MAX(ROUND(CAST(ts.score AS REAL) / ts.total * 100)) AS percent,
           MAX(ts.finished_at) AS finished_at
    FROM assignment_sessions ases
    JOIN test_sessions ts ON ts.id = ases.session_id AND ts.status = 'completed'
    JOIN assignments a ON a.id = ases.assignment_id
    WHERE a.class_id = ? AND a.user_id IS NULL
    GROUP BY ases.user_id, ases.assignment_id
  `).all(req.params.id);

  // course progress per student
  const courses = db.prepare(`
    SELECT c.id, c.title, c.subject_slug,
      (SELECT COUNT(*) FROM lessons l WHERE l.course_id = c.id AND l.is_published = 1) AS lesson_count
    FROM class_courses cc
    JOIN courses c ON cc.course_id = c.id
    WHERE cc.class_id = ?
    ORDER BY cc.assigned_at
  `).all(req.params.id);

  let courseProgress = [];
  if (courses.length && members.length) {
    const cIds = courses.map(c => c.id);
    const mIds = members.map(m => m.id);
    const cPh = cIds.map(() => '?').join(',');
    const mPh = mIds.map(() => '?').join(',');
    const rows = db.prepare(`
      SELECT l.course_id, lp.user_id, COUNT(*) AS done_count
      FROM lesson_progress lp
      JOIN lessons l ON lp.lesson_id = l.id
      WHERE l.course_id IN (${cPh}) AND lp.user_id IN (${mPh}) AND lp.completed = 1
      GROUP BY l.course_id, lp.user_id
    `).all(...cIds, ...mIds);
    const doneMap = {};
    for (const r of rows) doneMap[r.user_id + '_' + r.course_id] = r.done_count;
    for (const c of courses) {
      for (const m of members) {
        const done = doneMap[m.id + '_' + c.id] || 0;
        courseProgress.push({
          userId: m.id, courseId: c.id,
          doneCount: done, totalLessons: c.lesson_count,
          percent: c.lesson_count > 0 ? Math.round(done / c.lesson_count * 100) : 0,
        });
      }
    }
  }

  // averages per student — O(n) via Map instead of O(n*m) filter
  const resultsByUser = new Map();
  for (const r of results) {
    if (!resultsByUser.has(r.user_id)) resultsByUser.set(r.user_id, []);
    resultsByUser.get(r.user_id).push(r);
  }
  const studentStats = members.map(m => {
    const studentResults = resultsByUser.get(m.id) || [];
    const avgPct = studentResults.length > 0
      ? Math.round(studentResults.reduce((s, r) => s + r.percent, 0) / studentResults.length)
      : null;
    return { userId: m.id, avgPct, completedCount: studentResults.length, totalAssignments: assignments.length };
  });

  res.json({ className: cls.name, members, assignments, results, courses, courseProgress, studentStats });
}

/* ── GET /api/classes/:id/journal/csv ── export gradebook as CSV ────────── */
function classJournalCsv(req, res) {
  const cls = stmts.getClassWithName.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });

  const members = db.prepare(`
    SELECT u.id, u.name, u.email FROM class_members cm
    JOIN users u ON u.id = cm.user_id WHERE cm.class_id = ? ORDER BY u.name
  `).all(req.params.id);

  const assignments = db.prepare(`
    SELECT id, title FROM assignments WHERE class_id = ? AND user_id IS NULL ORDER BY created_at ASC
  `).all(req.params.id);

  const results = db.prepare(`
    SELECT ases.user_id, ases.assignment_id,
           MAX(ROUND(CAST(ts.score AS REAL) / ts.total * 100)) AS percent
    FROM assignment_sessions ases
    JOIN test_sessions ts ON ts.id = ases.session_id AND ts.status = 'completed'
    JOIN assignments a ON a.id = ases.assignment_id
    WHERE a.class_id = ? AND a.user_id IS NULL
    GROUP BY ases.user_id, ases.assignment_id
  `).all(req.params.id);

  // Build result map
  const rmap = {};
  results.forEach(r => { rmap[r.user_id + '_' + r.assignment_id] = r.percent; });

  // CSV header
  // Protect against CSV formula injection (=, +, @, - at start trigger Excel/Sheets formulas)
  const csvEsc = s => {
    const str = String(s || '');
    const safe = /^[=+@\-]/.test(str) ? `'${str}` : str;
    return '"' + safe.replace(/"/g, '""') + '"';
  };
  const header = ['Ученик', 'Email', ...assignments.map(a => a.title), 'Средний %'];
  const rows = [header.map(csvEsc).join(',')];

  members.forEach(m => {
    const scores = assignments.map(a => {
      const key = m.id + '_' + a.id;
      return rmap[key] !== undefined ? rmap[key] : '';
    });
    const filled = scores.filter(s => s !== '');
    const avg = filled.length > 0 ? Math.round(filled.reduce((s, v) => s + v, 0) / filled.length) : '';
    rows.push([csvEsc(m.name), csvEsc(m.email), ...scores, avg].join(','));
  });

  const bom = '\uFEFF'; // UTF-8 BOM for Excel
  res.setHeader('Content-Type', 'text/csv; charset=utf-8');
  res.setHeader('Content-Disposition', `attachment; filename="gradebook-${cls.name.replace(/[^a-zA-Zа-яА-Я0-9]/g, '_')}.csv"`);
  res.send(bom + rows.join('\n'));
}

/* ── DELETE /api/classes/:id ──────────────────────────────────────────── */
function deleteClass(req, res) {
  const cls = stmts.getClassOwner.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });
  stmts.deleteClass.run(req.params.id);
  res.json({ ok: true });
}

/* ── POST /api/classes/join ── student joins by invite code ────────────── */
function joinClass(req, res) {
  const { invite_code } = req.body;
  const cls = stmts.getClassByCode.get(invite_code?.trim().toUpperCase());
  if (!cls) return res.status(404).json({ error: 'Неверный код приглашения' });
  try {
    stmts.insertMember.run(cls.id, req.user.id);
  } catch (e) {
    if (e.message.includes('UNIQUE')) return res.status(409).json({ error: 'Вы уже в этом классе' });
    throw e;
  }
  // Notify teacher about new student joining
  try {
    const teacher = stmts.getClassTeacherId.get(cls.id);
    if (teacher) pushNotif(teacher.teacher_id, 'join', `«${req.user.name}» вступил в класс «${cls.name}»`, '/classes');
  } catch {}
  try { onClassJoined(req.user.id); } catch {}
  res.json({ ok: true, class_name: cls.name });
}

/* ── GET /api/classes/students ── list students in teacher's classes ─ */
function listStudents(req, res) {
  if (req.user.role === 'admin') {
    const rows = db.prepare(
      "SELECT id, name, email FROM users WHERE role IN ('student','free_student') ORDER BY name"
    ).all();
    return res.json(rows);
  }
  // Teacher: students in their classes + personal students (teacher_students)
  const rows = db.prepare(`
    SELECT id, name, email FROM (
      SELECT DISTINCT u.id, u.name, u.email FROM users u
      JOIN class_members cm ON cm.user_id = u.id
      JOIN classes c ON c.id = cm.class_id
      WHERE c.teacher_id = ? AND u.role IN ('student','free_student')
      UNION
      SELECT u.id, u.name, u.email FROM users u
      JOIN teacher_students ts ON ts.student_id = u.id
      WHERE ts.teacher_id = ? AND u.role IN ('student','free_student')
    )
    ORDER BY name
  `).all(req.user.id, req.user.id);
  res.json(rows);
}

/* ── POST /api/classes/:id/members ── admin/teacher adds student by email or id ─ */
function addMember(req, res) {
  const cls = stmts.getClassOwner.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Class not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });

  const { email, user_id } = req.body;
  let student;
  if (user_id) {
    student = stmts.getStudentById.get(Number(user_id));
    if (!student) return res.status(404).json({ error: 'Ученик не найден' });
  } else {
    if (!email?.trim()) return res.status(400).json({ error: 'email required' });
    student = stmts.getStudentByEmail.get(email.trim().toLowerCase());
    if (!student) return res.status(404).json({ error: 'Ученик с таким email не найден' });
  }

  try {
    stmts.insertMember.run(cls.id, student.id);
  } catch (e) {
    if (e.message.includes('UNIQUE')) return res.status(409).json({ error: 'Ученик уже в этом классе' });
    throw e;
  }
  res.json({ ok: true, name: student.name });
}

/* ── DELETE /api/classes/:id/members/:uid ── kick ─────────────────────── */
function kickMember(req, res) {
  const cls = stmts.getClassOwner.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });
  stmts.deleteMember.run(req.params.id, req.params.uid);
  res.json({ ok: true });
}

/* ── GET /api/classes/my ── student's enrolled classes ─────────────────── */
function myClasses(req, res) {
  const classes = db.prepare(`
    SELECT c.id, c.name, c.description, u.name AS teacher_name,
           COUNT(DISTINCT a.id) AS assignment_count
    FROM class_members cm
    JOIN classes c ON c.id = cm.class_id
    JOIN users u ON u.id = c.teacher_id
    LEFT JOIN assignments a ON a.class_id = c.id
    WHERE cm.user_id = ?
    GROUP BY c.id ORDER BY cm.joined_at DESC
  `).all(req.user.id);
  res.json(classes);
}

/* ── GET /api/classes/:id/announcements ─────────────────────────────────── */
function getAnnouncements(req, res) {
  const cls = stmts.getClassOwner.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });
  const announcements = db.prepare(`
    SELECT a.id, a.text, a.created_at, u.name AS author_name
    FROM announcements a
    JOIN users u ON u.id = a.author_id
    WHERE a.class_id = ?
    ORDER BY a.created_at DESC LIMIT 50
  `).all(req.params.id);
  res.json(announcements);
}

/* ── POST /api/classes/:id/announcements ────────────────────────────────── */
function createAnnouncement(req, res) {
  const text = stripTags(req.body.text || '');
  if (!text) return res.status(400).json({ error: 'text required' });
  if (text.length > 4000) return res.status(400).json({ error: 'text too long (max 4000 chars)' });
  const cls = stmts.getClassWithName.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });

  const r = stmts.insertAnnouncement.run(req.params.id, req.user.id, text);

  const members = stmts.getClassMembers.all(req.params.id);
  const preview = text.slice(0, 80) + (text.length > 80 ? '…' : '');
  const batchNotif = db.transaction(() => {
    for (const m of members) {
      pushNotif(m.user_id, 'announcement', `Объявление в «${cls.name}»: ${preview}`, '/classes');
    }
  });
  batchNotif();
  res.status(201).json({ id: r.lastInsertRowid });
}

/* ── GET /api/classes/:id/feed ── class board (students + teacher) ──────── */
function classFeed(req, res) {
  const { role, id: uid } = req.user;
  const classId = req.params.id;

  const cls = stmts.getClassWithName.get(classId);
  if (!cls) return res.status(404).json({ error: 'Not found' });

  const isTeacherOrAdmin = role === 'admin' || cls.teacher_id === uid;
  if (!isTeacherOrAdmin) {
    const member = stmts.getMemberCheck.get(classId, uid);
    if (!member) return res.status(403).json({ error: 'Forbidden' });
  }

  const assignments = db.prepare(`
    SELECT a.id, a.title, a.subject_slug, a.mode, a.deadline, a.created_at, a.is_homework,
           a.max_attempts,
           f.title AS file_title,
           COUNT(DISTINCT cm.user_id) AS total_members,
           COUNT(DISTINCT CASE WHEN ts.status = 'completed' THEN ases.user_id END) AS done_count
    FROM assignments a
    LEFT JOIN files f ON f.id = a.file_id
    LEFT JOIN class_members cm ON cm.class_id = a.class_id
    LEFT JOIN assignment_sessions ases ON ases.assignment_id = a.id
    LEFT JOIN test_sessions ts ON ts.id = ases.session_id
    WHERE a.class_id = ?
    GROUP BY a.id
    ORDER BY a.created_at DESC LIMIT 20
  `).all(classId);

  if (!isTeacherOrAdmin && assignments.length) {
    const aIds = assignments.map(a => a.id);
    const ph = aIds.map(() => '?').join(',');

    // Latest session per assignment
    const latest = db.prepare(`
      SELECT ases.assignment_id, ts.score, ts.total, ts.status
      FROM assignment_sessions ases
      JOIN test_sessions ts ON ts.id = ases.session_id
      WHERE ases.assignment_id IN (${ph}) AND ases.user_id = ?
        AND ases.id = (SELECT MAX(a2.id) FROM assignment_sessions a2
                       WHERE a2.assignment_id = ases.assignment_id AND a2.user_id = ases.user_id)
    `).all(...aIds, uid);
    const latestMap = {};
    for (const r of latest) latestMap[r.assignment_id] = r;

    // Completed attempts count per assignment
    const attempts = db.prepare(`
      SELECT ases.assignment_id, COUNT(*) AS n
      FROM assignment_sessions ases
      JOIN test_sessions ts ON ts.id = ases.session_id AND ts.status = 'completed'
      WHERE ases.assignment_id IN (${ph}) AND ases.user_id = ?
      GROUP BY ases.assignment_id
    `).all(...aIds, uid);
    const attemptsMap = {};
    for (const r of attempts) attemptsMap[r.assignment_id] = r.n;

    for (const a of assignments) {
      const ses = latestMap[a.id];
      a.my_status = ses ? ses.status : null;
      a.my_score  = ses?.score ?? null;
      a.my_total  = ses?.total ?? null;
      a.attempts_used = attemptsMap[a.id] || 0;
    }
  }

  const announcements = db.prepare(`
    SELECT a.id, a.text, a.created_at, u.name AS author_name
    FROM announcements a
    JOIN users u ON u.id = a.author_id
    WHERE a.class_id = ?
    ORDER BY a.created_at DESC LIMIT 20
  `).all(classId);

  const activity = db.prepare(`
    SELECT u.name AS student_name, a.title AS assignment_title,
           ts.score, ts.total, ts.finished_at AS completed_at
    FROM test_sessions ts
    JOIN assignment_sessions ases ON ases.session_id = ts.id
    JOIN assignments a ON a.id = ases.assignment_id
    JOIN users u ON u.id = ts.user_id
    WHERE a.class_id = ? AND ts.status = 'completed'
    ORDER BY ts.finished_at DESC LIMIT 15
  `).all(classId);

  res.json({ class: cls, assignments, announcements, activity });
}

/* ── DELETE /api/classes/:id/announcements/:aid ─────────────────────────── */
function deleteAnnouncement(req, res) {
  const cls = stmts.getClassOwner.get(req.params.id);
  if (!cls) return res.status(404).json({ error: 'Not found' });
  if (req.user.role !== 'admin' && cls.teacher_id !== req.user.id)
    return res.status(403).json({ error: 'Forbidden' });
  stmts.deleteAnnouncement.run(req.params.aid, req.params.id);
  res.json({ ok: true });
}

module.exports = {
  listClasses, createClass, getClass, deleteClass,
  joinClass, kickMember, addMember, myClasses, listStudents,
  getAnnouncements, createAnnouncement, deleteAnnouncement, classFeed,
  updateClass, regenerateCode, classJournal, classJournalCsv,
};
