/**
 * Employee Routes
 */

import express, { Request, Response } from 'express';
import { body, validationResult } from 'express-validator';
import { getModels } from '../models';
import { authenticateJwtOrApiToken } from '../middleware/dualAuth';
import { requirePermission } from '../middleware/permissions';
import { validateSocialProfiles, normalizeSocialProfiles } from '../utils/socialUrlValidation';

const router = express.Router();

// Escape a string for safe use inside a RegExp — an email legitimately contains
// '.', '+' and other characters that would otherwise be read as metacharacters.
const escapeRegex = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

router.use(authenticateJwtOrApiToken);

// Get all employees for a company
router.get('/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    const { Employee } = getModels();

    if (!req.user!.companyIds.includes(companyId) && req.user!.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const employees = await Employee.find({ companyId });
    res.json(employees);
  } catch (error) {
    res.status(500).json({ error: 'Failed to get employees' });
  }
});

// Get single employee
router.get('/detail/:id', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { Employee } = getModels();

    const employee = await Employee.findById(id);
    if (!employee) {
      res.status(404).json({ error: 'Employee not found' });
      return;
    }

    if (!req.user!.companyIds.includes(employee.companyId) && req.user!.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    res.json(employee);
  } catch (error) {
    res.status(500).json({ error: 'Failed to get employee' });
  }
});

// Create employee
router.post(
  '/',
  requirePermission('employees', 'create'),
  [
    body('name').trim().notEmpty().withMessage('Employee name is required'),
    body('companyId').notEmpty().withMessage('Company ID is required'),
    // Country code is mandatory whenever a phone number is provided (Task 5).
    body('phoneCountryCode').custom((value, { req }) => {
      if (req.body.phone && String(req.body.phone).trim() && !String(value ?? '').trim()) {
        throw new Error('Please select a country code.');
      }
      return true;
    }),
  ],
  async (req: Request, res: Response) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        res.status(400).json({ errors: errors.array() });
        return;
      }

      if (!req.user!.companyIds.includes(req.body.companyId) && req.user!.role !== 'admin') {
        res.status(403).json({ error: 'Access denied' });
        return;
      }

      const { Employee } = getModels();

      // Build clean employee data from request body
      const { name, companyId, designation, department, level, email, phone, phoneCountryCode, city, state, country,
        dateOfBirth, workAnniversary, expertise, responsibilityArea, reportsTo, bio,
        socialProfiles, assets, photos, driveLink } = req.body;

      // Check for duplicate employee with same name within the company
      if (name && name.trim()) {
        const nameDuplicate = await Employee.findOne({ companyId, name: { $regex: new RegExp(`^${name.trim()}$`, 'i') } });
        if (nameDuplicate) {
          res.status(400).json({ error: 'An employee with this name already exists in your company' });
          return;
        }
      }

      // Check for duplicate employee with same email within the company.
      // Matched case-insensitively (same way the name check above works): emails are
      // stored exactly as typed, so an equality test against a lower-cased value
      // never found an existing "John@Example.com" and let the duplicate through.
      if (email && email.trim()) {
        const emailDuplicate = await Employee.findOne({
          companyId,
          email: { $regex: `^${escapeRegex(email.trim())}$`, $options: 'i' },
        });
        if (emailDuplicate) {
          res.status(400).json({ error: 'An employee with this email already exists in your company' });
          return;
        }
      }

      // Reject social links that aren't valid URLs for their platform, using the
      // same utility the Founder routes use — the employee routes had no check at
      // all, so "linkedin" reached the database via the API and the CSV import.
      const socialError = validateSocialProfiles(socialProfiles);
      if (socialError) {
        res.status(400).json({ error: socialError });
        return;
      }

      // Helper: non-empty string (skips undefined, null, "")
      const hasValue = (v: any) => v !== undefined && v !== null && v !== '';
      // Helper: non-empty object
      const isObj = (v: any) => v && typeof v === 'object' && !Array.isArray(v);

      const employeeData: any = { name, companyId };
      if (hasValue(designation)) employeeData.designation = designation;
      if (hasValue(department)) employeeData.department = department;
      if (hasValue(level)) employeeData.level = level;
      if (hasValue(email)) employeeData.email = email;
      if (hasValue(phone)) employeeData.phone = phone;
      if (hasValue(phoneCountryCode)) employeeData.phoneCountryCode = phoneCountryCode;
      if (hasValue(city)) employeeData.city = city;
      if (hasValue(state)) employeeData.state = state;
      if (hasValue(country)) employeeData.country = country;
      if (hasValue(dateOfBirth)) employeeData.dateOfBirth = dateOfBirth;
      if (hasValue(workAnniversary)) employeeData.workAnniversary = workAnniversary;
      if (hasValue(responsibilityArea)) employeeData.responsibilityArea = responsibilityArea;
      if (hasValue(reportsTo)) employeeData.reportsTo = reportsTo;
      if (hasValue(bio)) employeeData.bio = bio;
      if (hasValue(driveLink)) employeeData.driveLink = driveLink;
      // Store links in a consistent format — prepend https:// when missing.
      if (isObj(socialProfiles)) employeeData.socialProfiles = normalizeSocialProfiles(socialProfiles);
      if (Array.isArray(expertise)) employeeData.expertise = expertise;
      if (Array.isArray(assets)) employeeData.assets = assets;
      if (Array.isArray(photos)) employeeData.photos = photos;

      const employee = new Employee({ ...employeeData, createdBy: req.user!._id });
      await employee.save();

      res.status(201).json(employee);
    } catch (error: any) {
      console.error('[Employee] Create error:', error.message || error);
      if (error?.name === 'ValidationError') {
        const messages = Object.values(error.errors).map((e: any) => e.message);
        res.status(400).json({ error: messages.join('. '), details: error.message });
      } else if (error?.code === 11000) {
        // A unique index rejected the save (companyId+name or companyId+email).
        // Report the existing duplicate message instead of an opaque 500.
        const onEmail = Object.keys(error?.keyPattern || {}).includes('email');
        res.status(400).json({
          error: onEmail
            ? 'An employee with this email already exists in your company'
            : 'An employee with this name already exists in your company',
        });
      } else {
        res.status(500).json({ error: 'Failed to create employee', details: error?.message });
      }
    }
  }
);

// Update employee
router.put('/:id', requirePermission('employees', 'edit'), async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { Employee } = getModels();

    const employee = await Employee.findById(id);
    if (!employee) {
      res.status(404).json({ error: 'Employee not found' });
      return;
    }

    if (!req.user!.companyIds.includes(employee.companyId) && req.user!.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Country code is mandatory whenever a phone number is being set (Task 5).
    if (req.body.phone !== undefined && String(req.body.phone).trim() &&
        !String(req.body.phoneCountryCode ?? '').trim()) {
      res.status(400).json({ error: 'Please select a country code.' });
      return;
    }

    // Sanitize update data — only allow known schema fields
    const { name, companyId: _cid, designation, department, level, email, phone, phoneCountryCode, city, state, country,
      dateOfBirth, workAnniversary, expertise, responsibilityArea, reportsTo, bio,
      socialProfiles, assets, photos, driveLink } = req.body;

    const hasValue = (v: any) => v !== undefined && v !== null && v !== '';
    const isObj = (v: any) => v && typeof v === 'object' && !Array.isArray(v);

    // Reject social links that aren't valid URLs for their platform (see create route).
    if (socialProfiles !== undefined) {
      const socialError = validateSocialProfiles(socialProfiles);
      if (socialError) {
        res.status(400).json({ error: socialError });
        return;
      }
    }

    // Check for duplicate name within the company (excluding current employee)
    if (name && name.trim()) {
      const nameDuplicate = await Employee.findOne({
        companyId: employee.companyId,
        name: { $regex: new RegExp(`^${name.trim()}$`, 'i') },
        _id: { $ne: id }
      });
      if (nameDuplicate) {
        res.status(400).json({ error: 'An employee with this name already exists in your company' });
        return;
      }
    }

    // Check for duplicate email within the company (excluding current employee).
    // Case-insensitive for the same reason as the create route above; excluding the
    // current id keeps a normal edit that leaves the email unchanged working.
    if (email && email.trim()) {
      const emailDuplicate = await Employee.findOne({
        companyId: employee.companyId,
        email: { $regex: `^${escapeRegex(email.trim())}$`, $options: 'i' },
        _id: { $ne: id }
      });
      if (emailDuplicate) {
        res.status(400).json({ error: 'An employee with this email already exists in your company' });
        return;
      }
    }

    if (hasValue(name)) employee.name = name;
    if (hasValue(designation)) employee.designation = designation;
    if (hasValue(department)) employee.department = department;
    if (hasValue(level)) employee.level = level;
    if (email !== undefined) employee.email = email;
    if (phone !== undefined) employee.phone = phone;
    if (phoneCountryCode !== undefined) employee.phoneCountryCode = phoneCountryCode;
    if (city !== undefined) employee.city = city;
    if (state !== undefined) employee.state = state;
    if (country !== undefined) employee.country = country;
    if (hasValue(dateOfBirth)) employee.dateOfBirth = dateOfBirth;
    if (hasValue(workAnniversary)) employee.workAnniversary = workAnniversary;
    if (hasValue(responsibilityArea)) employee.responsibilityArea = responsibilityArea;
    if (hasValue(reportsTo)) employee.reportsTo = reportsTo;
    if (bio !== undefined) employee.bio = bio;
    if (hasValue(driveLink)) employee.driveLink = driveLink;
    if (isObj(socialProfiles)) employee.socialProfiles = normalizeSocialProfiles(socialProfiles);
    if (Array.isArray(expertise)) employee.expertise = expertise;
    if (Array.isArray(assets)) employee.assets = assets;
    if (Array.isArray(photos)) employee.photos = photos;

    employee.updatedAt = new Date().toISOString() as any;
    await employee.save();

    res.json(employee);
  } catch (error: any) {
    // Return Mongoose validation errors as 400 with clear messages
    if (error?.name === 'ValidationError') {
      const messages = Object.values(error.errors).map((e: any) => e.message);
      res.status(400).json({ error: messages.join('. '), details: error?.message });
      return;
    }
    // A unique index rejected the save — report the existing duplicate message.
    if (error?.code === 11000) {
      const onEmail = Object.keys(error?.keyPattern || {}).includes('email');
      res.status(400).json({
        error: onEmail
          ? 'An employee with this email already exists in your company'
          : 'An employee with this name already exists in your company',
      });
      return;
    }
    res.status(500).json({ error: 'Failed to update employee', details: error?.message });
  }
});

// Clear all employees for a company (atomic bulk delete)
router.delete('/clear/:companyId', requirePermission('employees', 'delete'), async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    const { Employee } = getModels();

    if (!req.user!.companyIds.includes(companyId) && req.user!.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const result = await Employee.deleteMany({ companyId });
    res.json({ message: 'Employees cleared successfully', deletedCount: result.deletedCount });
  } catch (error) {
    res.status(500).json({ error: 'Failed to clear employees' });
  }
});

// Delete employee
router.delete('/:id', requirePermission('employees', 'delete'), async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { Employee } = getModels();

    const employee = await Employee.findById(id);
    if (!employee) {
      res.status(404).json({ error: 'Employee not found' });
      return;
    }

    if (!req.user!.companyIds.includes(employee.companyId) && req.user!.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    await Employee.findByIdAndDelete(id);
    res.json({ message: 'Employee deleted successfully' });
  } catch (error) {
    res.status(500).json({ error: 'Failed to delete employee' });
  }
});

export default router;
