import { Router } from 'express';
import bcrypt from 'bcryptjs';
import { v4 as uuidv4 } from 'uuid';
import { getDb } from '../config/database.js';
import { authenticateToken, requireAdmin, getBaseUrl } from '../middleware/auth.js';
import { isMailerConfigured, sendInviteEmail } from '../config/mailer.js';
import { getAppName } from '../config/appName.js';
import { log } from '../config/logger.js';
import {
  getOAuthConfig,
  saveOAuthConfig,
  deleteOAuthConfig,
  discoverOIDC,
} from '../config/oauth.js';

const EMAIL_RE = /^[^\s@]{1,64}@[^\s@]{1,253}\.[^\s@]{2,}$/;

const router = Router();

// POST /api/admin/users - Create user (admin)
router.post('/users', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { name, display_name, email, password, role } = req.body;

    if (!name || !display_name || !email || !password) {
      return res.status(400).json({ error: 'All fields are required' });
    }

    // L4: display_name length limit
    if (display_name.length > 100) {
      return res.status(400).json({ error: 'Display name must not exceed 100 characters' });
    }

    const usernameRegex = /^[a-zA-Z0-9_-]{3,30}$/;
    if (!usernameRegex.test(name)) {
      return res.status(400).json({ error: 'Username may only contain letters, numbers, _ and - (3-30 chars)' });
    }

    if (password.length < 8) {
      return res.status(400).json({ error: 'Password must be at least 8 characters long' });
    }
    if (password.length > 64) {
      return res.status(400).json({ error: 'Password must not exceed 64 characters' });
    }

    // M9: email format validation
    if (!EMAIL_RE.test(email)) {
      return res.status(400).json({ error: 'Invalid email address' });
    }

    const validRole = ['user', 'admin'].includes(role) ? role : 'user';
    const db = getDb();

    const existing = await db.get('SELECT id FROM users WHERE email = ?', [email.toLowerCase()]);
    if (existing) {
      return res.status(409).json({ error: 'Email is already in use' });
    }

    const existingUsername = await db.get('SELECT id FROM users WHERE LOWER(name) = LOWER(?)', [name]);
    if (existingUsername) {
      return res.status(409).json({ error: 'Username is already taken' });
    }

    const hash = await bcrypt.hash(password, 12);
    const result = await db.run(
      'INSERT INTO users (name, display_name, email, password_hash, role, email_verified) VALUES (?, ?, ?, ?, ?, 1)',
      [name, display_name, email.toLowerCase(), hash, validRole]
    );

    const user = await db.get('SELECT id, name, display_name, email, role, created_at FROM users WHERE id = ?', [result.lastInsertRowid]);
    res.status(201).json({ user });
  } catch (err) {
    log.admin.error(`Create user error: ${err.message}`);
    res.status(500).json({ error: 'User could not be created' });
  }
});

// GET /api/admin/users - List all users
router.get('/users', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const db = getDb();
    const users = await db.all(`
      SELECT id, name, display_name, email, role, language, theme, avatar_color, avatar_image, created_at,
        (SELECT COUNT(*) FROM rooms WHERE rooms.user_id = users.id) as room_count
      FROM users
      ORDER BY created_at DESC
    `);

    res.json({ users });
  } catch (err) {
    log.admin.error(`List users error: ${err.message}`);
    res.status(500).json({ error: 'Users could not be loaded' });
  }
});

// PUT /api/admin/users/:id/role - Update user role
router.put('/users/:id/role', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { role } = req.body;
    if (!['user', 'admin'].includes(role)) {
      return res.status(400).json({ error: 'Invalid role' });
    }

    const db = getDb();
    
    // Prevent demoting last admin
    if (role === 'user') {
      const adminCount = await db.get('SELECT COUNT(*) as count FROM users WHERE role = ?', ['admin']);
      const currentUser = await db.get('SELECT role FROM users WHERE id = ?', [req.params.id]);
      if (currentUser?.role === 'admin' && adminCount.count <= 1) {
        return res.status(400).json({ error: 'The last admin cannot be demoted' });
      }
    }

    await db.run('UPDATE users SET role = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', [role, req.params.id]);
    const updated = await db.get('SELECT id, name, email, role, created_at FROM users WHERE id = ?', [req.params.id]);

    // S7: verify user actually exists
    if (!updated) {
      return res.status(404).json({ error: 'User not found' });
    }

    res.json({ user: updated });
  } catch (err) {
    log.admin.error(`Update role error: ${err.message}`);
    res.status(500).json({ error: 'Role could not be updated' });
  }
});

// DELETE /api/admin/users/:id - Delete user
router.delete('/users/:id', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const db = getDb();

    if (parseInt(req.params.id) === req.user.id) {
      return res.status(400).json({ error: 'You cannot delete yourself' });
    }

    const user = await db.get('SELECT id, role FROM users WHERE id = ?', [req.params.id]);
    if (!user) {
      return res.status(404).json({ error: 'User not found' });
    }

    // Check if it's the last admin
    if (user.role === 'admin') {
      const adminCount = await db.get('SELECT COUNT(*) as count FROM users WHERE role = ?', ['admin']);
      if (adminCount.count <= 1) {
        return res.status(400).json({ error: 'The last admin cannot be deleted' });
      }
    }

    await db.run('DELETE FROM users WHERE id = ?', [req.params.id]);
    res.json({ message: 'User deleted' });
  } catch (err) {
    log.admin.error(`Delete user error: ${err.message}`);
    res.status(500).json({ error: 'User could not be deleted' });
  }
});

// PUT /api/admin/users/:id/password - Reset user password (admin)
router.put('/users/:id/password', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { newPassword } = req.body;
    if (!newPassword || typeof newPassword !== 'string' || newPassword.length < 8) {
      return res.status(400).json({ error: 'Password must be at least 8 characters long' });
    }
    if (newPassword.length > 64) {
      return res.status(400).json({ error: 'Password must not exceed 64 characters' });
    }

    const db = getDb();
    const hash = await bcrypt.hash(newPassword, 12);
    await db.run('UPDATE users SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', [hash, req.params.id]);

    res.json({ message: 'Password reset' });
  } catch (err) {
    log.admin.error(`Reset password error: ${err.message}`);
    res.status(500).json({ error: 'Password could not be reset' });
  }
});

// ── User Invite System ─────────────────────────────────────────────────────

// POST /api/admin/invites - Create and send an invite
router.post('/invites', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { email } = req.body;
    if (!email || !EMAIL_RE.test(email)) {
      return res.status(400).json({ error: 'A valid email address is required' });
    }

    const db = getDb();

    // Check if user with this email already exists
    const existing = await db.get('SELECT id FROM users WHERE email = ?', [email.toLowerCase()]);
    if (existing) {
      return res.status(409).json({ error: 'A user with this email already exists' });
    }

    // Check if there's already a pending invite for this email
    const existingInvite = await db.get(
      'SELECT id FROM user_invites WHERE email = ? AND used_at IS NULL AND expires_at > CURRENT_TIMESTAMP',
      [email.toLowerCase()]
    );
    if (existingInvite) {
      return res.status(409).json({ error: 'There is already a pending invite for this email' });
    }

    const token = uuidv4();
    const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(); // 7 days

    await db.run(
      'INSERT INTO user_invites (token, email, created_by, expires_at) VALUES (?, ?, ?, ?)',
      [token, email.toLowerCase(), req.user.id, expiresAt]
    );

    // Send invite email if SMTP is configured
    const baseUrl = getBaseUrl(req);
    const inviteUrl = `${baseUrl}/register?invite=${token}`;

    // Load configured app name (admin setting → env → default)
    const appName = await getAppName();

    if (isMailerConfigured()) {
      try {
        await sendInviteEmail(email.toLowerCase(), inviteUrl, appName, 'en');
      } catch (mailErr) {
        log.admin.warn(`Invite email failed (non-fatal): ${mailErr.message}`);
      }
    }

    res.status(201).json({ invite: { token, email: email.toLowerCase(), expiresAt, inviteUrl } });
  } catch (err) {
    log.admin.error(`Create invite error: ${err.message}`);
    res.status(500).json({ error: 'Invite could not be created' });
  }
});

// GET /api/admin/invites - List all invites
router.get('/invites', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const db = getDb();
    const invites = await db.all(`
      SELECT ui.id, ui.token, ui.email, ui.expires_at, ui.created_at, ui.used_at,
             creator.name as created_by_name,
             used_user.name as used_by_name
      FROM user_invites ui
      LEFT JOIN users creator ON creator.id = ui.created_by
      LEFT JOIN users used_user ON used_user.id = ui.used_by
      ORDER BY ui.created_at DESC
    `);
    res.json({ invites });
  } catch (err) {
    log.admin.error(`List invites error: ${err.message}`);
    res.status(500).json({ error: 'Invites could not be loaded' });
  }
});

// DELETE /api/admin/invites/:id - Delete an invite
router.delete('/invites/:id', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const db = getDb();
    const invite = await db.get('SELECT id FROM user_invites WHERE id = ?', [req.params.id]);
    if (!invite) {
      return res.status(404).json({ error: 'Invite not found' });
    }
    await db.run('DELETE FROM user_invites WHERE id = ?', [req.params.id]);
    res.json({ message: 'Invite deleted' });
  } catch (err) {
    log.admin.error(`Delete invite error: ${err.message}`);
    res.status(500).json({ error: 'Invite could not be deleted' });
  }
});

// ── OAuth / SSO Configuration (admin only) ──────────────────────────────────

// GET /api/admin/oauth - Get current OAuth configuration
router.get('/oauth', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const config = await getOAuthConfig();
    if (!config) {
      return res.json({ configured: false, config: null });
    }
    // Never expose the decrypted client secret to the frontend
    res.json({
      configured: true,
      config: {
        issuer: config.issuer,
        clientId: config.clientId,
        hasClientSecret: !!config.clientSecret,
        displayName: config.displayName || 'SSO',
        autoRegister: config.autoRegister ?? true,
      },
    });
  } catch (err) {
    log.admin.error(`Get OAuth config error: ${err.message}`);
    res.status(500).json({ error: 'Could not load OAuth configuration' });
  }
});

// PUT /api/admin/oauth - Save OAuth configuration
router.put('/oauth', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { issuer, clientId, clientSecret, displayName, autoRegister } = req.body;

    if (!issuer || !clientId) {
      return res.status(400).json({ error: 'Issuer URL and Client ID are required' });
    }

    // Validate issuer URL
    try {
      const parsed = new URL(issuer);
      if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
        return res.status(400).json({ error: 'Issuer URL must use https:// (or http:// for development)' });
      }
    } catch {
      return res.status(400).json({ error: 'Invalid Issuer URL' });
    }

    // Validate display name length
    if (displayName && displayName.length > 50) {
      return res.status(400).json({ error: 'Display name must not exceed 50 characters' });
    }

    // Check if the existing config has a secret and none is being sent (keep old one)
    let finalSecret = clientSecret;
    if (!clientSecret) {
      const existing = await getOAuthConfig();
      if (existing?.clientSecret) {
        finalSecret = existing.clientSecret;
      }
    }

    // Attempt OIDC discovery to validate the issuer endpoint
    try {
      await discoverOIDC(issuer);
    } catch (discErr) {
      return res.status(400).json({
        error: `Could not discover OIDC configuration at ${issuer}: ${discErr.message}`,
      });
    }

    await saveOAuthConfig({
      issuer,
      clientId,
      clientSecret: finalSecret || '',
      displayName: displayName || 'SSO',
      autoRegister: autoRegister !== false,
    });

    log.admin.info(`OAuth configuration saved by admin (issuer: ${issuer})`);
    res.json({ message: 'OAuth configuration saved' });
  } catch (err) {
    log.admin.error(`Save OAuth config error: ${err.message}`);
    res.status(500).json({ error: 'Could not save OAuth configuration' });
  }
});

// DELETE /api/admin/oauth - Remove OAuth configuration
router.delete('/oauth', authenticateToken, requireAdmin, async (req, res) => {
  try {
    await deleteOAuthConfig();
    log.admin.info('OAuth configuration removed by admin');
    res.json({ message: 'OAuth configuration removed' });
  } catch (err) {
    log.admin.error(`Delete OAuth config error: ${err.message}`);
    res.status(500).json({ error: 'Could not remove OAuth configuration' });
  }
});

// ── Room Management (admin only) ────────────────────────────────────────────

// GET /api/admin/rooms - List all rooms with owner info
router.get('/rooms', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const db = getDb();
    const rooms = await db.all(`
      SELECT r.id, r.uid, r.name, r.user_id, r.max_participants, r.access_code,
             r.mute_on_join, r.record_meeting, r.guest_access, r.presentation_file,
             r.created_at, r.updated_at,
             COALESCE(NULLIF(u.display_name,''), u.name) as owner_name,
             u.email as owner_email,
             (SELECT COUNT(*) FROM room_shares rs WHERE rs.room_id = r.id) as share_count
      FROM rooms r
      JOIN users u ON r.user_id = u.id
      ORDER BY r.created_at DESC
    `);
    res.json({ rooms });
  } catch (err) {
    log.admin.error(`List rooms error: ${err.message}`);
    res.status(500).json({ error: 'Rooms could not be loaded' });
  }
});

export default router;
