/**
 * Notices Controller
 * CRUD for the `notices` table — the school's communication hub
 * (notice board / notification system). 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
 *
 * Audience filtering on GET /:
 *   - admins (school_admin / systems_admin / principal) see every notice
 *   - everyone else sees only notices where audience matches their role
 *     ("All", or their own role name e.g. "Teachers", "Students", "Parents")
 *
 * Mounted at /api/notices 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 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;
};

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' });
  }
};

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();
};

const isAdmin = (role) => ['school_admin', 'systems_admin', 'principal'].includes(role);

// Map internal role -> notice audience token (matches what the form saves)
const audienceForRole = (role) => {
  switch (role) {
    case 'teacher': return 'Teachers';
    case 'student': return 'Students';
    case 'parent': return 'Parents';
    default: return null; // admins / others only see "All"
  }
};

// ============ NOTICES CRUD ============

/**
 * GET /api/notices
 * Optional query: category, search, include_expired (default true)
 * Admins see everything. Non-admins see notices where audience = 'All'
 * or audience = their role token.
 */
/**
 * @openapi
 * /api/notices:
 *   get:
 *     summary: List notices
 *     description: |
 *       Returns active notices ordered by `is_pinned` then `created_at`
 *       descending. Admins (`school_admin`, `systems_admin`,
 *       `principal`) see every notice; non-admins see only notices
 *       where `audience` is `All` or matches their role token
 *       (`Teachers`, `Students`, `Parents`). Optional filters:
 *       `category`, free-text `search` against title/content, and
 *       `include_expired` (defaults to `true`).
 *     tags:
 *       - Notices
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: category
 *         schema: { type: string }
 *       - in: query
 *         name: search
 *         schema: { type: string }
 *       - in: query
 *         name: include_expired
 *         schema: { type: boolean }
 *     responses:
 *       200:
 *         description: Notice list
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items: { type: object }
 *       401: { description: Unauthorized }
 *       500: { description: Server error }
 *   post:
 *     summary: Create notice
 *     description: |
 *       Creates a notice. Publishing is allowed for admins, principals,
 *       teachers, HR, and bursar; other roles are rejected. Default
 *       `category` is `Academic`, default `priority` is `Normal`, and
 *       default `audience` is `All`.
 *     tags:
 *       - Notices
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - title
 *               - content
 *             properties:
 *               title: { type: string }
 *               content: { type: string }
 *               category: { type: string }
 *               priority: { type: string }
 *               audience: { type: string }
 *               is_pinned: { type: boolean }
 *               expiry_date: { type: string, format: date-time }
 *     responses:
 *       201:
 *         description: Created notice
 *         content:
 *           application/json:
 *             schema: { type: object }
 *       400: { description: Title and content are required }
 *       401: { description: Unauthorized }
 *       403: { description: Cannot publish notices }
 *       500: { description: Server error }
 */
router.get('/', auth, (req, res) => {
  try {
    const { category, search, include_expired } = req.query;
    const includeExpired = include_expired !== 'false';

    let query = `
      SELECT n.*,
        u.first_name || ' ' || u.last_name as creator_name,
        u.role as creator_role
      FROM notices n
      LEFT JOIN users u ON n.created_by = u.id
      WHERE n.is_deleted = 0
    `;
    const params = [];

    if (!includeExpired) {
      query += ' AND (n.expiry_date IS NULL OR date(n.expiry_date) >= date(\'now\'))';
    }

    if (!isAdmin(req.user.role)) {
      const myAudience = audienceForRole(req.user.role);
      if (myAudience) {
        query += ' AND (n.audience = ? OR n.audience = ?)';
        params.push('All', myAudience);
      } else {
        query += ' AND n.audience = ?';
        params.push('All');
      }
    }

    if (category && category !== 'All') {
      query += ' AND n.category = ?';
      params.push(category);
    }
    if (search) {
      query += ' AND (n.title LIKE ? OR n.content LIKE ?)';
      const term = `%${search}%`;
      params.push(term, term);
    }

    query += ' ORDER BY n.is_pinned DESC, n.created_at DESC';
    res.json(getDb().prepare(query).all(...params));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * GET /api/notices/:id
 */
/**
 * @openapi
 * /api/notices/{id}:
 *   get:
 *     summary: Get notice by id
 *     description: |
 *       Returns a single notice with creator name and role. Non-admin
 *       callers can only read notices where `audience` is `All` or
 *       matches their role token.
 *     tags:
 *       - Notices
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema: { type: integer }
 *     responses:
 *       200:
 *         description: Notice detail
 *         content:
 *           application/json:
 *             schema: { type: object }
 *       401: { description: Unauthorized }
 *       403: { description: Not authorized to view this notice }
 *       404: { description: Notice not found }
 *       500: { description: Server error }
 *   put:
 *     summary: Update notice
 *     description: |
 *       Updates an existing notice. Editors are the original creator
 *       or an admin/principal. Unspecified fields are left unchanged
 *       (`COALESCE`).
 *     tags:
 *       - Notices
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema: { type: integer }
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               title: { type: string }
 *               content: { type: string }
 *               category: { type: string }
 *               priority: { type: string }
 *               audience: { type: string }
 *               is_pinned: { type: boolean }
 *               expiry_date: { type: string, format: date-time }
 *     responses:
 *       200:
 *         description: Updated notice
 *         content:
 *           application/json:
 *             schema: { type: object }
 *       401: { description: Unauthorized }
 *       403: { description: Not authorized to edit this notice }
 *       404: { description: Notice not found }
 *       500: { description: Server error }
 *   delete:
 *     summary: Soft-delete notice
 *     description: |
 *       Marks the notice as deleted. Restricted to admins and
 *       principals.
 *     tags:
 *       - Notices
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema: { type: integer }
 *     responses:
 *       200:
 *         description: Archived
 *         content:
 *           application/json:
 *             schema: { type: object }
 *       401: { description: Unauthorized }
 *       403: { description: Admin or Principal only }
 *       404: { description: Notice not found }
 *       500: { description: Server error }
 */
router.get('/:id', auth, (req, res) => {
  try {
    const notice = getDb().prepare(`
      SELECT n.*,
        u.first_name || ' ' || u.last_name as creator_name,
        u.role as creator_role
      FROM notices n
      LEFT JOIN users u ON n.created_by = u.id
      WHERE n.id = ? AND n.is_deleted = 0
    `).get(req.params.id);
    if (!notice) return res.status(404).json({ error: 'Notice not found' });

    // Non-admins can only read notices targeted at them
    if (!isAdmin(req.user.role)) {
      const myAudience = audienceForRole(req.user.role);
      const visible = notice.audience === 'All' || notice.audience === myAudience;
      if (!visible) return res.status(403).json({ error: 'Not authorized to view this notice' });
    }
    res.json(notice);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * POST /api/notices
 * Admins / teachers / HR / bursar may create notifications.
 */
router.post('/', auth, (req, res) => {
  try {
    const allowed = ['school_admin', 'systems_admin', 'principal', 'teacher', 'hr', 'bursar'];
    if (!allowed.includes(req.user.role)) {
      return res.status(403).json({ error: 'You do not have permission to publish notices' });
    }

    const { title, content, category, priority, audience, is_pinned, expiry_date } = req.body;
    if (!title || !content) {
      return res.status(400).json({ error: 'Title and content are required' });
    }

    const result = getDb().prepare(`
      INSERT INTO notices (uid, title, content, category, priority, audience, is_pinned, created_by, expiry_date, sync_status)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending')
    `).run(
      uuidv4(),
      title,
      content,
      category || 'Academic',
      priority || 'Normal',
      audience || 'All',
      is_pinned ? 1 : 0,
      req.user.id,
      expiry_date || null
    );
    res.status(201).json(getDb().prepare('SELECT * FROM notices WHERE id = ?').get(result.lastInsertRowid));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * PUT /api/notices/:id
 * Editors: the original creator OR an admin/principal.
 */
router.put('/:id', auth, (req, res) => {
  try {
    const existing = getDb().prepare(
      'SELECT * FROM notices WHERE id = ? AND is_deleted = 0'
    ).get(req.params.id);
    if (!existing) return res.status(404).json({ error: 'Notice not found' });

    const canEdit = isAdmin(req.user.role) || existing.created_by === req.user.id;
    if (!canEdit) return res.status(403).json({ error: 'Not authorized to edit this notice' });

    const { title, content, category, priority, audience, is_pinned, expiry_date } = req.body;
    getDb().prepare(`
      UPDATE notices SET
        title = COALESCE(?, title),
        content = COALESCE(?, content),
        category = COALESCE(?, category),
        priority = COALESCE(?, priority),
        audience = COALESCE(?, audience),
        is_pinned = COALESCE(?, is_pinned),
        expiry_date = COALESCE(?, expiry_date),
        updated_at = CURRENT_TIMESTAMP,
        sync_status = 'pending'
      WHERE id = ? AND is_deleted = 0
    `).run(
      title,
      content,
      category,
      priority,
      audience,
      is_pinned === undefined ? null : (is_pinned ? 1 : 0),
      expiry_date,
      req.params.id
    );
    res.json(getDb().prepare('SELECT * FROM notices WHERE id = ?').get(req.params.id));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

/**
 * DELETE /api/notices/:id
 * Soft delete. Only admins/principals may delete.
 */
router.delete('/:id', auth, adminOnly, (req, res) => {
  try {
    const result = getDb().prepare(`
      UPDATE notices
      SET is_deleted = 1, updated_at = CURRENT_TIMESTAMP, sync_status = 'pending'
      WHERE id = ? AND is_deleted = 0
    `).run(req.params.id);
    if (result.changes === 0) return res.status(404).json({ error: 'Notice not found' });
    res.json({ success: true, message: 'Notice archived' });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

module.exports = router;
