/**
 * User Routes
 */

import express, { Request, Response } from 'express';
import { body, validationResult } from 'express-validator';
import { getModels } from '../models';
import { authenticate } from '../middleware/auth';
import { requirePermission } from '../middleware/permissions';

const router = express.Router();

router.use(authenticate);

// Get all users (requires employees:view permission)
router.get('/', requirePermission('employees', 'view'), async (req: Request, res: Response) => {
  try {
    const { User } = getModels();
    const users = await User.find().select('-passwordHash').sort({ name: 1 });
    res.json(users);
  } catch (error) {
    res.status(500).json({ error: 'Failed to get users' });
  }
});

// Get current user profile
router.get('/profile', async (req: Request, res: Response) => {
  try {
    const { User } = getModels();
    const user = await User.findById(req.user!._id).select('-passwordHash');
    res.json(user);
  } catch (error) {
    res.status(500).json({ error: 'Failed to get profile' });
  }
});

// Update current user profile
router.put(
  '/profile',
  [
    body('name').optional().trim().notEmpty(),
    body('email').optional().isEmail(),
  ],
  async (req: Request, res: Response) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        res.status(400).json({ errors: errors.array() });
        return;
      }

      const allowedUpdates = ['name', 'email', 'avatar'];
      const updates: any = {};

      allowedUpdates.forEach((key) => {
        if (req.body[key] !== undefined) {
          updates[key] = req.body[key];
        }
      });

      Object.assign(req.user!, updates);
      await req.user!.save();

      res.json({
        id: req.user!._id,
        email: req.user!.email,
        name: req.user!.name,
        role: req.user!.role,
        avatar: req.user!.avatar,
      });
    } catch (error) {
      res.status(500).json({ error: 'Failed to update profile' });
    }
  }
);

// Change password (any authenticated user can change their own password).
// Enforces the same password policy used at registration (min 8 chars +
// uppercase/lowercase/number/special) — the single source of truth for
// self-service passwords across the app.
router.put(
  '/change-password',
  [
    body('currentPassword').notEmpty().withMessage('Current password is required'),
    body('newPassword')
      .isLength({ min: 8 }).withMessage('New password must be at least 8 characters')
      .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d\s])/)
      .withMessage('Password must include uppercase, lowercase, number, and special character'),
  ],
  async (req: Request, res: Response) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        res.status(400).json({ errors: errors.array() });
        return;
      }

      const { currentPassword, newPassword } = req.body;

      // New password must differ from the current one.
      if (currentPassword === newPassword) {
        res.status(400).json({ error: 'New password must be different from your current password' });
        return;
      }

      const isMatch = await req.user!.comparePassword(currentPassword);
      if (!isMatch) {
        // 400 (not 401): this is a form-field validation failure, not a session
        // auth failure — a 401 would trip the client's global "session expired"
        // handler and log the user out mid-form.
        res.status(400).json({ error: 'Current password is incorrect' });
        return;
      }

      // Assigning the plaintext triggers the User pre-save hook, which hashes it
      // with bcrypt before persisting. The hash is stripped from all responses.
      req.user!.passwordHash = newPassword;
      await req.user!.save();

      res.json({ message: 'Password changed successfully' });
    } catch (error) {
      res.status(500).json({ error: 'Failed to change password' });
    }
  }
);

// Get single user by ID (requires employees:view permission)
router.get('/:id', requirePermission('employees', 'view'), async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { User } = getModels();

    const user = await User.findById(id).select('-passwordHash');
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    res.json(user);
  } catch (error) {
    res.status(500).json({ error: 'Failed to get user' });
  }
});

// Update user (requires employees:edit permission)
router.put('/:id', requirePermission('employees', 'edit'), async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { User } = getModels();

    const user = await User.findById(id);
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    const allowedUpdates = ['name', 'email', 'role', 'activeCompanyId', 'companyIds'];
    allowedUpdates.forEach((key) => {
      if (req.body[key] !== undefined) {
        (user as any)[key] = req.body[key];
      }
    });

    await user.save();

    res.json(user);
  } catch (error) {
    res.status(500).json({ error: 'Failed to update user' });
  }
});

// Delete user (requires employees:delete permission)
router.delete('/:id', requirePermission('employees', 'delete'), async (req: Request, res: Response) => {
  try {
    const { id } = req.params;

    // Prevent deleting yourself
    if (id === req.user!._id!.toString()) {
      res.status(400).json({ error: 'Cannot delete yourself' });
      return;
    }

    const { User } = getModels();
    await User.findByIdAndDelete(id);
    res.json({ message: 'User deleted successfully' });
  } catch (error) {
    res.status(500).json({ error: 'Failed to delete user' });
  }
});

export default router;
