const express = require('express');
const fs      = require('fs');
const router  = express.Router();
const { query, queryOne, queryResult, exec } = require('../models/db');
const { authMiddleware, adminMiddleware } = require('../middleware/auth');

function deleteImageFile(imageUrl) {
  if (!imageUrl) return;
  try { const fp = '/app' + imageUrl; if (fs.existsSync(fp)) fs.unlinkSync(fp); }
  catch (e) { console.warn('[Groups] Could not delete image:', e.message); }
}

// Schema-aware room name helper
const R = (schema, type, id) => `${schema}:${type}:${id}`;

// Compute and store composite_members for a non-managed private group.
// Captures up to 4 current members (excluding deleted users), ordered by name.
async function computeAndStoreComposite(schema, groupId) {
  const members = await query(schema,
    `SELECT u.id, u.name, u.avatar FROM group_members gm
     JOIN users u ON gm.user_id = u.id
     WHERE gm.group_id = $1 AND u.name != 'Deleted User'
     ORDER BY u.name LIMIT 4`,
    [groupId]
  );
  await exec(schema, 'UPDATE groups SET composite_members=$1 WHERE id=$2',
    [JSON.stringify(members), groupId]
  );
}

module.exports = (io) => {

async function emitGroupNew(schema, io, groupId) {
  const group = await queryOne(schema, 'SELECT * FROM groups WHERE id=$1', [groupId]);
  if (!group) return;
  if (group.type === 'public') {
    io.to(R(schema, 'schema', 'all')).emit('group:new', { group });
  } else {
    const members = await query(schema, 'SELECT user_id FROM group_members WHERE group_id=$1', [groupId]);
    for (const m of members) io.to(R(schema, 'user', m.user_id)).emit('group:new', { group });
  }
}

async function emitGroupUpdated(schema, io, groupId) {
  const group = await queryOne(schema, 'SELECT * FROM groups WHERE id=$1', [groupId]);
  if (!group) return;
  let uids;
  if (group.type === 'public') {
    uids = await query(schema, "SELECT id AS user_id FROM users WHERE status='active'");
  } else {
    uids = await query(schema, 'SELECT user_id FROM group_members WHERE group_id=$1', [groupId]);
  }
  for (const m of uids) io.to(R(schema, 'user', m.user_id)).emit('group:updated', { group });
}

// GET all groups for current user
router.get('/', authMiddleware, async (req, res) => {
  try {
    const userId = req.user.id;
    const publicGroups = await query(req.schema, `
      SELECT g.*,
        (SELECT COUNT(*) FROM messages m WHERE m.group_id=g.id AND m.is_deleted=FALSE) AS message_count,
        (SELECT m.content  FROM messages m WHERE m.group_id=g.id AND m.is_deleted=FALSE ORDER BY m.created_at DESC LIMIT 1) AS last_message,
        (SELECT m.created_at FROM messages m WHERE m.group_id=g.id AND m.is_deleted=FALSE ORDER BY m.created_at DESC LIMIT 1) AS last_message_at,
        (SELECT m.user_id  FROM messages m WHERE m.group_id=g.id AND m.is_deleted=FALSE ORDER BY m.created_at DESC LIMIT 1) AS last_message_user_id
      FROM groups g WHERE g.type='public' ORDER BY g.is_default DESC, g.name ASC
    `);

    const privateGroupsRaw = await query(req.schema, `
      SELECT g.*, u.name AS owner_name,
        (SELECT COUNT(*) FROM messages m WHERE m.group_id=g.id AND m.is_deleted=FALSE) AS message_count,
        (SELECT m.content  FROM messages m WHERE m.group_id=g.id AND m.is_deleted=FALSE ORDER BY m.created_at DESC LIMIT 1) AS last_message,
        (SELECT m.created_at FROM messages m WHERE m.group_id=g.id AND m.is_deleted=FALSE ORDER BY m.created_at DESC LIMIT 1) AS last_message_at,
        (SELECT m.user_id  FROM messages m WHERE m.group_id=g.id AND m.is_deleted=FALSE ORDER BY m.created_at DESC LIMIT 1) AS last_message_user_id,
        (SELECT json_agg(t) FROM (
          SELECT u2.id, u2.name, u2.avatar
          FROM group_members gm2
          JOIN users u2 ON gm2.user_id = u2.id
          WHERE gm2.group_id = g.id AND u2.name != 'Deleted User'
          ORDER BY u2.name LIMIT 4
        ) t) AS member_previews
      FROM groups g JOIN group_members gm ON g.id=gm.group_id AND gm.user_id=$1
      LEFT JOIN users u ON g.owner_id=u.id WHERE g.type='private'
      ORDER BY last_message_at DESC NULLS LAST
    `, [userId]);

    const privateGroups = await Promise.all(privateGroupsRaw.map(async g => {
      if (g.is_direct) {
        if (!g.direct_peer1_id || !g.direct_peer2_id) {
          const peers = await query(req.schema, 'SELECT user_id FROM group_members WHERE group_id=$1 LIMIT 2', [g.id]);
          if (peers.length === 2) {
            await exec(req.schema, 'UPDATE groups SET direct_peer1_id=$1, direct_peer2_id=$2 WHERE id=$3', [peers[0].user_id, peers[1].user_id, g.id]);
            g.direct_peer1_id = peers[0].user_id; g.direct_peer2_id = peers[1].user_id;
          }
        }
        const otherUserId = g.direct_peer1_id === userId ? g.direct_peer2_id : g.direct_peer1_id;
        if (otherUserId) {
          const other = await queryOne(req.schema, 'SELECT display_name, name, avatar FROM users WHERE id=$1', [otherUserId]);
          if (other) {
            g.peer_id = otherUserId; g.peer_real_name = other.name;
            g.peer_display_name = other.display_name || null; g.peer_avatar = other.avatar || null;
            g.name = other.display_name || other.name;
          }
        }
      }
      const custom = await queryOne(req.schema, 'SELECT name FROM user_group_names WHERE user_id=$1 AND group_id=$2', [userId, g.id]);
      if (custom) { g.owner_name_original = g.name; g.name = custom.name; }
      return g;
    }));

    res.json({ publicGroups, privateGroups });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// POST create group
router.post('/', authMiddleware, async (req, res) => {
  const { name, type, memberIds, isReadonly, isDirect } = req.body;
  try {
    if (type === 'public' && req.user.role !== 'admin')
      return res.status(403).json({ error: 'Only admins can create public groups' });

    // Direct message
    if (isDirect && memberIds?.length === 1) {
      const otherUserId = memberIds[0], userId = req.user.id;

      // U2U restriction check — admins always exempt
      if (req.user.role !== 'admin') {
        // Get all user groups the initiating user belongs to
        const initiatorGroups = await query(req.schema,
          'SELECT user_group_id FROM user_group_members WHERE user_id = $1', [userId]
        );
        const initiatorGroupIds = initiatorGroups.map(r => r.user_group_id);

        // Get all user groups the target user belongs to
        const targetGroups = await query(req.schema,
          'SELECT user_group_id FROM user_group_members WHERE user_id = $1', [otherUserId]
        );
        const targetGroupIds = targetGroups.map(r => r.user_group_id);

        // Least-restrictive-wins: the initiator needs at least ONE group
        // that has no restriction against ALL of the target's groups.
        // If initiatorGroups is empty, no restrictions apply (user not in any managed group).
        if (initiatorGroupIds.length > 0 && targetGroupIds.length > 0) {
          // For each initiator group, check if it is restricted from ANY of the target groups
          let canDm = false;
          for (const igId of initiatorGroupIds) {
            const restrictions = await query(req.schema,
              'SELECT blocked_group_id FROM user_group_dm_restrictions WHERE restricting_group_id = $1',
              [igId]
            );
            const blockedIds = new Set(restrictions.map(r => r.blocked_group_id));
            // This initiator group is unrestricted if none of the target's groups are blocked
            const isRestricted = targetGroupIds.some(tgId => blockedIds.has(tgId));
            if (!isRestricted) { canDm = true; break; }
          }
          if (!canDm) {
            return res.status(403).json({
              error: 'Direct messages with this user are not permitted.',
              code: 'DM_RESTRICTED'
            });
          }
        }
      }

      const existing = await queryOne(req.schema, `
        SELECT g.id FROM groups g
        JOIN group_members gm1 ON gm1.group_id=g.id AND gm1.user_id=$1
        JOIN group_members gm2 ON gm2.group_id=g.id AND gm2.user_id=$2
        WHERE g.is_direct=TRUE LIMIT 1
      `, [userId, otherUserId]);
      if (existing) {
        await exec(req.schema, "UPDATE groups SET is_readonly=FALSE, owner_id=NULL, updated_at=NOW() WHERE id=$1", [existing.id]);
        await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [existing.id, userId]);
        return res.json({ group: await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [existing.id]) });
      }
      const otherUser = await queryOne(req.schema, 'SELECT name, display_name FROM users WHERE id=$1', [otherUserId]);
      const dmName = (otherUser?.display_name || otherUser?.name) + ' ↔ ' + (req.user.display_name || req.user.name);
      const r = await queryResult(req.schema,
        "INSERT INTO groups (name,type,owner_id,is_readonly,is_direct,direct_peer1_id,direct_peer2_id) VALUES ($1,'private',NULL,FALSE,TRUE,$2,$3) RETURNING id",
        [dmName, userId, otherUserId]
      );
      const groupId = r.rows[0].id;
      await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [groupId, userId]);
      await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [groupId, otherUserId]);
      await emitGroupNew(req.schema, io, groupId);
      return res.json({ group: await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [groupId]) });
    }

    // Check for duplicate private group
    if ((type === 'private' || !type) && !isDirect && memberIds?.length > 0) {
      const allMemberIds = [...new Set([req.user.id, ...memberIds])].sort((a,b) => a-b);
      const candidates = await query(req.schema,
        'SELECT g.id FROM groups g JOIN group_members gm ON gm.group_id=g.id AND gm.user_id=$1 WHERE g.type=\'private\' AND g.is_direct=FALSE',
        [req.user.id]
      );
      for (const c of candidates) {
        const members = (await query(req.schema, 'SELECT user_id FROM group_members WHERE group_id=$1 ORDER BY user_id', [c.id])).map(r => r.user_id);
        if (members.length === allMemberIds.length && members.every((id,i) => id === allMemberIds[i]))
          return res.json({ group: await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [c.id]), duplicate: true });
      }
    }

    const r = await queryResult(req.schema,
      'INSERT INTO groups (name,type,owner_id,is_readonly,is_direct) VALUES ($1,$2,$3,$4,FALSE) RETURNING id',
      [name, type||'private', req.user.id, !!isReadonly]
    );
    const groupId = r.rows[0].id;
    if (type === 'public') {
      const allUsers = await query(req.schema, "SELECT id FROM users WHERE status='active'");
      for (const u of allUsers) await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [groupId, u.id]);
    } else {
      await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [groupId, req.user.id]);
      if (memberIds?.length > 0) {
        const defaultAdmin = await queryOne(req.schema, 'SELECT id FROM users WHERE is_default_admin=TRUE');
        for (const uid of memberIds) {
          if (defaultAdmin && uid === defaultAdmin.id) continue;
          await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [groupId, uid]);
        }
      }
      // Generate composite avatar for non-managed private groups with 3+ members
      const totalCount = await queryOne(req.schema, 'SELECT COUNT(*) AS cnt FROM group_members WHERE group_id=$1', [groupId]);
      if (parseInt(totalCount.cnt) >= 3) {
        await computeAndStoreComposite(req.schema, groupId);
      }
    }
    await emitGroupNew(req.schema, io, groupId);
    res.json({ group: await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [groupId]) });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// PATCH rename
router.patch('/:id/rename', authMiddleware, async (req, res) => {
  const { name } = req.body;
  try {
    const group = await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [req.params.id]);
    if (!group) return res.status(404).json({ error: 'Group not found' });
    if (group.is_default) return res.status(403).json({ error: 'Cannot rename default group' });
    if (group.is_direct)  return res.status(403).json({ error: 'Cannot rename a direct message' });
    if (group.type === 'public' && req.user.role !== 'admin') return res.status(403).json({ error: 'Only admins can rename public groups' });
    if (group.type === 'private' && group.owner_id !== req.user.id && req.user.role !== 'admin') return res.status(403).json({ error: 'Only owner can rename' });
    await exec(req.schema, 'UPDATE groups SET name=$1, updated_at=NOW() WHERE id=$2', [name, group.id]);
    await emitGroupUpdated(req.schema, io, group.id);
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// GET members
router.get('/:id/members', authMiddleware, async (req, res) => {
  try {
    const members = await query(req.schema,
      'SELECT u.id,u.name,u.display_name,u.avatar,u.role,u.status FROM group_members gm JOIN users u ON gm.user_id=u.id WHERE gm.group_id=$1 ORDER BY u.name ASC',
      [req.params.id]
    );
    res.json({ members });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// POST add member
router.post('/:id/members', authMiddleware, async (req, res) => {
  const { userId } = req.body;
  try {
    const group = await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [req.params.id]);
    if (!group) return res.status(404).json({ error: 'Group not found' });
    if (group.type !== 'private') return res.status(400).json({ error: 'Cannot manually add members to public groups' });
    if (group.is_direct) return res.status(400).json({ error: 'Cannot add members to a direct message' });
    if (group.owner_id !== req.user.id && req.user.role !== 'admin') return res.status(403).json({ error: 'Only owner can add members' });
    const targetUser = await queryOne(req.schema, 'SELECT is_default_admin FROM users WHERE id=$1', [userId]);
    if (targetUser?.is_default_admin) return res.status(400).json({ error: 'Default admin cannot be added to private groups' });
    // Capture pre-add count to decide if composite should regenerate
    const preAddCount = await queryOne(req.schema, 'SELECT COUNT(*) AS cnt FROM group_members WHERE group_id=$1', [group.id]);
    await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [group.id, userId]);
    const addedUser = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [userId]);
    const addedName = addedUser?.display_name || addedUser?.name || 'Unknown';
    const mr = await queryResult(req.schema,
      "INSERT INTO messages (group_id,user_id,content,type) VALUES ($1,$2,$3,'system') RETURNING id",
      [group.id, userId, `${addedName} has joined the conversation.`]
    );
    const sysMsg = await queryOne(req.schema,
      'SELECT m.*,u.name AS user_name,u.display_name AS user_display_name,u.avatar AS user_avatar,u.role AS user_role,u.status AS user_status,u.hide_admin_tag AS user_hide_admin_tag,u.about_me AS user_about_me FROM messages m JOIN users u ON m.user_id=u.id WHERE m.id=$1',
      [mr.rows[0].id]
    );
    sysMsg.reactions = [];
    io.to(R(req.schema,'group',group.id)).emit('message:new', sysMsg);
    // For non-managed private groups, always notify existing members of the updated group,
    // and regenerate composite when pre-add count was ≤3 and new total reaches ≥3.
    if (!group.is_managed && !group.is_direct) {
      const preCount = parseInt(preAddCount.cnt);
      if (preCount <= 3) {
        const newTotal = preCount + 1;
        if (newTotal >= 3) {
          await computeAndStoreComposite(req.schema, group.id);
        }
      }
      await emitGroupUpdated(req.schema, io, group.id);
    }
    io.in(R(req.schema,'user',userId)).socketsJoin(R(req.schema,'group',group.id));
    io.to(R(req.schema,'user',userId)).emit('group:new', { group });
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// DELETE remove member
router.delete('/:id/members/:userId', authMiddleware, async (req, res) => {
  try {
    const group = await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [req.params.id]);
    if (!group) return res.status(404).json({ error: 'Group not found' });
    if (group.type !== 'private') return res.status(400).json({ error: 'Cannot remove members from public groups' });
    if (group.owner_id !== req.user.id && req.user.role !== 'admin') return res.status(403).json({ error: 'Only owner or admin can remove members' });
    const targetId = parseInt(req.params.userId);
    // Admins can remove the owner only if the owner is a deleted user (orphan cleanup)
    const targetUser = await queryOne(req.schema, 'SELECT status FROM users WHERE id=$1', [targetId]);
    const isDeletedOrphan = targetUser?.status === 'deleted';
    if (targetId === group.owner_id && !isDeletedOrphan && req.user.role !== 'admin') {
      return res.status(400).json({ error: 'Cannot remove the group owner' });
    }
    if (targetId === group.owner_id && !isDeletedOrphan) {
      return res.status(400).json({ error: 'Cannot remove the group owner' });
    }
    const removedUser = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [targetId]);
    const removedName = removedUser?.display_name || removedUser?.name || 'Unknown';
    await exec(req.schema, 'DELETE FROM group_members WHERE group_id=$1 AND user_id=$2', [group.id, targetId]);
    const mr = await queryResult(req.schema,
      "INSERT INTO messages (group_id,user_id,content,type) VALUES ($1,$2,$3,'system') RETURNING id",
      [group.id, targetId, `${removedName} has been removed from the conversation.`]
    );
    const sysMsg = await queryOne(req.schema,
      'SELECT m.*,u.name AS user_name,u.display_name AS user_display_name,u.avatar AS user_avatar,u.role AS user_role,u.status AS user_status,u.hide_admin_tag AS user_hide_admin_tag,u.about_me AS user_about_me FROM messages m JOIN users u ON m.user_id=u.id WHERE m.id=$1',
      [mr.rows[0].id]
    );
    sysMsg.reactions = [];
    io.to(R(req.schema,'group',group.id)).emit('message:new', sysMsg);
    io.in(R(req.schema,'user',targetId)).socketsLeave(R(req.schema,'group',group.id));
    io.to(R(req.schema,'user',targetId)).emit('group:deleted', { groupId: group.id });
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// DELETE leave
router.delete('/:id/leave', authMiddleware, async (req, res) => {
  try {
    const group = await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [req.params.id]);
    if (!group) return res.status(404).json({ error: 'Group not found' });
    if (group.type === 'public') return res.status(400).json({ error: 'Cannot leave public groups' });
    if (group.is_managed && req.user.role !== 'admin') return res.status(403).json({ error: 'This group is managed by an administrator.' });
    const userId = req.user.id;
    const leaverName = req.user.display_name || req.user.name;
    await exec(req.schema, 'DELETE FROM group_members WHERE group_id=$1 AND user_id=$2', [group.id, userId]);
    const mr = await queryResult(req.schema,
      "INSERT INTO messages (group_id,user_id,content,type) VALUES ($1,$2,$3,'system') RETURNING id",
      [group.id, userId, `${leaverName} has left the conversation.`]
    );
    const sysMsg = await queryOne(req.schema,
      'SELECT m.*,u.name AS user_name,u.display_name AS user_display_name,u.avatar AS user_avatar,u.role AS user_role,u.status AS user_status,u.hide_admin_tag AS user_hide_admin_tag,u.about_me AS user_about_me FROM messages m JOIN users u ON m.user_id=u.id WHERE m.id=$1',
      [mr.rows[0].id]
    );
    sysMsg.reactions = [];
    io.to(R(req.schema,'group',group.id)).emit('message:new', sysMsg);
    io.in(R(req.schema,'user',userId)).socketsLeave(R(req.schema,'group',group.id));
    io.to(R(req.schema,'user',userId)).emit('group:deleted', { groupId: group.id });
    if (group.is_direct) {
      const remaining = await queryOne(req.schema, 'SELECT user_id FROM group_members WHERE group_id=$1 LIMIT 1', [group.id]);
      if (remaining) await exec(req.schema, 'UPDATE groups SET owner_id=$1, updated_at=NOW() WHERE id=$2', [remaining.user_id, group.id]);
    }
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// POST take-ownership
router.post('/:id/take-ownership', authMiddleware, adminMiddleware, async (req, res) => {
  try {
    const group = await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [req.params.id]);
    if (group?.is_managed) return res.status(403).json({ error: 'Managed groups are administered via the Group Manager.' });
    await exec(req.schema, 'UPDATE groups SET owner_id=$1, updated_at=NOW() WHERE id=$2', [req.user.id, req.params.id]);
    await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [req.params.id, req.user.id]);
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// DELETE group
router.delete('/:id', authMiddleware, async (req, res) => {
  try {
    const group = await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [req.params.id]);
    if (!group) return res.status(404).json({ error: 'Group not found' });
    if (group.is_default) return res.status(403).json({ error: 'Cannot delete default group' });
    if (group.type === 'public' && req.user.role !== 'admin') return res.status(403).json({ error: 'Only admins can delete public groups' });
    if (group.type === 'private' && group.owner_id !== req.user.id && req.user.role !== 'admin') return res.status(403).json({ error: 'Only owner or admin can delete' });
    const members = (await query(req.schema, 'SELECT user_id FROM group_members WHERE group_id=$1', [group.id])).map(m => m.user_id);
    if (group.type === 'public') {
      const all = await query(req.schema, "SELECT id FROM users WHERE status='active'");
      for (const u of all) if (!members.includes(u.id)) members.push(u.id);
    }
    const imageMessages = await query(req.schema, 'SELECT image_url FROM messages WHERE group_id=$1 AND image_url IS NOT NULL', [group.id]);
    await exec(req.schema, 'DELETE FROM groups WHERE id=$1', [group.id]);
    for (const msg of imageMessages) deleteImageFile(msg.image_url);
    for (const uid of members) io.to(R(req.schema,'user',uid)).emit('group:deleted', { groupId: group.id });
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

// PATCH custom-name
router.patch('/:id/custom-name', authMiddleware, async (req, res) => {
  const { name } = req.body;
  const groupId = parseInt(req.params.id), userId = req.user.id;
  try {
    if (!name?.trim()) {
      await exec(req.schema, 'DELETE FROM user_group_names WHERE user_id=$1 AND group_id=$2', [userId, groupId]);
      return res.json({ success: true, name: null });
    }
    await exec(req.schema,
      'INSERT INTO user_group_names (user_id,group_id,name) VALUES ($1,$2,$3) ON CONFLICT (user_id,group_id) DO UPDATE SET name=EXCLUDED.name',
      [userId, groupId, name.trim()]
    );
    res.json({ success: true, name: name.trim() });
  } catch (e) { res.status(500).json({ error: e.message }); }
});

return router;
};
