import { Router } from 'express';
import bcrypt from 'bcryptjs';
import { randomBytes } from 'node:crypto';
import {
  findUserByUsername,
  findUserById,
  createUser,
  createSession,
  findSession,
  deleteSession,
} from './db.js';

const SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000;
const BCRYPT_ROUNDS = 8;

const USERNAME_RE = /^[a-zA-Z0-9_.-]{3,64}$/;

function newToken() {
  return randomBytes(24).toString('hex');
}

function sanitizeUser(u) {
  return { id: u.id, username: u.username, role: u.role };
}

export async function validateToken(token) {
  if (!token || typeof token !== 'string') return null;
  const s = await findSession(token);
  if (!s) return null;
  return { id: s.user_id, username: s.username, role: s.role };
}

export function requireAuth(req, res, next) {
  const h = req.get('authorization') || '';
  const m = /^Bearer\s+(.+)$/i.exec(h);
  if (!m) return res.status(401).json({ error: 'Not authenticated' });
  validateToken(m[1])
    .then((user) => {
      if (!user) return res.status(401).json({ error: 'Invalid or expired session' });
      req.user = user;
      req.token = m[1];
      next();
    })
    .catch((err) => res.status(500).json({ error: err.message }));
}

export function requireTeacher(req, res, next) {
  requireAuth(req, res, () => {
    if (req.user.role !== 'teacher') return res.status(403).json({ error: 'Teacher only' });
    next();
  });
}

const router = Router();

router.post('/register', async (req, res) => {
  const { username, password } = req.body || {};
  if (!username || !password) return res.status(400).json({ error: 'username and password required' });
  if (!USERNAME_RE.test(username)) return res.status(400).json({ error: 'username must be 3-64 chars, letters/digits/._-' });
  if (password.length < 4) return res.status(400).json({ error: 'password must be at least 4 characters' });

  try {
    const existing = await findUserByUsername(username);
    if (existing) return res.status(409).json({ error: 'username already taken' });
    const hash = await bcrypt.hash(password, BCRYPT_ROUNDS);
    const user = await createUser({ username, passwordHash: hash, role: 'student' });
    const token = newToken();
    const expiresAt = new Date(Date.now() + SESSION_TTL_MS);
    await createSession({ token, userId: user.id, expiresAt });
    res.json({ token, user: sanitizeUser(user) });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.post('/login', async (req, res) => {
  const { username, password } = req.body || {};
  if (!username || !password) return res.status(400).json({ error: 'username and password required' });

  try {
    const user = await findUserByUsername(username);
    if (!user) return res.status(401).json({ error: 'Invalid username or password' });
    const ok = await bcrypt.compare(password, user.password_hash);
    if (!ok) return res.status(401).json({ error: 'Invalid username or password' });
    const token = newToken();
    const expiresAt = new Date(Date.now() + SESSION_TTL_MS);
    await createSession({ token, userId: user.id, expiresAt });
    res.json({ token, user: sanitizeUser(user) });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.post('/logout', async (req, res) => {
  const h = req.get('authorization') || '';
  const m = /^Bearer\s+(.+)$/i.exec(h);
  if (m) {
    try { await deleteSession(m[1]); } catch { /* ignore */ }
  }
  res.json({ ok: true });
});

router.get('/me', requireAuth, async (req, res) => {
  const fresh = await findUserById(req.user.id);
  if (!fresh) return res.status(401).json({ error: 'User no longer exists' });
  res.json({ user: sanitizeUser(fresh) });
});

export default router;
