const express = require('express');
const cors = require('cors');
const path = require('path');
const Database = require('better-sqlite3');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

// Import controllers
const usersController = require('./controllers/users.controller');
const departmentsController = require('./controllers/departments.controller');
const reportsController = require('./controllers/reports.controller');
const settingsController = require('./controllers/settings.controller');
const coursesController = require('./controllers/courses.controller');
const assignmentsController = require('./controllers/assignments.controller');
const gradesController = require('./controllers/grades.controller');
const attendanceController = require('./controllers/attendance.controller');
const messagesController = require('./controllers/messages.controller');
const calendarController = require('./controllers/calendar.controller');
const examsController = require('./controllers/exams.controller');
const paynowController = require('./controllers/paynow.controller');
const hrController = require('./controllers/hr.controller');
const financeController = require('./controllers/finance.controller');
const teacherController = require('./controllers/teacher.controller');
const socialController = require('./controllers/social.controller');
const { getSyncEngine } = require('./services/SyncEngine');

const app = express();
const PORT = process.env.PORT || 3001;
const JWT_SECRET = process.env.JWT_SECRET || 'africa-alert-secret-key-2024';

// Database setup
const dbPath = process.env.DB_PATH || path.join(__dirname, '../data/school.db');
const db = new Database(dbPath);
db.pragma('foreign_keys = ON');

// Initialize Sync Engine
getSyncEngine();

// Middleware
app.use(cors());

// Prometheus Metrics Setup
const client = require('prom-client');
const register = new client.Registry();
client.collectDefaultMetrics({ register });

const httpRequestDurationSeconds = new client.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'code'],
  buckets: [0.05, 0.1, 0.3, 0.5, 1, 2, 5]
});
register.registerMetric(httpRequestDurationSeconds);

app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = (Date.now() - start) / 1000;
    httpRequestDurationSeconds
      .labels(req.method, req.route ? req.route.path : req.path, res.statusCode)
      .observe(duration);
  });
  next();
});

app.get(['/metrics', '/api/metrics'], async (req, res) => {
  try {
    res.set('Content-Type', register.contentType);
    res.end(await register.metrics());
  } catch (err) {
    res.status(500).end(err);
  }
});
app.use(express.json());
app.use('/uploads', express.static(path.join(__dirname, '../uploads')));

// Register controller routes
app.use('/api/users', usersController);
app.use('/api/departments', departmentsController);
app.use('/api/reports', reportsController);
app.use('/api/settings', settingsController);
app.use('/api/courses', coursesController);
app.use('/api/assignments', assignmentsController);
app.use('/api/grades', gradesController);
app.use('/api/attendance', attendanceController);
app.use('/api/messages', messagesController);
app.use('/api/calendar', calendarController);
app.use('/api/exams', examsController);
app.use('/api/paynow', paynowController);
app.use('/api/hr', hrController);
app.use('/api/finance', financeController);
app.use('/api/teacher', teacherController);
app.use('/api/social', socialController);

// 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 {
    req.user = jwt.verify(token, JWT_SECRET);
    next();
  } catch (err) {
    res.status(401).json({ error: 'Invalid token' });
  }
};

// ============ AUTH ROUTES ============
app.post('/api/auth/login', (req, res) => {
  const { email, password } = req.body;
  const user = db.prepare('SELECT * FROM users WHERE email = ?').get(email);
  
  if (!user || !bcrypt.compareSync(password, user.password)) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  const token = jwt.sign({ id: user.id, email: user.email, role: user.role }, JWT_SECRET, { expiresIn: '7d' });
  res.json({ 
    token, 
    user: { id: user.id, email: user.email, role: user.role, first_name: user.first_name, last_name: user.last_name }
  });
});

app.post('/api/auth/register', (req, res) => {
  const { email, password, role, first_name, last_name, phone } = req.body;
  const { v4: uuidv4 } = require('uuid');
  
  try {
    const hashedPassword = bcrypt.hashSync(password, 10);
    const uid = uuidv4();
    const result = db.prepare(`
      INSERT INTO users (uid, email, password, role, first_name, last_name, phone)
      VALUES (?, ?, ?, ?, ?, ?, ?)
    `).run(uid, email, hashedPassword, role || 'student', first_name, last_name, phone);
    
    res.json({ id: result.lastInsertRowid, email, role: role || 'student' });
  } catch (err) {
    res.status(400).json({ error: 'Email already exists' });
  }
});

// ============ DASHBOARD STATS ============
app.get('/api/dashboard/stats', auth, (req, res) => {
  const stats = {
    totalStudents: db.prepare("SELECT COUNT(*) as count FROM users WHERE role = 'student'").get().count,
    totalTeachers: db.prepare("SELECT COUNT(*) as count FROM users WHERE role = 'teacher'").get().count,
    totalClasses: db.prepare("SELECT COUNT(*) as count FROM classes").get().count,
    totalRevenue: db.prepare("SELECT COALESCE(SUM(amount), 0) as total FROM payments").get().total,
    pendingFees: db.prepare("SELECT COALESCE(SUM(amount - paid_amount), 0) as total FROM student_fees WHERE status != 'paid'").get().total,
    todayAttendance: db.prepare(`
      SELECT 
        COUNT(CASE WHEN status = 'present' THEN 1 END) as present,
        COUNT(*) as total
      FROM attendance WHERE date = date('now')
    `).get()
  };
  res.json(stats);
});

// ============ USERS CRUD ============
app.get('/api/users', auth, (req, res) => {
  const { role } = req.query;
  let query = 'SELECT id, email, role, first_name, last_name, phone, address, image_url, created_at FROM users';
  const params = [];
  if (role) {
    query += ' WHERE role = ?';
    params.push(role);
  }
  query += ' ORDER BY last_name, first_name';
  const users = db.prepare(query).all(...params);
  res.json(users);
});

app.get('/api/users/:id', auth, (req, res) => {
  const user = db.prepare('SELECT id, email, role, first_name, last_name, phone, address, image_url, created_at FROM users WHERE id = ?').get(req.params.id);
  if (!user) return res.status(404).json({ error: 'User not found' });
  res.json(user);
});

app.post('/api/users', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  
  const { email, password, role, first_name, last_name, phone, address } = req.body;
  try {
    const hashedPassword = bcrypt.hashSync(password, 10);
    const result = db.prepare(`
      INSERT INTO users (email, password, role, first_name, last_name, phone, address)
      VALUES (?, ?, ?, ?, ?, ?, ?)
    `).run(email, hashedPassword, role, first_name, last_name, phone, address);
    res.json({ id: result.lastInsertRowid, email, role, first_name, last_name });
  } catch (err) {
    res.status(400).json({ error: 'Email already exists' });
  }
});

app.put('/api/users/:id', auth, (req, res) => {
  const { first_name, last_name, phone, address } = req.body;
  db.prepare(`
    UPDATE users SET first_name = ?, last_name = ?, phone = ?, address = ?, updated_at = CURRENT_TIMESTAMP
    WHERE id = ?
  `).run(first_name, last_name, phone, address, req.params.id);
  res.json({ success: true });
});

app.delete('/api/users/:id', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  db.prepare('DELETE FROM users WHERE id = ?').run(req.params.id);
  res.json({ success: true });
});

// ============ CLASSES CRUD ============
app.get('/api/classes', auth, (req, res) => {
  const classes = db.prepare('SELECT * FROM classes ORDER BY name').all();
  res.json(classes);
});

app.post('/api/classes', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  const { name, section, capacity } = req.body;
  const result = db.prepare('INSERT INTO classes (name, section, capacity) VALUES (?, ?, ?)').run(name, section, capacity);
  res.json({ id: result.lastInsertRowid, name, section, capacity });
});

app.put('/api/classes/:id', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  const { name, section, capacity } = req.body;
  db.prepare('UPDATE classes SET name = ?, section = ?, capacity = ? WHERE id = ?').run(name, section, capacity, req.params.id);
  res.json({ success: true });
});

app.delete('/api/classes/:id', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  db.prepare('DELETE FROM classes WHERE id = ?').run(req.params.id);
  res.json({ success: true });
});

// ============ SUBJECTS CRUD ============
app.get('/api/subjects', auth, (req, res) => {
  const subjects = db.prepare(`
    SELECT s.*, c.name as class_name, u.first_name || ' ' || u.last_name as teacher_name
    FROM subjects s
    LEFT JOIN classes c ON s.class_id = c.id
    LEFT JOIN users u ON s.teacher_id = u.id
    ORDER BY s.name
  `).all();
  res.json(subjects);
});

app.post('/api/subjects', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  const { name, class_id, teacher_id } = req.body;
  const result = db.prepare('INSERT INTO subjects (name, class_id, teacher_id) VALUES (?, ?, ?)').run(name, class_id, teacher_id);
  res.json({ id: result.lastInsertRowid, name, class_id, teacher_id });
});

app.delete('/api/subjects/:id', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  db.prepare('DELETE FROM subjects WHERE id = ?').run(req.params.id);
  res.json({ success: true });
});

// ============ ENROLLMENTS ============
app.get('/api/enrollments', auth, (req, res) => {
  const { class_id } = req.query;
  let query = `
    SELECT e.*, u.first_name, u.last_name, u.email, c.name as class_name, u.id as student_id
    FROM enrollments e
    JOIN users u ON e.student_id = u.id
    JOIN classes c ON e.class_id = c.id
  `;
  const params = [];
  if (class_id) {
    query += ' WHERE e.class_id = ?';
    params.push(class_id);
  }
  const enrollments = db.prepare(query).all(...params);
  res.json(enrollments);
});

app.post('/api/enrollments', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  const { student_id, class_id, roll_number, admission_date } = req.body;
  const result = db.prepare(`
    INSERT INTO enrollments (student_id, class_id, roll_number, admission_date)
    VALUES (?, ?, ?, ?)
  `).run(student_id, class_id, roll_number, admission_date);
  res.json({ id: result.lastInsertRowid, student_id, class_id, roll_number, admission_date });
});

// ============ ATTENDANCE ============
app.get('/api/attendance', auth, (req, res) => {
  const { date, class_id } = req.query;
  let query = `
    SELECT a.*, u.first_name, u.last_name, u.id as student_id
    FROM attendance a
    JOIN users u ON a.student_id = u.id
    WHERE 1=1
  `;
  const params = [];
  if (date) { query += ' AND a.date = ?'; params.push(date); }
  if (class_id) { query += ' AND a.class_id = ?'; params.push(class_id); }
  const attendance = db.prepare(query).all(...params);
  res.json(attendance);
});

app.post('/api/attendance', auth, (req, res) => {
  const { student_id, class_id, date, status } = req.body;
  // Upsert attendance
  const existing = db.prepare('SELECT id FROM attendance WHERE student_id = ? AND date = ?').get(student_id, date);
  if (existing) {
    db.prepare('UPDATE attendance SET status = ? WHERE id = ?').run(status, existing.id);
  } else {
    db.prepare('INSERT INTO attendance (student_id, class_id, date, status) VALUES (?, ?, ?, ?)').run(student_id, class_id, date, status);
  }
  res.json({ success: true });
});

app.post('/api/attendance/bulk', auth, (req, res) => {
  const { records } = req.body;
  const insert = db.prepare('INSERT OR REPLACE INTO attendance (student_id, class_id, date, status) VALUES (?, ?, ?, ?)');
  const insertMany = db.transaction((records) => {
    records.forEach(r => insert.run(r.student_id, r.class_id, r.date, r.status));
  });
  insertMany(records);
  res.json({ success: true, count: records.length });
});

// ============ FEES ============
app.get('/api/fees/groups', auth, (req, res) => {
  const groups = db.prepare('SELECT * FROM fee_groups ORDER BY name').all();
  res.json(groups);
});

app.post('/api/fees/groups', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  const { name, description, amount, due_date } = req.body;
  const result = db.prepare('INSERT INTO fee_groups (name, description, amount, due_date) VALUES (?, ?, ?, ?)').run(name, description, amount, due_date);
  res.json({ id: result.lastInsertRowid });
});

app.get('/api/fees/students', auth, (req, res) => {
  const { student_id } = req.query;
  let query = `
    SELECT sf.*, fg.name as fee_name, fg.amount as total_amount, u.first_name, u.last_name
    FROM student_fees sf
    JOIN fee_groups fg ON sf.fee_group_id = fg.id
    JOIN users u ON sf.student_id = u.id
  `;
  const params = [];
  if (student_id) {
    query += ' WHERE sf.student_id = ?';
    params.push(student_id);
  }
  const fees = db.prepare(query).all(...params);
  res.json(fees);
});

app.post('/api/fees/students', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  const { student_id, fee_group_id, due_date } = req.body;
  const feeGroup = db.prepare('SELECT amount FROM fee_groups WHERE id = ?').get(fee_group_id);
  const result = db.prepare('INSERT INTO student_fees (student_id, fee_group_id, amount, due_date) VALUES (?, ?, ?, ?)').run(student_id, fee_group_id, feeGroup.amount, due_date);
  res.json({ id: result.lastInsertRowid });
});

app.post('/api/fees/pay', auth, (req, res) => {
  const { student_fee_id, amount, payment_method, reference_number, notes } = req.body;
  
  db.transaction(() => {
    db.prepare('INSERT INTO payments (student_fee_id, amount, payment_method, reference_number, notes) VALUES (?, ?, ?, ?, ?)').run(student_fee_id, amount, payment_method, reference_number, notes);
    
    const fee = db.prepare('SELECT * FROM student_fees WHERE id = ?').get(student_fee_id);
    const newPaid = fee.paid_amount + amount;
    const status = newPaid >= fee.amount ? 'paid' : (newPaid > 0 ? 'partial' : 'pending');
    db.prepare('UPDATE student_fees SET paid_amount = ?, status = ? WHERE id = ?').run(newPaid, status, student_fee_id);
  })();
  
  res.json({ success: true });
});

// ============ MESSAGES ============
app.get('/api/messages', auth, (req, res) => {
  const messages = db.prepare(`
    SELECT m.*, 
      (SELECT first_name || ' ' || last_name FROM users WHERE id = m.sender_id) as sender_name,
      (SELECT first_name || ' ' || last_name FROM users WHERE id = m.recipient_id) as recipient_name
    FROM messages m
    WHERE m.recipient_id = ? OR m.sender_id = ?
    ORDER BY m.created_at DESC
  `).all(req.user.id, req.user.id);
  res.json(messages);
});

app.post('/api/messages', auth, (req, res) => {
  const { recipient_id, subject, body } = req.body;
  const result = db.prepare('INSERT INTO messages (sender_id, recipient_id, subject, body) VALUES (?, ?, ?, ?)').run(req.user.id, recipient_id, subject, body);
  res.json({ id: result.lastInsertRowid });
});

// ============ EVENTS ============
app.get('/api/events', auth, (req, res) => {
  const events = db.prepare('SELECT * FROM events ORDER BY event_date').all();
  res.json(events);
});

app.post('/api/events', auth, (req, res) => {
  const { title, description, event_date, event_type } = req.body;
  const result = db.prepare('INSERT INTO events (title, description, event_date, event_type, created_by) VALUES (?, ?, ?, ?, ?)').run(title, description, event_date, event_type, req.user.id);
  res.json({ id: result.lastInsertRowid });
});

// ============ NOTICE BOARD ============
app.get('/api/notices', auth, (req, res) => {
  const notices = db.prepare(`
    SELECT n.*,
      u.first_name || ' ' || u.last_name AS creator_name
    FROM notices n
    LEFT JOIN users u ON u.id = n.created_by
    WHERE n.is_deleted = 0
    ORDER BY n.is_pinned DESC, n.created_at DESC
  `).all();

  res.json(notices);
});

app.post('/api/notices', auth, (req, res) => {
  const {
    title,
    content,
    category,
    priority,
    audience,
    is_pinned,
    expiry_date
  } = req.body;

  const uid = require('uuid').v4();

  const result = db.prepare(`
    INSERT INTO notices (
      uid,
      title,
      content,
      category,
      priority,
      audience,
      is_pinned,
      created_by,
      expiry_date
    )
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(
    uid,
    title,
    content,
    category,
    priority,
    audience,
    is_pinned ? 1 : 0,
    req.user.id,
    expiry_date
  );

  res.json({
    success: true,
    id: result.lastInsertRowid
  });
});

app.put('/api/notices/:id', auth, (req, res) => {
  const { id } = req.params;

  const {
    title,
    content,
    category,
    priority,
    audience,
    is_pinned,
    expiry_date
  } = req.body;

  db.prepare(`
    UPDATE notices
    SET
      title = ?,
      content = ?,
      category = ?,
      priority = ?,
      audience = ?,
      is_pinned = ?,
      expiry_date = ?,
      updated_at = CURRENT_TIMESTAMP
    WHERE id = ?
  `).run(
    title,
    content,
    category,
    priority,
    audience,
    is_pinned ? 1 : 0,
    expiry_date,
    id
  );

  res.json({ success: true });
});

app.delete('/api/notices/:id', auth, (req, res) => {
  const { id } = req.params;

  db.prepare(`
    UPDATE notices
    SET is_deleted = 1
    WHERE id = ?
  `).run(id);

  res.json({ success: true });
});

// ============ LIBRARY ============
app.get('/api/library/books', auth, (req, res) => {
  const books = db.prepare(`
    SELECT *
    FROM library_books
    WHERE is_deleted = 0
    ORDER BY title
  `).all();
  res.json(books);
});

app.post('/api/library/books', auth, (req, res) => {
  const {
    isbn,
    title,
    author,
    category,
    publisher,
    year_published,
    total_copies,
    shelf_location
  } = req.body;
  const uid = require('uuid').v4();
  const result = db.prepare(`
    INSERT INTO library_books (
      uid,
      isbn,
      title,
      author,
      category,
      publisher,
      year_published,
      total_copies,
      available_copies,
      shelf_location
    )
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(
    uid,
    isbn,
    title,
    author,
    category,
    publisher,
    year_published,
    total_copies,
    total_copies,
    shelf_location
  );
  res.json({
    success: true,
    id: result.lastInsertRowid
  });
});

// FIXED: Use correct columns 'total_copies' and adjust 'available_copies'
app.put('/api/library/books/:id', auth, (req, res) => {
  const { id } = req.params;
  const {
    title,
    author,
    isbn,
    category,
    publisher,
    year_published,
    total_copies,
    shelf_location
  } = req.body;

  // Get current book data
  const current = db.prepare('SELECT total_copies, available_copies FROM library_books WHERE id = ?').get(id);
  if (!current) {
    return res.status(404).json({ error: 'Book not found' });
  }

  const oldTotal = current.total_copies;
  const newTotal = total_copies !== undefined ? total_copies : oldTotal;
  const delta = newTotal - oldTotal;

  // Update the book metadata
  db.prepare(`
    UPDATE library_books
    SET
      title = COALESCE(?, title),
      author = COALESCE(?, author),
      isbn = COALESCE(?, isbn),
      category = COALESCE(?, category),
      publisher = COALESCE(?, publisher),
      year_published = COALESCE(?, year_published),
      total_copies = ?,
      shelf_location = COALESCE(?, shelf_location),
      available_copies = available_copies + ?,
      updated_at = CURRENT_TIMESTAMP
    WHERE id = ?
  `).run(
    title,
    author,
    isbn,
    category,
    publisher,
    year_published,
    newTotal,
    shelf_location,
    delta, // increase available copies by the same delta (cannot go negative; we rely on client input validity)
    id
  );

  // Ensure available_copies does not become negative (safety)
  db.prepare(`
    UPDATE library_books
    SET available_copies = MAX(0, available_copies)
    WHERE id = ?
  `).run(id);

  res.json({ success: true });
});

app.delete('/api/library/books/:id', auth, (req, res) => {
  const { id } = req.params;
  db.prepare(`
    DELETE FROM library_books
    WHERE id = ?
  `).run(id);
  res.json({ success: true });
});

// ============ LIBRARY ISSUES ============
// Get issued books
app.get('/api/library/issues', auth, (req, res) => {
  const issues = db.prepare(`
    SELECT
      li.*,
      lb.title as book_title,
      lb.author as book_author
    FROM library_issues li
    LEFT JOIN library_books lb ON li.book_id = lb.id
    WHERE li.is_deleted = 0
    ORDER BY li.issue_date DESC
  `).all();
  res.json(issues);
});

// Issue a book
app.post('/api/library/issues', auth, (req, res) => {
  const { book_id, user_id, due_date } = req.body;
  const book = db.prepare(`
    SELECT *
    FROM library_books
    WHERE id = ?
  `).get(book_id);
  if (!book) {
    return res.status(404).json({ error: 'Book not found' });
  }
  if (book.available_copies <= 0) {
    return res.status(400).json({ error: 'No copies available' });
  }
  const uid = require('uuid').v4();
  const result = db.prepare(`
    INSERT INTO library_issues (
      uid,
      book_id,
      user_id,
      issue_date,
      due_date,
      status,
      issued_by
    )
    VALUES (?, ?, ?, date('now'), ?, 'issued', ?)
  `).run(
    uid,
    book_id,
    user_id,
    due_date,
    req.user.id
  );
  db.prepare(`
    UPDATE library_books
    SET available_copies = available_copies - 1
    WHERE id = ?
  `).run(book_id);
  res.json({
    success: true,
    id: result.lastInsertRowid
  });
});

// Return a book
app.put('/api/library/issues/:id/return', auth, (req, res) => {
  const { id } = req.params;
  const issue = db.prepare(`
    SELECT *
    FROM library_issues
    WHERE id = ?
  `).get(id);
  if (!issue) {
    return res.status(404).json({ error: 'Issue record not found' });
  }
  db.prepare(`
    UPDATE library_issues
    SET
      return_date = date('now'),
      status = 'returned'
    WHERE id = ?
  `).run(id);
  db.prepare(`
    UPDATE library_books
    SET available_copies = available_copies + 1
    WHERE id = ?
  `).run(issue.book_id);
  res.json({ success: true });
});

// ============ EXPENSES ============
app.get('/api/expenses', auth, (req, res) => {
  const expenses = db.prepare(`
    SELECT e.*, u.first_name || ' ' || u.last_name as created_by_name
    FROM expenses e
    LEFT JOIN users u ON e.created_by = u.id
    ORDER BY e.expense_date DESC
  `).all();
  res.json(expenses);
});

app.post('/api/expenses', auth, (req, res) => {
  if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin only' });
  const { category, description, amount, expense_date } = req.body;
  const result = db.prepare('INSERT INTO expenses (category, description, amount, expense_date, created_by) VALUES (?, ?, ?, ?, ?)').run(category, description, amount, expense_date, req.user.id);
  res.json({ id: result.lastInsertRowid });
});

// Serve static frontend files in production
app.use(express.static(path.join(__dirname, '../../client/dist')));
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, '../../client/dist/index.html'));
});

// Start server
app.listen(PORT, () => {
  console.log(`Africa Alert API running on http://localhost:${PORT}`);
});

module.exports = app;
