const express = require('express');
const bcrypt  = require('bcryptjs');
const multer  = require('multer');
const path    = require('path');
const router  = express.Router();
const { query, queryOne, queryResult, exec, addUserToPublicGroups, getOrCreateSupportGroup } = require('../models/db');
const { authMiddleware, teamManagerMiddleware } = require('../middleware/auth');

const avatarStorage = multer.diskStorage({
  destination: '/app/uploads/avatars',
  filename: (req, file, cb) => cb(null, `avatar_${req.user.id}_${Date.now()}${path.extname(file.originalname)}`),
});
const uploadAvatar = multer({
  storage: avatarStorage,
  limits: { fileSize: 2 * 1024 * 1024 },
  fileFilter: (req, file, cb) => file.mimetype.startsWith('image/') ? cb(null, true) : cb(new Error('Images only')),
});

async function resolveUniqueName(schema, baseName, excludeId = null) {
  const existing = await query(schema,
    "SELECT name FROM users WHERE status != 'deleted' AND id != $1 AND (name = $2 OR name LIKE $3)",
    [excludeId ?? -1, baseName, `${baseName} (%)`]
  );
  if (existing.length === 0) return baseName;
  let max = 0;
  for (const u of existing) { const m = u.name.match(/\((\d+)\)$/); if (m) max = Math.max(max, parseInt(m[1])); else max = Math.max(max, 0); }
  return `${baseName} (${max + 1})`;
}

function isValidEmail(e) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e); }

// List users
router.get('/', authMiddleware, teamManagerMiddleware, async (req, res) => {
  try {
    const users = await query(req.schema,
      "SELECT id,name,first_name,last_name,phone,is_minor,email,role,status,is_default_admin,must_change_password,avatar,about_me,display_name,allow_dm,created_at,last_online FROM users WHERE status != 'deleted' ORDER BY name ASC"
    );
    res.json({ users });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Search users
// When q is empty (full-list load by GroupManagerPage / NewChatModal) — return ALL active users,
// no LIMIT, so the complete roster is available for member-picker UIs.
// When q is non-empty (typed search / mention autocomplete) — keep LIMIT 10 for performance.
router.get('/search', authMiddleware, async (req, res) => {
  const { q, groupId } = req.query;
  const isTyped = q && q.length > 0;
  try {
    let users;
    if (groupId) {
      const group = await queryOne(req.schema, 'SELECT type, is_direct FROM groups WHERE id = $1', [parseInt(groupId)]);
      if (group && (group.type === 'private' || group.is_direct)) {
        users = await query(req.schema,
          `SELECT u.id,u.name,u.display_name,u.avatar,u.role,u.status,u.hide_admin_tag,u.allow_dm FROM users u JOIN group_members gm ON gm.user_id=u.id AND gm.group_id=$1 WHERE u.status='active' AND u.id!=$2 AND (u.name ILIKE $3 OR u.display_name ILIKE $3) ORDER BY u.name ASC${isTyped ? ' LIMIT 10' : ''}`,
          [parseInt(groupId), req.user.id, `%${q}%`]
        );
      } else {
        users = await query(req.schema,
          `SELECT id,name,display_name,avatar,role,status,hide_admin_tag,allow_dm FROM users WHERE status='active' AND id!=$1 AND (name ILIKE $2 OR display_name ILIKE $2) ORDER BY name ASC${isTyped ? ' LIMIT 10' : ''}`,
          [req.user.id, `%${q}%`]
        );
      }
    } else {
      users = await query(req.schema,
        `SELECT id,name,display_name,avatar,role,status,hide_admin_tag,allow_dm FROM users WHERE status='active' AND (name ILIKE $1 OR display_name ILIKE $1) ORDER BY name ASC${isTyped ? ' LIMIT 10' : ''}`,
        [`%${q}%`]
      );
    }
    res.json({ users });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Check display name
router.get('/check-display-name', authMiddleware, async (req, res) => {
  const { name } = req.query;
  if (!name) return res.json({ taken: false });
  try {
    const conflict = await queryOne(req.schema,
      "SELECT id FROM users WHERE LOWER(display_name)=LOWER($1) AND id!=$2 AND status!='deleted'",
      [name, req.user.id]
    );
    res.json({ taken: !!conflict });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Create user
router.post('/', authMiddleware, teamManagerMiddleware, async (req, res) => {
  const { firstName, lastName, email, password, role, phone, isMinor } = req.body;
  if (!firstName?.trim() || !lastName?.trim() || !email)
    return res.status(400).json({ error: 'First name, last name and email required' });
  if (!isValidEmail(email.trim())) return res.status(400).json({ error: 'Invalid email address' });
  const validRoles = ['member', 'admin', 'manager'];
  const assignedRole = validRoles.includes(role) ? role : 'member';
  const name = `${firstName.trim()} ${lastName.trim()}`;
  try {
    const exists = await queryOne(req.schema, "SELECT id FROM users WHERE LOWER(email) = LOWER($1) AND status != 'deleted'", [email.trim()]);
    if (exists) return res.status(400).json({ error: 'Email already in use' });
    const resolvedName = await resolveUniqueName(req.schema, name);
    const pw   = (password || '').trim() || process.env.USER_PASS || 'user@1234';
    const hash = bcrypt.hashSync(pw, 10);
    const r    = await queryResult(req.schema,
      "INSERT INTO users (name,first_name,last_name,email,password,role,phone,is_minor,status,must_change_password) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'active',TRUE) RETURNING id",
      [resolvedName, firstName.trim(), lastName.trim(), email.trim().toLowerCase(), hash, assignedRole, phone?.trim() || null, !!isMinor]
    );
    const userId = r.rows[0].id;
    await addUserToPublicGroups(req.schema, userId);
    if (assignedRole === 'admin') {
      const sgId = await getOrCreateSupportGroup(req.schema);
      if (sgId) await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [sgId, userId]);
    }
    const user = await queryOne(req.schema, 'SELECT id,name,first_name,last_name,phone,is_minor,email,role,status,must_change_password,created_at FROM users WHERE id=$1', [userId]);
    res.json({ user });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Update user (general — name components, phone, is_minor, role, optional password reset)
router.patch('/:id', authMiddleware, teamManagerMiddleware, async (req, res) => {
  const id = parseInt(req.params.id);
  if (isNaN(id)) return res.status(400).json({ error: 'Invalid user ID' });
  const { firstName, lastName, phone, isMinor, role, password } = req.body;
  if (!firstName?.trim() || !lastName?.trim())
    return res.status(400).json({ error: 'First and last name required' });
  const validRoles = ['member', 'admin', 'manager'];
  if (!validRoles.includes(role)) return res.status(400).json({ error: 'Invalid role' });
  try {
    const target = await queryOne(req.schema, 'SELECT * FROM users WHERE id=$1', [id]);
    if (!target) return res.status(404).json({ error: 'User not found' });
    if (target.is_default_admin && role !== 'admin')
      return res.status(403).json({ error: 'Cannot change default admin role' });
    const name = `${firstName.trim()} ${lastName.trim()}`;
    const resolvedName = await resolveUniqueName(req.schema, name, id);
    await exec(req.schema,
      'UPDATE users SET name=$1,first_name=$2,last_name=$3,phone=$4,is_minor=$5,role=$6,updated_at=NOW() WHERE id=$7',
      [resolvedName, firstName.trim(), lastName.trim(), phone?.trim() || null, !!isMinor, role, id]
    );
    if (password && password.length >= 6) {
      const hash = bcrypt.hashSync(password, 10);
      await exec(req.schema, 'UPDATE users SET password=$1,must_change_password=TRUE,updated_at=NOW() WHERE id=$2', [hash, id]);
    }
    if (role === 'admin' && target.role !== 'admin') {
      const sgId = await getOrCreateSupportGroup(req.schema);
      if (sgId) await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [sgId, id]);
    }
    const user = await queryOne(req.schema,
      'SELECT id,name,first_name,last_name,phone,is_minor,email,role,status,must_change_password,last_online,created_at FROM users WHERE id=$1',
      [id]
    );
    res.json({ user });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Bulk create
router.post('/bulk', authMiddleware, teamManagerMiddleware, async (req, res) => {
  const { users } = req.body;
  const results = { created: [], skipped: [] };
  const seenEmails = new Set();
  const defaultPw = process.env.USER_PASS || 'user@1234';
  const validRoles = ['member', 'manager', 'admin'];
  try {
    for (const u of users) {
      const email     = (u.email     || '').trim().toLowerCase();
      const firstName = (u.firstName || '').trim();
      const lastName  = (u.lastName  || '').trim();
      // Support legacy name field too
      const name = (firstName && lastName) ? `${firstName} ${lastName}` : (u.name || '').trim();
      if (!email)              { results.skipped.push({ email: '(blank)', reason: 'Email required' }); continue; }
      if (!isValidEmail(email)){ results.skipped.push({ email, reason: 'Invalid email address' }); continue; }
      if (!name)               { results.skipped.push({ email, reason: 'First and last name required' }); continue; }
      if (seenEmails.has(email)){ results.skipped.push({ email, reason: 'Duplicate email in CSV' }); continue; }
      seenEmails.add(email);
      const exists = await queryOne(req.schema, "SELECT id FROM users WHERE email=$1 AND status != 'deleted'", [email]);
      if (exists) { results.skipped.push({ email, reason: 'Email already exists' }); continue; }
      try {
        const resolvedName = await resolveUniqueName(req.schema, name);
        const pw       = (u.password || '').trim() || defaultPw;
        const hash     = bcrypt.hashSync(pw, 10);
        const newRole  = validRoles.includes(u.role) ? u.role : 'member';
        const fn       = firstName || name.split(' ')[0] || '';
        const ln       = lastName  || name.split(' ').slice(1).join(' ') || '';
        const r = await queryResult(req.schema,
          "INSERT INTO users (name,first_name,last_name,email,password,role,status,must_change_password) VALUES ($1,$2,$3,$4,$5,$6,'active',TRUE) RETURNING id",
          [resolvedName, fn, ln, email, hash, newRole]
        );
        const userId = r.rows[0].id;
        await addUserToPublicGroups(req.schema, userId);
        if (newRole === 'admin') {
          const sgId = await getOrCreateSupportGroup(req.schema);
          if (sgId) await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [sgId, userId]);
        }
        // Add to user group if specified (silent — user was just created, no socket needed)
        if (u.userGroupId) {
          const ug = await queryOne(req.schema, 'SELECT * FROM user_groups WHERE id=$1', [u.userGroupId]);
          if (ug) {
            await exec(req.schema, 'INSERT INTO user_group_members (user_group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [ug.id, userId]);
            if (ug.dm_group_id) {
              await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [ug.dm_group_id, userId]);
            }
          }
        }
        results.created.push(email);
      } catch (e) { results.skipped.push({ email, reason: e.message }); }
    }
    res.json(results);
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Patch name
router.patch('/:id/name', authMiddleware, teamManagerMiddleware, async (req, res) => {
  const { name } = req.body;
  if (!name?.trim()) return res.status(400).json({ error: 'Name required' });
  try {
    const target = await queryOne(req.schema, 'SELECT * FROM users WHERE id=$1', [req.params.id]);
    if (!target) return res.status(404).json({ error: 'User not found' });
    const resolvedName = await resolveUniqueName(req.schema, name.trim(), req.params.id);
    await exec(req.schema, 'UPDATE users SET name=$1, updated_at=NOW() WHERE id=$2', [resolvedName, target.id]);
    res.json({ success: true, name: resolvedName });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Patch role
router.patch('/:id/role', authMiddleware, teamManagerMiddleware, async (req, res) => {
  const { role } = req.body;
  if (!['member','admin','manager'].includes(role)) return res.status(400).json({ error: 'Invalid role' });
  try {
    const target = await queryOne(req.schema, 'SELECT * FROM users WHERE id=$1', [req.params.id]);
    if (!target) return res.status(404).json({ error: 'User not found' });
    if (target.is_default_admin) return res.status(403).json({ error: 'Cannot modify default admin role' });
    await exec(req.schema, 'UPDATE users SET role=$1, updated_at=NOW() WHERE id=$2', [role, target.id]);
    if (role === 'admin') {
      const sgId = await getOrCreateSupportGroup(req.schema);
      if (sgId) await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [sgId, target.id]);
    }
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Reset password
router.patch('/:id/reset-password', authMiddleware, teamManagerMiddleware, async (req, res) => {
  const { password } = req.body;
  if (!password || password.length < 6) return res.status(400).json({ error: 'Password too short' });
  try {
    const hash = bcrypt.hashSync(password, 10);
    await exec(req.schema, 'UPDATE users SET password=$1, must_change_password=TRUE, updated_at=NOW() WHERE id=$2', [hash, req.params.id]);
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Suspend / activate / delete
router.patch('/:id/suspend',  authMiddleware, teamManagerMiddleware, async (req, res) => {
  try {
    const t = await queryOne(req.schema, 'SELECT * FROM users WHERE id=$1', [req.params.id]);
    if (!t) return res.status(404).json({ error: 'User not found' });
    if (t.is_default_admin) return res.status(403).json({ error: 'Cannot suspend default admin' });
    await exec(req.schema, "UPDATE users SET status='suspended', updated_at=NOW() WHERE id=$1", [t.id]);
    // Clear active sessions so suspended user is immediately kicked
    await exec(req.schema, 'DELETE FROM active_sessions WHERE user_id=$1', [t.id]);
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});
router.patch('/:id/activate', authMiddleware, teamManagerMiddleware, async (req, res) => {
  try {
    await exec(req.schema, "UPDATE users SET status='active', updated_at=NOW() WHERE id=$1", [req.params.id]);
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});
router.delete('/:id',         authMiddleware, teamManagerMiddleware, async (req, res) => {
  try {
    const t = await queryOne(req.schema, 'SELECT * FROM users WHERE id=$1', [req.params.id]);
    if (!t) return res.status(404).json({ error: 'User not found' });
    if (t.is_default_admin) return res.status(403).json({ error: 'Cannot delete default admin' });

    // ── 1. Anonymise the user record ─────────────────────────────────────────
    // Scrub the email immediately so the address is free for re-use.
    // Replace name/display_name/avatar/about_me so no PII is retained.
    await exec(req.schema, `
      UPDATE users SET
        status        = 'deleted',
        email         = $1,
        name          = 'Deleted User',
        first_name    = NULL,
        last_name     = NULL,
        phone         = NULL,
        is_minor      = FALSE,
        display_name  = NULL,
        avatar        = NULL,
        about_me      = NULL,
        password      = '',
        updated_at    = NOW()
      WHERE id = $2
    `, [`deleted_${t.id}@deleted`, t.id]);

    // ── 2. Anonymise their messages ───────────────────────────────────────────
    // Mark all their messages as deleted so they render as "This message was
    // deleted" in conversation history — no content holes for other members.
    await exec(req.schema,
      'UPDATE messages SET is_deleted=TRUE, content=NULL, image_url=NULL WHERE user_id=$1 AND is_deleted=FALSE',
      [t.id]
    );

    // ── 3. Freeze any DMs that only had this user + one other person ──────────
    // The surviving peer still has their DM visible but it becomes read-only
    // (frozen) since the other party is gone. Group chats (3+ people) are
    // left intact — the other members' history and ongoing chat is unaffected.
    await exec(req.schema, `
      UPDATE groups SET is_readonly=TRUE, updated_at=NOW()
      WHERE is_direct=TRUE
        AND (direct_peer1_id=$1 OR direct_peer2_id=$1)
    `, [t.id]);

    // ── 4. Remove memberships ────────────────────────────────────────────────
    await exec(req.schema, 'DELETE FROM group_members    WHERE user_id=$1', [t.id]);
    await exec(req.schema, 'DELETE FROM user_group_members WHERE user_id=$1', [t.id]);

    // ── 5. Purge sessions, push subscriptions, notifications ─────────────────
    await exec(req.schema, 'DELETE FROM active_sessions    WHERE user_id=$1', [t.id]);
    await exec(req.schema, 'DELETE FROM push_subscriptions WHERE user_id=$1', [t.id]);
    await exec(req.schema, 'DELETE FROM notifications      WHERE user_id=$1', [t.id]);
    await exec(req.schema, 'DELETE FROM event_availability WHERE user_id=$1', [t.id]);

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

// Update own profile
router.patch('/me/profile', authMiddleware, async (req, res) => {
  const { displayName, aboutMe, hideAdminTag, allowDm } = req.body;
  try {
    if (displayName) {
      const conflict = await queryOne(req.schema,
        "SELECT id FROM users WHERE LOWER(display_name)=LOWER($1) AND id!=$2 AND status!='deleted'",
        [displayName, req.user.id]
      );
      if (conflict) return res.status(400).json({ error: 'Display name already in use' });
    }
    await exec(req.schema,
      'UPDATE users SET display_name=$1, about_me=$2, hide_admin_tag=$3, allow_dm=$4, updated_at=NOW() WHERE id=$5',
      [displayName || null, aboutMe || null, !!hideAdminTag, allowDm !== false, req.user.id]
    );
    const user = await queryOne(req.schema,
      'SELECT id,name,email,role,status,avatar,about_me,display_name,hide_admin_tag,allow_dm FROM users WHERE id=$1',
      [req.user.id]
    );
    res.json({ user });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// Upload avatar
router.post('/me/avatar', authMiddleware, uploadAvatar.single('avatar'), async (req, res) => {
  if (req.user.is_default_admin) return res.status(403).json({ error: 'Default admin avatar cannot be changed' });
  if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
  try {
    const sharp    = require('sharp');
    const filePath = req.file.path;
    const MAX_DIM  = 256;
    const image    = sharp(filePath);
    const meta     = await image.metadata();
    const needsResize = meta.width > MAX_DIM || meta.height > MAX_DIM;
    if (req.file.size >= 500 * 1024 || needsResize) {
      const outPath = filePath.replace(/\.[^.]+$/, '.webp');
      await sharp(filePath).resize(MAX_DIM,MAX_DIM,{fit:'cover',withoutEnlargement:true}).webp({quality:82}).toFile(outPath);
      const fs = require('fs');
      fs.unlinkSync(filePath);
      const avatarUrl = `/uploads/avatars/${path.basename(outPath)}`;
      await exec(req.schema, 'UPDATE users SET avatar=$1, updated_at=NOW() WHERE id=$2', [avatarUrl, req.user.id]);
      return res.json({ avatarUrl });
    }
    const avatarUrl = `/uploads/avatars/${req.file.filename}`;
    await exec(req.schema, 'UPDATE users SET avatar=$1, updated_at=NOW() WHERE id=$2', [avatarUrl, req.user.id]);
    res.json({ avatarUrl });
  } catch (err) {
    console.error('Avatar error:', err);
    const avatarUrl = `/uploads/avatars/${req.file.filename}`;
    await exec(req.schema, 'UPDATE users SET avatar=$1, updated_at=NOW() WHERE id=$2', [avatarUrl, req.user.id]).catch(()=>{});
    res.json({ avatarUrl });
  }
});

module.exports = router;
