import express from 'express';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { createPermissionHelpers } from './permissions.js';

export function createAuthRoutes({ pool }) {
  const router = express.Router();
  const { requirePermission, getUserPermissions } = createPermissionHelpers({ pool });

  // Local authenticate(): used by user-management endpoints that need req.user populated
  // with id/username/email/is_admin. NOT the per-service authenticate() — that lives in
  // shared/auth/middleware.js and is used by downstream services. Auth-server's surface is
  // small enough that a local copy is fine; the security boundary is the JWT verify step.
  async function authenticate(req, res, next) {
    try {
      const authHeader = req.headers.authorization;
      if (!authHeader || !authHeader.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      const token = authHeader.split(' ')[1];
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      const result = await pool.query(
        'SELECT id, username, email, is_admin, rocket_chat_user_id FROM users WHERE id = $1',
        [decoded.userId]
      );
      if (result.rows.length === 0) {
        return res.status(401).json({ error: 'User not found' });
      }
      req.user = result.rows[0];
      next();
    } catch (error) {
      res.status(401).json({ error: 'Invalid token' });
    }
  }

  router.post('/login', async (req, res) => {
    try {
      const { username, password } = req.body;
      const result = await pool.query(
        'SELECT id, username, password, is_admin, is_active, rocket_chat_user_id FROM users WHERE username = $1',
        [username]
      );
      if (result.rows.length === 0) {
        return res.status(401).json({ error: 'Invalid username or password' });
      }
      const user = result.rows[0];
      if (!user.is_active) {
        return res.status(403).json({ error: 'Account is inactive' });
      }
      const validPassword = await bcrypt.compare(password, user.password);
      if (!validPassword) {
        return res.status(401).json({ error: 'Invalid username or password' });
      }
      await pool.query(
        'UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = $1',
        [user.id]
      );
      const token = jwt.sign(
        { userId: user.id, username: user.username },
        process.env.JWT_SECRET,
        { expiresIn: '8h' }
      );
      const permissions = await getUserPermissions(user.id);
      res.json({
        token,
        user: {
          id: user.id,
          username: user.username,
          is_admin: user.is_admin,
          rocket_chat_user_id: user.rocket_chat_user_id,
          permissions,
        },
      });
    } catch (error) {
      console.error('Login error:', error);
      res.status(500).json({ error: 'Server error' });
    }
  });

  router.get('/me', authenticate, async (req, res) => {
    try {
      const permissions = await getUserPermissions(req.user.id);
      res.json({
        id: req.user.id,
        username: req.user.username,
        email: req.user.email,
        is_admin: req.user.is_admin,
        rocket_chat_user_id: req.user.rocket_chat_user_id,
        permissions,
      });
    } catch (error) {
      console.error('Error getting current user:', error);
      res.status(500).json({ error: 'Server error' });
    }
  });

  router.get('/users', authenticate, requirePermission('view:users'), async (req, res) => {
    try {
      const result = await pool.query(`
        SELECT id, username, email, is_admin, is_active, rocket_chat_user_id, created_at, last_login
        FROM users
        ORDER BY username
      `);
      res.json(result.rows);
    } catch (error) {
      console.error('Error getting users:', error);
      res.status(500).json({ error: 'Server error' });
    }
  });

  router.get('/users/:id', authenticate, requirePermission('view:users'), async (req, res) => {
    try {
      const userId = req.params.id;
      const userResult = await pool.query(`
        SELECT id, username, email, is_admin, is_active, rocket_chat_user_id, created_at, last_login
        FROM users
        WHERE id = $1
      `, [userId]);
      if (userResult.rows.length === 0) {
        return res.status(404).json({ error: 'User not found' });
      }
      const permissionsResult = await pool.query(`
        SELECT p.id, p.name, p.code, p.category, p.description
        FROM permissions p
        JOIN user_permissions up ON p.id = up.permission_id
        WHERE up.user_id = $1
        ORDER BY p.category, p.name
      `, [userId]);
      res.json({
        ...userResult.rows[0],
        permissions: permissionsResult.rows,
      });
    } catch (error) {
      console.error('Error getting user:', error);
      res.status(500).json({ error: 'Server error' });
    }
  });

  router.post('/users', authenticate, requirePermission('create:users'), async (req, res) => {
    const client = await pool.connect();
    try {
      const { username, email, password, is_admin, is_active, rocket_chat_user_id, permissions } = req.body;
      if (!username || !password) {
        return res.status(400).json({ error: 'Username and password are required' });
      }
      const existingUser = await client.query(
        'SELECT id FROM users WHERE username = $1',
        [username]
      );
      if (existingUser.rows.length > 0) {
        return res.status(400).json({ error: 'Username already exists' });
      }
      await client.query('BEGIN');
      const hashedPassword = await bcrypt.hash(password, 10);
      const rcUserId = rocket_chat_user_id ? parseInt(rocket_chat_user_id, 10) : null;
      const userResult = await client.query(`
        INSERT INTO users (username, email, password, is_admin, is_active, rocket_chat_user_id, created_at)
        VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP)
        RETURNING id
      `, [username, email || null, hashedPassword, !!is_admin, is_active !== false, rcUserId]);
      const userId = userResult.rows[0].id;

      if (!is_admin && Array.isArray(permissions) && permissions.length > 0) {
        const permissionIds = normalizePermissionIds(permissions);
        if (permissionIds.length > 0) {
          await client.query(
            `INSERT INTO user_permissions (user_id, permission_id)
             SELECT $1, unnest($2::int[])
             ON CONFLICT DO NOTHING`,
            [userId, permissionIds]
          );
        }
      }

      await client.query('COMMIT');
      res.status(201).json({ id: userId, message: 'User created successfully' });
    } catch (error) {
      await client.query('ROLLBACK');
      console.error('Error creating user:', error);
      res.status(500).json({ error: 'Server error' });
    } finally {
      client.release();
    }
  });

  router.put('/users/:id', authenticate, requirePermission('edit:users'), async (req, res) => {
    const client = await pool.connect();
    try {
      const userId = req.params.id;
      const { username, email, password, is_admin, is_active, rocket_chat_user_id, permissions } = req.body;

      const userExists = await client.query('SELECT id FROM users WHERE id = $1', [userId]);
      if (userExists.rows.length === 0) {
        return res.status(404).json({ error: 'User not found' });
      }

      await client.query('BEGIN');

      const updateFields = [];
      const updateValues = [userId];
      let paramIndex = 2;

      if (username !== undefined) { updateFields.push(`username = $${paramIndex++}`); updateValues.push(username); }
      if (email !== undefined) { updateFields.push(`email = $${paramIndex++}`); updateValues.push(email || null); }
      if (is_admin !== undefined) { updateFields.push(`is_admin = $${paramIndex++}`); updateValues.push(!!is_admin); }
      if (is_active !== undefined) { updateFields.push(`is_active = $${paramIndex++}`); updateValues.push(!!is_active); }
      if (rocket_chat_user_id !== undefined) {
        updateFields.push(`rocket_chat_user_id = $${paramIndex++}`);
        updateValues.push(rocket_chat_user_id ? parseInt(rocket_chat_user_id, 10) : null);
      }
      if (password) {
        const hashedPassword = await bcrypt.hash(password, 10);
        updateFields.push(`password = $${paramIndex++}`);
        updateValues.push(hashedPassword);
      }

      if (updateFields.length > 0) {
        updateFields.push(`updated_at = CURRENT_TIMESTAMP`);
        await client.query(`
          UPDATE users SET ${updateFields.join(', ')} WHERE id = $1
        `, updateValues);
      }

      if (Array.isArray(permissions)) {
        await client.query('DELETE FROM user_permissions WHERE user_id = $1', [userId]);
        const newIsAdmin = is_admin !== undefined
          ? is_admin
          : (await client.query('SELECT is_admin FROM users WHERE id = $1', [userId])).rows[0].is_admin;

        if (!newIsAdmin && permissions.length > 0) {
          const permissionIds = normalizePermissionIds(permissions);
          if (permissionIds.length > 0) {
            await client.query(
              `INSERT INTO user_permissions (user_id, permission_id)
               SELECT $1, unnest($2::int[])
               ON CONFLICT DO NOTHING`,
              [userId, permissionIds]
            );
          }
        }
      }

      await client.query('COMMIT');
      res.json({ message: 'User updated successfully' });
    } catch (error) {
      await client.query('ROLLBACK');
      console.error('Error updating user:', error);
      res.status(500).json({ error: 'Server error' });
    } finally {
      client.release();
    }
  });

  router.delete('/users/:id', authenticate, requirePermission('delete:users'), async (req, res) => {
    try {
      const userId = req.params.id;
      if (req.user.id === parseInt(userId, 10)) {
        return res.status(400).json({ error: 'Cannot delete your own account' });
      }
      const result = await pool.query(
        'DELETE FROM users WHERE id = $1 RETURNING id',
        [userId]
      );
      if (result.rows.length === 0) {
        return res.status(404).json({ error: 'User not found' });
      }
      res.json({ message: 'User deleted successfully' });
    } catch (error) {
      console.error('Error deleting user:', error);
      res.status(500).json({ error: 'Server error' });
    }
  });

  router.get('/permissions/categories', authenticate, requirePermission('view:users'), async (req, res) => {
    try {
      const result = await pool.query(`
        SELECT category, json_agg(
          json_build_object(
            'id', id, 'name', name, 'code', code, 'description', description
          ) ORDER BY name
        ) as permissions
        FROM permissions
        GROUP BY category
        ORDER BY category
      `);
      res.json(result.rows);
    } catch (error) {
      console.error('Error getting permissions:', error);
      res.status(500).json({ error: 'Server error' });
    }
  });

  router.get('/permissions', authenticate, requirePermission('view:users'), async (req, res) => {
    try {
      const result = await pool.query(`
        SELECT * FROM permissions ORDER BY category, name
      `);
      res.json(result.rows);
    } catch (error) {
      console.error('Error getting permissions:', error);
      res.status(500).json({ error: 'Server error' });
    }
  });

  return router;
}

function normalizePermissionIds(permissions) {
  return permissions
    .map((p) => {
      if (typeof p === 'object' && p?.id) return parseInt(p.id, 10);
      if (typeof p === 'number') return p;
      if (typeof p === 'string' && !Number.isNaN(parseInt(p, 10))) return parseInt(p, 10);
      return null;
    })
    .filter((id) => id !== null && !Number.isNaN(id));
}
