/**
 * Academic Rollover / Student Promotion Controller
 * Handles academic term transitions and academic year-end student promotions.
 */

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

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-only middleware (Systems or School admin). Re-exported as
// `requireStrictAdmin` from src/middleware/requireRole so other controllers
// can use the same gate without copying the predicate.
const { requireStrictAdmin: adminOnly } = require('../middleware/requireRole');

// ============ GET DATA ============
// Fetch current settings, classes, and students list for transition setup
/**
 * @openapi
 * /api/academic-rollover/data:
 *   get:
 *     summary: Preview rollover data
 *     description: |
 *       Returns the aggregated current-year data the rollover
 *       will affect: academic settings (`academic_year`,
 *       `term`, `term_start_date`, `term_end_date`), active
 *       classes, and active student enrollments for the
 *       current academic year joined with student and class
 *       details. Used by the UI to render the rollover preview
 *       and class/student mapping forms before executing.
 *       Requires `systems_admin` or `school_admin`.
 *     tags:
 *       - Academic Rollover
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Preview data
 *         content:
 *           application/json:
 *             schema: { type: object }
 *       401: { description: Unauthorized }
 *       403:
 *         description: Requires `systems_admin` or `school_admin`
 *       500: { description: Server error }
 */
router.get('/data', auth, adminOnly, (req, res) => {
  try {
    const database = getDb();

    // 1. Get academic settings
    const yearSetting = database.prepare(`
      SELECT value FROM system_settings WHERE key = 'academic_year' AND is_deleted = 0
    `).get();
    const currentAcademicYear = yearSetting ? yearSetting.value : new Date().getFullYear().toString();

    const termSetting = database.prepare(`
      SELECT value FROM system_settings WHERE key = 'term' AND is_deleted = 0
    `).get();
    const currentTerm = termSetting ? termSetting.value : 'Term 1';

    const startDateSetting = database.prepare(`
      SELECT value FROM system_settings WHERE key = 'term_start_date' AND is_deleted = 0
    `).get();
    const termStartDate = startDateSetting ? startDateSetting.value : '';

    const endDateSetting = database.prepare(`
      SELECT value FROM system_settings WHERE key = 'term_end_date' AND is_deleted = 0
    `).get();
    const termEndDate = endDateSetting ? endDateSetting.value : '';

    // 2. Get active classes
    const classes = database.prepare(`
      SELECT id, name, section, capacity
      FROM classes
      WHERE is_deleted = 0
      ORDER BY name, section
    `).all();

    // 3. Get active student enrollments for the current academic year.
    //    A student should have at most one active enrollment per academic
    //    year in the data model, but historical data may have multiple.
    //    Pick the most recent (highest id) per student so the row key
    //    (`student_id`) is unique.
    const students = database.prepare(`
      SELECT
        u.id as student_id,
        u.first_name,
        u.last_name,
        u.email,
        e.id as enrollment_id,
        e.class_id,
        e.roll_number,
        e.academic_year,
        c.name as class_name,
        c.section as class_section
      FROM (
        SELECT student_id, MAX(id) AS enrollment_id
        FROM enrollments
        WHERE status = 'active'
          AND is_deleted = 0
          AND academic_year = ?
        GROUP BY student_id
      ) latest
      JOIN enrollments e ON e.id = latest.enrollment_id
      JOIN users u ON u.id = latest.student_id AND u.is_deleted = 0 AND u.role = 'student'
      JOIN classes c ON c.id = e.class_id
      ORDER BY c.name, c.section, u.last_name, u.first_name
    `).all(currentAcademicYear);

    res.json({
      currentAcademicYear,
      currentTerm,
      termStartDate,
      termEndDate,
      classes,
      students
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// ============ RUN ROLLOVER ============
// Process the transition (Term advance or Year rollover)
/**
 * @openapi
 * /api/academic-rollover/run:
 *   post:
 *     summary: Execute academic rollover (destructive)
 *     description: |
 *       Executes the academic transition. **Destructive** —
 *       this rewrites enrollment rows and system settings and
 *       cannot be undone. Always call `GET /data` first and
 *       review the preview before invoking this endpoint.
 *
 *       Two modes are supported via `mode`:
 *
 *       - `term` (default for term transitions): applies the
 *         per-student `studentExceptions` (`transfer` or
 *         `graduate`) and updates `term`,
 *         `term_start_date`, `term_end_date`. `nextTerm` is
 *         required.
 *       - `year`: runs the full year-end rollover. Promotes
 *         every active enrollment using `classMappings`
 *         (`currentClassId` -> `nextClassId`, or the literal
 *         string `'graduate'`), honors per-student
 *         `studentExceptions` (`promote`, `repeat`,
 *         `transfer`, `graduate`), then updates
 *         `academic_year`, `term`, and term dates.
 *         `nextAcademicYear` must differ from the current
 *         value.
 *
 *       Requires `systems_admin` or `school_admin`.
 *     tags:
 *       - Academic Rollover
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - nextTerm
 *             properties:
 *               mode:
 *                 type: string
 *                 enum: [year, term]
 *                 description: Defaults to `year`.
 *               nextAcademicYear:
 *                 type: string
 *                 description: Required when `mode = year`. Must differ from current.
 *               nextTerm:
 *                 type: string
 *                 description: Term label for the new period (e.g. `Term 2`).
 *               termStartDate:
 *                 type: string
 *                 format: date
 *               termEndDate:
 *                 type: string
 *                 format: date
 *               classMappings:
 *                 type: array
 *                 description: Required when `mode = year`.
 *                 items:
 *                   type: object
 *                   properties:
 *                     currentClassId: { type: integer }
 *                     nextClassId:
 *                       type: string
 *                       description: |
 *                         Target class id, or the literal
 *                         string `'graduate'` to mark the
 *                         cohort as graduating.
 *               studentExceptions:
 *                 type: array
 *                 items:
 *                   type: object
 *                   properties:
 *                     studentId: { type: integer }
 *                     action:
 *                       type: string
 *                       enum: [promote, repeat, transfer, graduate]
 *     responses:
 *       200:
 *         description: Rollover completed
 *         content:
 *           application/json:
 *             schema: { type: object }
 *       400: { description: Validation error }
 *       401: { description: Unauthorized }
 *       403:
 *         description: Requires `systems_admin` or `school_admin`
 *       500: { description: Server error }
 */
router.post('/run', auth, adminOnly, (req, res) => {
  try {
    const { 
      mode = 'year', // 'year' | 'term'
      nextAcademicYear, 
      nextTerm, 
      termStartDate, 
      termEndDate, 
      classMappings, 
      studentExceptions 
    } = req.body;

    if (!nextTerm) {
      return res.status(400).json({ error: 'nextTerm is required' });
    }

    const database = getDb();

    // Get current settings
    const yearSetting = database.prepare(`
      SELECT value FROM system_settings WHERE key = 'academic_year' AND is_deleted = 0
    `).get();
    const currentAcademicYear = yearSetting ? yearSetting.value : new Date().getFullYear().toString();

    const updateSystemSetting = database.prepare(`
      UPDATE system_settings
      SET value = ?, updated_at = datetime(\'now\', \'localtime\'), sync_status = 'pending'
      WHERE key = ?
    `);

    // --- TERM TRANSITION ONLY ---
    if (mode === 'term') {
      const exceptionMap = new Map(); // studentId -> action
      if (Array.isArray(studentExceptions)) {
        studentExceptions.forEach(e => {
          exceptionMap.set(Number(e.studentId), e.action);
        });
      }

      const updateEnrollmentStatus = database.prepare(`
        UPDATE enrollments
        SET status = ?, updated_at = datetime(\'now\', \'localtime\'), sync_status = 'pending'
        WHERE student_id = ? AND academic_year = ? AND status = 'active' AND is_deleted = 0
      `);

      let transferredCount = 0;
      let graduatedCount = 0;

      const runTermTx = database.transaction(() => {
        // Process student flags (Transfer or Graduate mid-year)
        for (const [studentId, action] of exceptionMap.entries()) {
          if (action === 'transfer') {
            updateEnrollmentStatus.run('transferred', studentId, currentAcademicYear);
            transferredCount++;
          } else if (action === 'graduate') {
            updateEnrollmentStatus.run('graduated', studentId, currentAcademicYear);
            graduatedCount++;
          }
        }

        updateSystemSetting.run(nextTerm, 'term');
        if (termStartDate) updateSystemSetting.run(termStartDate, 'term_start_date');
        if (termEndDate) updateSystemSetting.run(termEndDate, 'term_end_date');
      });

      runTermTx();

      return res.status(200).json({
        success: true,
        message: `Academic term successfully transitioned to ${nextTerm}.`,
        stats: {
          mode: 'term',
          nextTerm,
          termStartDate,
          termEndDate,
          transferred: transferredCount,
          graduated: graduatedCount,
          total: exceptionMap.size
        }
      });
    }

    // --- FULL YEAR ROLLOVER ---
    if (!nextAcademicYear) {
      return res.status(400).json({ error: 'nextAcademicYear is required for year rollover' });
    }

    if (currentAcademicYear === nextAcademicYear) {
      return res.status(400).json({ error: 'Next academic year must be different from current academic year' });
    }

    if (!Array.isArray(classMappings)) {
      return res.status(400).json({ error: 'classMappings must be an array' });
    }

    // Fetch active enrollments for current year
    const activeEnrollments = database.prepare(`
      SELECT id, student_id, class_id, roll_number, section
      FROM enrollments
      WHERE academic_year = ? AND status = 'active' AND is_deleted = 0
    `).all(currentAcademicYear);

    // Create lookup maps
    const classMap = new Map(); // currentClassId -> nextClassId ('graduate' or number)
    classMappings.forEach(m => {
      classMap.set(Number(m.currentClassId), m.nextClassId);
    });

    const exceptionMap = new Map(); // studentId -> action ('repeat' | 'transfer' | 'graduate' | 'promote')
    if (Array.isArray(studentExceptions)) {
      studentExceptions.forEach(e => {
        exceptionMap.set(Number(e.studentId), e.action);
      });
    }

    // Prepared statements for year transition
    const updateEnrollmentStatus = database.prepare(`
      UPDATE enrollments
      SET status = ?, updated_at = datetime(\'now\', \'localtime\'), sync_status = 'pending'
      WHERE id = ?
    `);

    const insertEnrollment = database.prepare(`
      INSERT INTO enrollments (uid, student_id, class_id, section, roll_number, academic_year, status, sync_status)
      VALUES (?, ?, ?, ?, ?, ?, 'active', 'pending')
    `);

    let promotedCount = 0;
    let repeatedCount = 0;
    let transferredCount = 0;
    let graduatedCount = 0;

    // Run year rollover transaction
    const runYearRolloverTx = database.transaction(() => {
      for (const env of activeEnrollments) {
        const studentId = env.student_id;
        const currentClassId = env.class_id;
        
        // 1. Determine action
        let action = exceptionMap.get(studentId);
        if (!action) {
          action = 'promote'; // default
        }

        // 2. Perform action
        if (action === 'repeat') {
          // Deactivate old enrollment (completed)
          updateEnrollmentStatus.run('inactive', env.id);
          // Re-enroll in current class for next year
          insertEnrollment.run(uuidv4(), studentId, currentClassId, env.section, env.roll_number, nextAcademicYear);
          repeatedCount++;
        } else if (action === 'transfer') {
          // Mark as transferred
          updateEnrollmentStatus.run('transferred', env.id);
          transferredCount++;
        } else if (action === 'graduate') {
          // Mark as graduated
          updateEnrollmentStatus.run('graduated', env.id);
          graduatedCount++;
        } else {
          // promote
          const nextClassId = classMap.get(currentClassId);
          if (nextClassId === 'graduate' || !nextClassId) {
            // Graduate student
            updateEnrollmentStatus.run('graduated', env.id);
            graduatedCount++;
          } else {
            // Deactivate old enrollment
            updateEnrollmentStatus.run('inactive', env.id);
            // Enroll in next class
            insertEnrollment.run(uuidv4(), studentId, Number(nextClassId), env.section, env.roll_number, nextAcademicYear);
            promotedCount++;
          }
        }
      }

      // 3. Update settings
      updateSystemSetting.run(nextAcademicYear, 'academic_year');
      updateSystemSetting.run(nextTerm, 'term');
      if (termStartDate) updateSystemSetting.run(termStartDate, 'term_start_date');
      if (termEndDate) updateSystemSetting.run(termEndDate, 'term_end_date');
    });

    runYearRolloverTx();

    res.status(200).json({
      success: true,
      message: `Academic rollover to ${nextAcademicYear} (${nextTerm}) executed successfully.`,
      stats: {
        mode: 'year',
        promoted: promotedCount,
        repeated: repeatedCount,
        transferred: transferredCount,
        graduated: graduatedCount,
        total: activeEnrollments.length
      }
    });

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

module.exports = router;
