/**
 * Classes Controller
 * CRUD for the `classes` table — grade-level sections (e.g. "Form 3A").
 * Architecture rules:
 *   - Sync-aware writes: every INSERT/UPDATE sets sync_status='pending', updates updated_at, mints a new uid (v4)
 *   - Soft delete: is_deleted = 1 instead of DELETE
 *   - DB access via better-sqlite3 with the getDb() helper
 *   - All routes are auth-protected; writes are admin-only
 *
 * Mounted at /api/classes in server/src/index.js.
 */

const express = require('express');
const router = express.Router();
const path = require('path');
const Database = require('better-sqlite3');
const { v4: uuidv4 } = require('uuid');
const AuditService = require('../services/AuditService');

const dbPath = process.env.DB_PATH || path.join(__dirname, '../../data/school.db');
let db;
const getDb = () => {
  if (!db) {
    db = new Database(dbPath);
    db.pragma('foreign_keys = ON');
  }
  return db;
};

// Auth middleware
const auth = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'No token provided' });
  try {
    const jwt = require('jsonwebtoken');
    const { jwtSecret: JWT_SECRET } = require('../config');
    req.user = jwt.verify(token, JWT_SECRET);
    next();
  } catch (err) {
    res.status(401).json({ error: 'Invalid token' });
  }
};

// Admin / principal gate (matches users.controller.js)
const adminOnly = (req, res, next) => {
  if (!['school_admin', 'systems_admin', 'principal'].includes(req.user.role)) {
    return res.status(403).json({ error: 'Admin or Principal access required' });
  }
  next();
};

// ============ CLASSES CRUD ============

/**
 * GET /api/classes
 * List all active class sections, with student and subject counts.
 * Optional query params: search (matches name/section), class_teacher_id, scope=mine
 *
 * Role behavior:
 *   - admin / principal / systems_admin: see all classes.
 *   - teacher: when `scope=mine` (or no `class_teacher_id` filter is supplied),
 *     the response is restricted to classes where the teacher is either
 *     `class_teacher_id` OR teaches at least one subject in the class.
 *     This is the default for the teacher dashboard / take-attendance flow.
 *   - other roles: see all (read-only).
 */
/**
 * @openapi
 * /api/classes:
 *   get:
 *     tags:
 *       - Classes
 *     summary: List all active class sections
 *     description: |
 *       Returns all active classes with the 4-tile HUD counts (student_count,
 *       chapters_count, assignments_count, materials_count) embedded per row,
 *       so the client can render the HUD without a follow-up call.
 *       Role-based filtering: admins/principals see all, teachers see their
 *       own classes when `scope=mine` or no `class_teacher_id` filter is
 *       provided.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: search
 *         schema:
 *           type: string
 *         description: Search term matching class name or section
 *       - in: query
 *         name: class_teacher_id
 *         schema:
 *           type: integer
 *         description: Filter by class teacher ID
 *       - in: query
 *         name: scope
 *         schema:
 *           type: string
 *           enum: [mine]
 *         description: Restrict results for teachers (scope=mine)
 *     responses:
 *       '200':
 *         description: Array of class objects
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 type: object
 *                 properties:
 *                   id: { type: integer }
 *                   uid: { type: string }
 *                   name: { type: string }
 *                   section: { type: string, nullable: true }
 *                   capacity: { type: integer }
 *                   class_teacher_id: { type: integer, nullable: true }
 *                   class_teacher_name: { type: string, nullable: true }
 *                   student_count: { type: integer, description: Active enrollments in this class whose user is not soft-deleted. }
 *                   subject_count: { type: integer, description: Active subjects bound to this class (legacy `subjects` table). }
 *                   chapters_count: { type: integer, description: Focus points reachable via schemes_of_work → subjects_new for this class. }
 *                   assignments_count: { type: integer, description: Homework + tests + assignments whose subject is bound to this class (legacy `subjects` ∪ `subjects_new`). }
 *                   materials_count: { type: integer, description: Attachments (parent_kind='subject') + syllabuses joined via subjects_new for this class. }
 *       '401':
 *         $ref: '#/components/responses/Unauthorized'
 *   post:
 *     tags:
 *       - Classes
 *     summary: Create a new class section
 *     description: Creates a new class with sync_status='pending'. Admin/Principal only.
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             $ref: '#/components/schemas/ClassCreate'
 *     responses:
 *       '201':
 *         description: Class created successfully
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/Class'
 *       '400':
 *         description: Validation error
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 error:
 *                   type: string
 *       '401':
 *         $ref: '#/components/responses/Unauthorized'
 *       '403':
 *         $ref: '#/components/responses/Forbidden'
 */
router.get('/', auth, (req, res) => {
  try {
    const { search, class_teacher_id, scope } = req.query;
    const role = req.user.role;
    const restrictTeacher =
      role === 'teacher' && (scope === 'mine' || !class_teacher_id);

    let query = `
      SELECT c.*,
        (SELECT COUNT(*) FROM enrollments e JOIN users u ON e.student_id = u.id WHERE e.class_id = c.id AND e.status = 'active' AND e.is_deleted = 0 AND u.is_deleted = 0) as student_count,
        (SELECT COUNT(*) FROM subjects WHERE class_id = c.id AND is_deleted = 0) as subject_count,
        t.first_name || ' ' || t.last_name as class_teacher_name,
        COALESCE((SELECT COUNT(fp.id) FROM focus_points fp JOIN schemes_of_work sw ON fp.scheme_id = sw.id JOIN subjects_new sn ON sw.subject_id = sn.id WHERE sn.class_id = c.id AND fp.is_deleted = 0 AND sw.is_deleted = 0 AND sn.is_deleted = 0), 0) as chapters_count,
        COALESCE((SELECT COUNT(h.id) FROM homework h WHERE h.class_id = c.id AND h.is_deleted = 0), 0) +
        COALESCE((SELECT COUNT(t.id) FROM tests t WHERE t.class_id = c.id AND t.is_deleted = 0), 0) +
        COALESCE((SELECT COUNT(a.id) FROM assignments a WHERE a.is_deleted = 0 AND a.course_id IN (SELECT id FROM subjects WHERE class_id = c.id AND is_deleted = 0 UNION SELECT id FROM subjects_new WHERE class_id = c.id AND is_deleted = 0)), 0) as assignments_count,
        COALESCE((SELECT COUNT(at.id) FROM attachments at WHERE at.parent_kind = 'subject' AND at.is_deleted = 0 AND at.parent_id IN (SELECT id FROM subjects WHERE class_id = c.id AND is_deleted = 0 UNION SELECT id FROM subjects_new WHERE class_id = c.id AND is_deleted = 0)), 0) +
        COALESCE((SELECT COUNT(sy.id) FROM syllabuses sy JOIN subjects_new sn ON sy.subject_id = sn.id WHERE sn.class_id = c.id AND sy.is_deleted = 0 AND sn.is_deleted = 0), 0) as materials_count
      FROM classes c
      LEFT JOIN users t ON c.class_teacher_id = t.id
      WHERE c.is_deleted = 0
    `;
    const params = [];
    if (search) {
      query += ' AND (c.name LIKE ? OR c.section LIKE ?)';
      const term = `%${search}%`;
      params.push(term, term);
    }
    if (class_teacher_id) {
      query += ' AND c.class_teacher_id = ?';
      params.push(class_teacher_id);
    }
    if (restrictTeacher) {
      // Classes where the teacher is the class_teacher OR teaches a subject
      // in the class. (EXISTS subquery so empty result is OK.)
      query += ` AND (
        c.class_teacher_id = ?
        OR EXISTS (SELECT 1 FROM subjects s WHERE s.class_id = c.id AND s.teacher_id = ? AND s.is_deleted = 0)
      )`;
      params.push(req.user.id, req.user.id);
    }
    query += ' ORDER BY c.name, c.section';
    res.json(getDb().prepare(query).all(...params));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * GET /api/classes/:id
 * Return a single class with its subjects and active students.
 */
/**
 * @openapi
 * /api/classes/{id}:
 *   get:
 *     tags:
 *       - Classes
 *     summary: Get a single class with subjects and students
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *     responses:
 *       '200':
 *         description: Class details with nested subjects and students
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/ClassDetail'
 *       '401':
 *         $ref: '#/components/responses/Unauthorized'
 *       '404':
 *         $ref: '#/components/responses/NotFound'
 *   put:
 *     tags:
 *       - Classes
 *     summary: Update a class section
 *     description: Updates a class. Sets sync_status='pending'. Admin/Principal only.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             $ref: '#/components/schemas/ClassUpdate'
 *     responses:
 *       '200':
 *         description: Class updated successfully
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/Class'
 *       '401':
 *         $ref: '#/components/responses/Unauthorized'
 *       '403':
 *         $ref: '#/components/responses/Forbidden'
 *       '404':
 *         $ref: '#/components/responses/NotFound'
 *   delete:
 *     tags:
 *       - Classes
 *     summary: Archive (soft delete) a class
 *     description: Soft deletes a class (is_deleted=1). Fails if class has active enrollments.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *     responses:
 *       '200':
 *         description: Class archived successfully
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                 message:
 *                   type: string
 *       '400':
 *         description: Cannot delete class with active enrollments
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 error:
 *                   type: string
 *       '401':
 *         $ref: '#/components/responses/Unauthorized'
 *       '403':
 *         $ref: '#/components/responses/Forbidden'
 *       '404':
 *         $ref: '#/components/responses/NotFound'
 */
/**
 * @openapi
 * /api/classes/{id}/heads-up:
 *   get:
 *     tags:
 *       - Classes
 *     summary: Class HUD counts (single class)
 *     description: |
 *       Returns the 4-tile HUD counts for a single class — student_count,
 *       chapters_count, assignments_count, materials_count. Equivalent to
 *       the per-row counts embedded in GET /api/classes; provided as a
 *       dedicated endpoint so the frontend can refresh one class without
 *       re-fetching the full list.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema: { type: integer }
 *         description: Numeric class id.
 *     responses:
 *       '200':
 *         description: HUD counts
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 student_count: { type: integer, description: Active enrollments whose user is not soft-deleted. }
 *                 chapters_count: { type: integer, description: Focus points via schemes_of_work → subjects_new. }
 *                 assignments_count: { type: integer, description: Homework + tests + assignments (subjects ∪ subjects_new). }
 *                 materials_count: { type: integer, description: Attachments (parent_kind='subject') + syllabuses via subjects_new. }
 *       '401': { $ref: '#/components/responses/Unauthorized' }
 *       '404': { description: Class not found }
 */
/**
 * GET /api/classes/:id/heads-up
 * Retrieve class HUD counts for chapters, assignments, materials, and learners.
 *
 * Counts source-of-truth:
 *   - student_count  → active enrollments whose student user is not soft-deleted
 *   - chapters_count → focus_points reachable via schemes_of_work → subjects_new
 *                      for this class (excludes soft-deleted intermediates)
 *   - assignments_count → homework + tests + assignments; assignments join the
 *                      subject registry via UNION of legacy `subjects` and
 *                      active `subjects_new` (assignments.course_id FK points
 *                      to legacy subjects, but the live path uses subjects_new)
 *   - materials_count → attachments with parent_kind='subject' pointing at any
 *                      subject in this class + syllabuses joined via subjects_new
 */
router.get('/:id/heads-up', auth, (req, res) => {
  try {
    const classId = req.params.id;
    const query = `
      SELECT
        (SELECT COUNT(*) FROM enrollments e JOIN users u ON e.student_id = u.id WHERE e.class_id = c.id AND e.status = 'active' AND e.is_deleted = 0 AND u.is_deleted = 0) as student_count,
        COALESCE((SELECT COUNT(fp.id) FROM focus_points fp JOIN schemes_of_work sw ON fp.scheme_id = sw.id JOIN subjects_new sn ON sw.subject_id = sn.id WHERE sn.class_id = c.id AND fp.is_deleted = 0 AND sw.is_deleted = 0 AND sn.is_deleted = 0), 0) as chapters_count,
        COALESCE((SELECT COUNT(h.id) FROM homework h WHERE h.class_id = c.id AND h.is_deleted = 0), 0) +
        COALESCE((SELECT COUNT(t.id) FROM tests t WHERE t.class_id = c.id AND t.is_deleted = 0), 0) +
        COALESCE((SELECT COUNT(a.id) FROM assignments a WHERE a.is_deleted = 0 AND a.course_id IN (SELECT id FROM subjects WHERE class_id = c.id AND is_deleted = 0 UNION SELECT id FROM subjects_new WHERE class_id = c.id AND is_deleted = 0)), 0) as assignments_count,
        COALESCE((SELECT COUNT(at.id) FROM attachments at WHERE at.parent_kind = 'subject' AND at.is_deleted = 0 AND at.parent_id IN (SELECT id FROM subjects WHERE class_id = c.id AND is_deleted = 0 UNION SELECT id FROM subjects_new WHERE class_id = c.id AND is_deleted = 0)), 0) +
        COALESCE((SELECT COUNT(sy.id) FROM syllabuses sy JOIN subjects_new sn ON sy.subject_id = sn.id WHERE sn.class_id = c.id AND sy.is_deleted = 0 AND sn.is_deleted = 0), 0) as materials_count
      FROM classes c
      WHERE c.id = ? AND c.is_deleted = 0
    `;
    const counts = getDb().prepare(query).get(classId);
    if (!counts) {
      return res.status(404).json({ error: 'Class not found' });
    }
    res.json(counts);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.get('/:id', auth, (req, res) => {
  try {
    const cls = getDb().prepare(`
      SELECT c.*, t.first_name || ' ' || t.last_name as class_teacher_name
      FROM classes c
      LEFT JOIN users t ON c.class_teacher_id = t.id
      WHERE c.id = ? AND c.is_deleted = 0
    `).get(req.params.id);
    if (!cls) return res.status(404).json({ error: 'Class not found' });

    const subjects = getDb().prepare(`
      SELECT s.*, t.first_name || ' ' || t.last_name as teacher_name
      FROM subjects s
      LEFT JOIN users t ON s.teacher_id = t.id
      WHERE s.class_id = ? AND s.is_deleted = 0
      ORDER BY s.name
    `).all(req.params.id);

    const students = getDb().prepare(`
      SELECT u.id, u.uid, u.first_name, u.last_name, u.email, u.phone, e.roll_number
      FROM enrollments e
      JOIN users u ON e.student_id = u.id
      WHERE e.class_id = ? AND e.status = 'active' AND e.is_deleted = 0 AND u.is_deleted = 0
      ORDER BY e.roll_number, u.last_name
    `).all(req.params.id);

    res.json({ ...cls, subjects, students });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * GET /api/classes/:id/students
 * Flat array of active students in the class. Used by the take-attendance
 * page (which historically called this URL — see AttendanceTake.tsx).
 * Optional: ?date=YYYY-MM-DD (currently unused; kept for forward compat).
 *
 * Role-based access: teachers can only see students from classes they own
 * (`class_teacher_id`) OR teach at least one subject in. Admins / principals
 * see all. Other roles see the class but with no teacher-scope filter.
 */
/**
 * @openapi
 * /api/classes/{id}/students:
 *   get:
 *     tags:
 *       - Classes
 *     summary: Get students in a class
 *     description: Returns flat array of active students. Teachers can only access their own classes.
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *       - in: query
 *         name: date
 *         schema:
 *           type: string
 *           format: date
 *         description: Optional date filter (reserved for future use)
 *     responses:
 *       '200':
 *         description: Array of student objects
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 $ref: '#/components/schemas/Student'
 *       '400':
 *         description: Invalid class id
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 error:
 *                   type: string
 *       '401':
 *         $ref: '#/components/responses/Unauthorized'
 *       '403':
 *         description: Teacher does not teach this class
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 error:
 *                   type: string
 *       '404':
 *         $ref: '#/components/responses/NotFound'
 */
router.get('/:id/students', auth, (req, res) => {
  try {
    const classId = parseInt(req.params.id, 10);
    if (!classId || Number.isNaN(classId)) {
      return res.status(400).json({ error: 'Invalid class id' });
    }
    const cls = getDb().prepare(
      'SELECT id, name, section, class_teacher_id FROM classes WHERE id = ? AND is_deleted = 0'
    ).get(classId);
    if (!cls) return res.status(404).json({ error: 'Class not found' });

    if (req.user.role === 'teacher') {
      const teachesClass = getDb().prepare(
        `SELECT 1 FROM subjects WHERE class_id = ? AND teacher_id = ? AND is_deleted = 0 LIMIT 1`
      ).get(classId, req.user.id);
      if (cls.class_teacher_id !== req.user.id && !teachesClass) {
        return res.status(403).json({ error: 'You do not teach this class' });
      }
    }

    const students = getDb().prepare(`
      SELECT u.id, u.uid, u.first_name, u.last_name, u.email, u.phone, e.roll_number
      FROM enrollments e
      JOIN users u ON e.student_id = u.id
      WHERE e.class_id = ?
        AND e.status = 'active'
        AND e.is_deleted = 0
        AND u.is_deleted = 0
      ORDER BY e.roll_number, u.last_name
    `).all(classId);

    res.json(students);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * POST /api/classes
 * Create a new class section. Sync-aware.
 *
 * Phase 1 PR 3: when `?withAssignments=1` is supplied, the body may
 * also include `classTeacherId` (alias for `class_teacher_id`) and
 * `initialStudentIds: number[]`; both are applied in a single
 * transaction. The response includes the new class plus a summary of
 * the bulk-enrol result so the wizard can render "Created. 25 students
 * added. Class teacher assigned." without a second round-trip.
 */
router.post('/', auth, adminOnly, (req, res) => {
  try {
    const withAssignments = String(req.query.withAssignments || '') === '1';
    const {
      name, section, capacity, class_teacher_id, description,
      classTeacherId, initialStudentIds, academicYear,
    } = req.body || {};
    if (!name) return res.status(400).json({ error: 'Class name is required' });

    const teacherId = classTeacherId || class_teacher_id || null;
    const initialIds = Array.isArray(initialStudentIds) ? initialStudentIds : [];

    const db = getDb();

    // Validate form tutor up front so the all-or-nothing transaction
    // doesn't waste time on a doomed insert.
    if (teacherId) {
      const t = db.prepare("SELECT id, role FROM users WHERE id = ? AND is_deleted = 0").get(teacherId);
      if (!t) return res.status(400).json({ error: 'classTeacherId: teacher not found' });
      if (t.role !== 'teacher') return res.status(400).json({ error: 'classTeacherId: user is not a teacher' });
    }

    if (initialIds.length > 0) {
      const placeholders = initialIds.map(() => '?').join(',');
      const found = db
        .prepare(`SELECT id, role FROM users WHERE id IN (${placeholders}) AND is_deleted = 0`)
        .all(...initialIds);
      const foundIds = new Set(found.map((r) => r.id));
      const nonStudent = found.filter((r) => r.role !== 'student').map((r) => r.id);
      if (nonStudent.length > 0) {
        return res.status(400).json({
          error: 'initialStudentIds: contains non-student users',
          invalidIds: nonStudent,
        });
      }
      const missing = initialIds.filter((id) => !foundIds.has(Number(id)));
      if (missing.length > 0) {
        return res.status(400).json({
          error: 'initialStudentIds: some users were not found',
          missingIds: missing,
        });
      }
    }

    const year = (academicYear || String(new Date().getFullYear())).toString();

    const result = db.transaction(() => {
      const ins = db.prepare(`
        INSERT INTO classes (uid, name, section, capacity, class_teacher_id, description, sync_status)
        VALUES (?, ?, ?, ?, ?, ?, 'pending')
      `).run(uuidv4(), name, section, capacity || 40, teacherId, description || null);
      const classId = ins.lastInsertRowid;

      let added = [];
      if (initialIds.length > 0) {
        const findEnrol = db.prepare(
          "SELECT id FROM enrollments WHERE student_id = ? AND class_id = ? AND is_deleted = 0"
        );
        const insertEnrol = db.prepare(`
          INSERT INTO enrollments (
            uid, student_id, class_id, roll_number, academic_year, section,
            status, request_status, requested_at, approved_by, approved_at,
            sync_status
          ) VALUES (?, ?, ?, ?, ?, ?, 'active', 'approved', NULL, ?, ?, 'pending')
        `);
        for (const rawId of initialIds) {
          const sid = Number(rawId);
          if (findEnrol.get(sid, classId)) continue;
          insertEnrol.run(uuidv4(), sid, classId, null, year, section || '', req.user.id, new Date().toISOString());
          added.push(sid);
        }
      }

      return { classId, added };
    })();

    const created = db.prepare('SELECT * FROM classes WHERE id = ?').get(result.classId);

    AuditService.log(req, 'CLASS_CREATE_WITH_ASSIGNMENTS', {
      entityType: 'class',
      entityId: result.classId,
      classTeacherId: teacherId,
      addedStudentIds: result.added,
      academicYear: year,
    }).catch(() => undefined);

    if (withAssignments) {
      return res.status(201).json({
        class: created,
        classTeacherId: teacherId,
        addedStudentIds: result.added,
        skippedStudentIds: initialIds.filter((id) => !result.added.includes(Number(id))),
        academicYear: year,
      });
    }
    res.status(201).json(created);
  } catch (err) {
    console.error('classes.create error:', err);
    res.status(500).json({ error: err.message });
  }
});

/**
 * PUT /api/classes/:id
 * Update a class section. Sync-aware (sets sync_status='pending').
 */
router.put('/:id', auth, adminOnly, (req, res) => {
  try {
    const { name, section, capacity, class_teacher_id, description } = req.body;
    const result = getDb().prepare(`
      UPDATE classes SET
        name = COALESCE(?, name),
        section = COALESCE(?, section),
        capacity = COALESCE(?, capacity),
        class_teacher_id = COALESCE(?, class_teacher_id),
        description = COALESCE(?, description),
        updated_at = datetime(\'now\', \'localtime\'),
        sync_status = 'pending'
      WHERE id = ? AND is_deleted = 0
    `).run(name, section, capacity, class_teacher_id, description, req.params.id);

    if (result.changes === 0) return res.status(404).json({ error: 'Class not found' });
    res.json(getDb().prepare('SELECT * FROM classes WHERE id = ?').get(req.params.id));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// ============ PHASE 1 PR 3 — class + subject teacher endpoints ============

/**
 * PUT /api/classes/:id/class-teacher
 * Set the form tutor (classes.class_teacher_id). The previous value
 * is overwritten, not soft-deleted; an audit row records the change.
 * Admin-only.
 */
router.put('/:id/class-teacher', auth, adminOnly, (req, res) => {
  try {
    const body = req.body || {};
    const teacherId = body.teacherId !== undefined ? body.teacherId : body.class_teacher_id;
    if (teacherId === undefined) {
      return res.status(400).json({ error: 'teacherId or class_teacher_id is required (use null to clear)' });
    }
    const db = getDb();
    const cls = db.prepare('SELECT id, class_teacher_id FROM classes WHERE id = ? AND is_deleted = 0').get(req.params.id);
    if (!cls) return res.status(404).json({ error: 'Class not found' });

    if (teacherId !== null) {
      const t = db.prepare("SELECT id, role FROM users WHERE id = ? AND is_deleted = 0").get(teacherId);
      if (!t) return res.status(400).json({ error: 'teacherId: teacher not found' });
      if (t.role !== 'teacher') return res.status(400).json({ error: 'teacherId: user is not a teacher' });
    }

    db.prepare(`
      UPDATE classes
      SET class_teacher_id = ?, updated_at = datetime('now','localtime'), sync_status = 'pending'
      WHERE id = ?
    `).run(teacherId, req.params.id);

    AuditService.log(req, 'CLASS_TEACHER_SET', {
      entityType: 'class',
      entityId: Number(req.params.id),
      fromTeacherId: cls.class_teacher_id,
      toTeacherId: teacherId,
    }).catch(() => undefined);

    const updated = db.prepare(`
      SELECT c.*, t.first_name || ' ' || t.last_name AS class_teacher_name
      FROM classes c LEFT JOIN users t ON c.class_teacher_id = t.id
      WHERE c.id = ?
    `).get(req.params.id);
    res.json(updated);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * GET /api/classes/:id/teachers
 * Returns the form tutor + per-subject teachers for the class. Used
 * by the ClassDetail Teachers tab so it can render everything in one
 * round-trip. Read access: any authenticated user (consistent with
 * GET /api/classes/:id).
 *
 * Active covers (from Phase 1 PR 1) are joined in too so the
 * "currently covering" indicator can render without a second call.
 */
router.get('/:id/teachers', auth, (req, res) => {
  try {
    const db = getDb();
    const cls = db.prepare(`
      SELECT c.id, c.name, c.section, c.class_teacher_id,
             t.first_name || ' ' || t.last_name AS class_teacher_name,
             t.email AS class_teacher_email
      FROM classes c
      LEFT JOIN users t ON c.class_teacher_id = t.id
      WHERE c.id = ? AND c.is_deleted = 0
    `).get(req.params.id);
    if (!cls) return res.status(404).json({ error: 'Class not found' });

    const subjectTeachers = db.prepare(`
      SELECT s.id AS subject_id, s.name AS subject_name, s.code AS subject_code,
             s.teacher_id,
             t.first_name || ' ' || t.last_name AS teacher_name,
             t.email AS teacher_email
      FROM subjects s
      LEFT JOIN users t ON s.teacher_id = t.id
      WHERE s.class_id = ? AND s.is_deleted = 0
      ORDER BY s.name
    `).all(req.params.id);

    // Active covers: time-bounded user_roles rows where
    // scope_class_id = this class. Mirrors the helper used by
    // rbac.getActiveCovers but inlined to keep the controller
    // self-contained.
    const now = new Date().toISOString();
    const covers = db.prepare(`
      SELECT ur.id, ur.user_id, ur.role, ur.starts_at, ur.expires_at, ur.reason,
             u.first_name || ' ' || u.last_name AS user_name,
             u.email AS user_email
      FROM user_roles ur
      JOIN users u ON ur.user_id = u.id
      WHERE ur.is_deleted = 0
        AND ur.revoked_at IS NULL
        AND ur.scope_class_id = ?
        AND (ur.starts_at IS NULL OR datetime(ur.starts_at) <= datetime('now','localtime'))
        AND (ur.expires_at IS NULL OR datetime(ur.expires_at) >= datetime('now','localtime'))
      ORDER BY ur.expires_at ASC
    `).all(req.params.id);

    res.json({
      class_teacher: cls.class_teacher_id
        ? { id: cls.class_teacher_id, name: cls.class_teacher_name, email: cls.class_teacher_email }
        : null,
      subject_teachers: subjectTeachers,
      active_covers: covers,
      generated_at: now,
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * POST /api/classes/:id/subjects/:subjectId/teacher
 * Set the teacher on a subject that belongs to the class. Used by
 * the ClassDetail Subjects tab. Admin-only. The subject must already
 * be bound to this class (subjects.class_id) so we don't accidentally
 * re-assign subjects from another class.
 */
router.post('/:id/subjects/:subjectId/teacher', auth, adminOnly, (req, res) => {
  try {
    const { teacherId } = req.body || {};
    if (teacherId === undefined) {
      return res.status(400).json({ error: 'teacherId is required (use null to clear)' });
    }
    const db = getDb();
    const cls = db.prepare('SELECT id, name FROM classes WHERE id = ? AND is_deleted = 0').get(req.params.id);
    if (!cls) return res.status(404).json({ error: 'Class not found' });
    const subj = db.prepare('SELECT id, class_id, teacher_id FROM subjects WHERE id = ? AND is_deleted = 0').get(req.params.subjectId);
    if (!subj) return res.status(404).json({ error: 'Subject not found' });
    if (Number(subj.class_id) !== Number(req.params.id)) {
      return res.status(400).json({ error: 'Subject is not bound to this class' });
    }

    if (teacherId !== null) {
      const t = db.prepare("SELECT id, role FROM users WHERE id = ? AND is_deleted = 0").get(teacherId);
      if (!t) return res.status(400).json({ error: 'teacherId: teacher not found' });
      if (t.role !== 'teacher') return res.status(400).json({ error: 'teacherId: user is not a teacher' });
    }

    db.prepare(`
      UPDATE subjects
      SET teacher_id = ?, updated_at = datetime('now','localtime'), sync_status = 'pending'
      WHERE id = ?
    `).run(teacherId, req.params.subjectId);

    AuditService.log(req, 'SUBJECT_TEACHER_SET', {
      entityType: 'subject',
      entityId: Number(req.params.subjectId),
      classId: Number(req.params.id),
      fromTeacherId: subj.teacher_id,
      toTeacherId: teacherId,
    }).catch(() => undefined);

    const updated = db.prepare(`
      SELECT s.*, t.first_name || ' ' || t.last_name AS teacher_name
      FROM subjects s LEFT JOIN users t ON s.teacher_id = t.id
      WHERE s.id = ?
    `).get(req.params.subjectId);
    res.json(updated);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * DELETE /api/classes/:id
 * Soft delete (is_deleted = 1). Refuses if the class still has active enrollments.
 */
router.delete('/:id', auth, adminOnly, (req, res) => {
  try {
    const studentCount = getDb().prepare(
      "SELECT COUNT(*) as c FROM enrollments WHERE class_id = ? AND status = 'active' AND is_deleted = 0"
    ).get(req.params.id).c;
    if (studentCount > 0) {
      return res.status(400).json({
        error: 'Cannot archive class with active enrollments. Transfer students first.',
      });
    }
    const result = getDb().prepare(`
      UPDATE classes
      SET is_deleted = 1, updated_at = datetime(\'now\', \'localtime\'), sync_status = 'pending'
      WHERE id = ? AND is_deleted = 0
    `).run(req.params.id);
    if (result.changes === 0) return res.status(404).json({ error: 'Class not found' });
    res.json({ success: true, message: 'Class archived' });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

module.exports = router;
