const SLA = require('../models/SLA');
const SLATracker = require('../models/SLATracker');
const Company = require('../models/Company');
const Role = require('../models/Role');
const ActivityLog = require('../models/ActivityLog');

// Get all SLAs
exports.getAllSLAs = async (req, res, next) => {
  try {
    const { 
      companyId, 
      type,
      isActive,
      search, 
      page = 1, 
      limit = 20,
      sortBy = 'createdAt',
      sortOrder = 'desc'
    } = req.query;
    
    // Build query
    let query = {};
    
    // Check user role and filter accordingly
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global') {
      query.companyId = req.user.companyId;
    } else if (companyId) {
      query.companyId = companyId;
    }
    
    // Filter by type
    if (type) {
      query.type = type;
    }
    
    // Filter by active status
    if (isActive !== undefined) {
      query.isActive = isActive === 'true';
    }
    
    // Search
    if (search) {
      query.$or = [
        { name: { $regex: search, $options: 'i' } },
        { description: { $regex: search, $options: 'i' } }
      ];
    }
    
    // Pagination
    const skip = (page - 1) * limit;
    
    // Sorting
    const sort = {};
    sort[sortBy] = sortOrder === 'asc' ? 1 : -1;
    
    const slas = await SLA.find(query)
      .populate('companyId', 'name code')
      .populate('createdBy', 'firstName lastName email')
      .populate('targets.escalationLevels.escalateTo', 'firstName lastName email')
      .populate('notifications.onBreach.notifyUsers', 'firstName lastName email')
      .populate('notifications.onBreach.notifyRoles', 'name displayName')
      .sort(sort)
      .limit(parseInt(limit))
      .skip(skip);
    
    const total = await SLA.countDocuments(query);
    
    res.status(200).json({
      success: true,
      count: slas.length,
      total,
      page: parseInt(page),
      pages: Math.ceil(total / limit),
      data: slas
    });
  } catch (error) {
    next(error);
  }
};

// Get single SLA
exports.getSLA = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const sla = await SLA.findById(id)
      .populate('companyId', 'name code')
      .populate('createdBy', 'firstName lastName email')
      .populate('targets.escalationLevels.escalateTo', 'firstName lastName email')
      .populate('targets.escalationLevels.notifyUsers', 'firstName lastName email')
      .populate('notifications.onBreach.notifyUsers', 'firstName lastName email')
      .populate('notifications.onBreach.notifyRoles', 'name displayName')
      .populate('notifications.onWarning.notifyUsers', 'firstName lastName email')
      .populate('notifications.onWarning.notifyRoles', 'name displayName');
    
    if (!sla) {
      return res.status(404).json({
        success: false,
        message: 'SLA not found'
      });
    }
    
    // Check access
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global' && sla.companyId._id.toString() !== req.user.companyId.toString()) {
      return res.status(403).json({
        success: false,
        message: 'You do not have access to this SLA'
      });
    }
    
    // Get SLA performance metrics
    const trackers = await SLATracker.find({ slaId: id });
    const totalTrackers = trackers.length;
    const metCount = trackers.filter(t => t.status === 'met').length;
    const breachedCount = trackers.filter(t => t.status === 'breached').length;
    const complianceRate = totalTrackers > 0 ? ((metCount / totalTrackers) * 100).toFixed(2) : 0;
    
    res.status(200).json({
      success: true,
      data: {
        ...sla.toObject(),
        performance: {
          totalTrackers,
          met: metCount,
          breached: breachedCount,
          complianceRate: parseFloat(complianceRate)
        }
      }
    });
  } catch (error) {
    next(error);
  }
};

// Create SLA
exports.createSLA = async (req, res, next) => {
  try {
    const slaData = req.body;
    
    // Verify company exists
    const company = await Company.findById(slaData.companyId);
    if (!company) {
      return res.status(404).json({
        success: false,
        message: 'Company not found'
      });
    }
    
    // Check access
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global' && slaData.companyId !== req.user.companyId.toString()) {
      return res.status(403).json({
        success: false,
        message: 'You can only create SLAs for your own company'
      });
    }
    
    // If setting as default, unset other defaults
    if (slaData.isDefault) {
      await SLA.updateMany(
        { companyId: slaData.companyId, type: slaData.type, isDefault: true },
        { isDefault: false }
      );
    }
    
    // Create SLA
    const sla = await SLA.create({
      ...slaData,
      createdBy: req.user._id
    });
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      companyId: sla.companyId,
      action: 'create',
      module: 'sla',
      entityType: 'SLA',
      entityId: sla._id,
      description: `Created SLA: ${sla.name}`,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    const populatedSLA = await SLA.findById(sla._id)
      .populate('companyId', 'name code')
      .populate('createdBy', 'firstName lastName email');
    
    res.status(201).json({
      success: true,
      message: 'SLA created successfully',
      data: populatedSLA
    });
  } catch (error) {
    next(error);
  }
};

// Update SLA
exports.updateSLA = async (req, res, next) => {
  try {
    const { id } = req.params;
    const updates = req.body;
    
    const sla = await SLA.findById(id);
    
    if (!sla) {
      return res.status(404).json({
        success: false,
        message: 'SLA not found'
      });
    }
    
    // Check access
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global' && sla.companyId.toString() !== req.user.companyId.toString()) {
      return res.status(403).json({
        success: false,
        message: 'You do not have access to this SLA'
      });
    }
    
    // If setting as default, unset other defaults
    if (updates.isDefault) {
      await SLA.updateMany(
        { companyId: sla.companyId, type: sla.type, isDefault: true, _id: { $ne: id } },
        { isDefault: false }
      );
    }
    
    // Don't allow updating certain fields
    delete updates.companyId;
    delete updates.createdBy;
    
    const updatedSLA = await SLA.findByIdAndUpdate(
      id,
      updates,
      { new: true, runValidators: true }
    )
      .populate('companyId', 'name code')
      .populate('createdBy', 'firstName lastName email');
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      companyId: sla.companyId,
      action: 'update',
      module: 'sla',
      entityType: 'SLA',
      entityId: sla._id,
      description: `Updated SLA: ${sla.name}`,
      changes: updates,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    res.status(200).json({
      success: true,
      message: 'SLA updated successfully',
      data: updatedSLA
    });
  } catch (error) {
    next(error);
  }
};

// Delete SLA
exports.deleteSLA = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const sla = await SLA.findById(id);
    
    if (!sla) {
      return res.status(404).json({
        success: false,
        message: 'SLA not found'
      });
    }
    
    // Check access
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global' && sla.companyId.toString() !== req.user.companyId.toString()) {
      return res.status(403).json({
        success: false,
        message: 'You do not have access to this SLA'
      });
    }
    
    // Check if SLA is being used
    const trackerCount = await SLATracker.countDocuments({ slaId: id, status: { $in: ['pending', 'in_progress'] } });
    if (trackerCount > 0) {
      return res.status(400).json({
        success: false,
        message: `Cannot delete SLA. ${trackerCount} active tracker(s) are using this SLA.`
      });
    }
    
    await SLA.findByIdAndDelete(id);
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      companyId: sla.companyId,
      action: 'delete',
      module: 'sla',
      entityType: 'SLA',
      entityId: sla._id,
      description: `Deleted SLA: ${sla.name}`,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    res.status(200).json({
      success: true,
      message: 'SLA deleted successfully'
    });
  } catch (error) {
    next(error);
  }
};

// Get SLA trackers
exports.getSLATrackers = async (req, res, next) => {
  try {
    const { 
      companyId,
      slaId,
      entityType,
      entityId,
      status,
      priority,
      breached,
      page = 1,
      limit = 20
    } = req.query;
    
    let query = {};
    
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global') {
      query.companyId = req.user.companyId;
    } else if (companyId) {
      query.companyId = companyId;
    }
    
    if (slaId) query.slaId = slaId;
    if (entityType) query.entityType = entityType;
    if (entityId) query.entityId = entityId;
    if (status) query.status = status;
    if (priority) query.priority = priority;
    if (breached === 'true') {
      query.$or = [
        { 'response.status': 'breached' },
        { 'resolution.status': 'breached' }
      ];
    }
    
    const skip = (page - 1) * limit;
    
    const trackers = await SLATracker.find(query)
      .populate('companyId', 'name code')
      .populate('slaId', 'name type')
      .populate('response.metBy', 'firstName lastName')
      .populate('resolution.resolvedBy', 'firstName lastName')
      .populate('escalations.escalatedTo', 'firstName lastName')
      .sort({ createdAt: -1 })
      .limit(parseInt(limit))
      .skip(skip);
    
    const total = await SLATracker.countDocuments(query);
    
    res.status(200).json({
      success: true,
      count: trackers.length,
      total,
      page: parseInt(page),
      pages: Math.ceil(total / limit),
      data: trackers
    });
  } catch (error) {
    next(error);
  }
};

// Get single SLA tracker
exports.getSLATracker = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const tracker = await SLATracker.findById(id)
      .populate('companyId', 'name code')
      .populate('slaId')
      .populate('response.metBy', 'firstName lastName email')
      .populate('resolution.resolvedBy', 'firstName lastName email')
      .populate('escalations.escalatedTo', 'firstName lastName email')
      .populate('notifications.sentTo', 'firstName lastName email');
    
    if (!tracker) {
      return res.status(404).json({
        success: false,
        message: 'SLA tracker not found'
      });
    }
    
    res.status(200).json({
      success: true,
      data: tracker
    });
  } catch (error) {
    next(error);
  }
};

// Create SLA tracker (usually called automatically)
exports.createSLATracker = async (req, res, next) => {
  try {
    const { companyId, slaId, entityType, entityId, priority } = req.body;
    
    // Get SLA details
    const sla = await SLA.findById(slaId);
    if (!sla) {
      return res.status(404).json({
        success: false,
        message: 'SLA not found'
      });
    }
    
    // Find target for priority
    const target = sla.targets.find(t => t.priority === priority);
    if (!target) {
      return res.status(400).json({
        success: false,
        message: `No SLA target defined for priority: ${priority}`
      });
    }
    
    // Calculate target times
    const now = new Date();
    const responseTargetTime = calculateTargetTime(now, target.responseTime, sla.businessHours);
    const resolutionTargetTime = calculateTargetTime(now, target.resolutionTime, sla.businessHours);
    
    // Create tracker
    const tracker = await SLATracker.create({
      companyId,
      slaId,
      entityType,
      entityId,
      priority,
      response: {
        targetTime: responseTargetTime
      },
      resolution: {
        targetTime: resolutionTargetTime
      },
      timeline: {
        startTime: now
      }
    });
    
    // Log activity
    await ActivityLog.create({
      userId: req.user._id,
      companyId,
      action: 'create',
      module: 'sla',
      entityType: 'SLATracker',
      entityId: tracker._id,
      description: `Started SLA tracking for ${entityType}`,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    });
    
    res.status(201).json({
      success: true,
      message: 'SLA tracker created successfully',
      data: tracker
    });
  } catch (error) {
    next(error);
  }
};

// Mark response as met
exports.markResponseMet = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const tracker = await SLATracker.findById(id);
    
    if (!tracker) {
      return res.status(404).json({
        success: false,
        message: 'SLA tracker not found'
      });
    }
    
    if (tracker.response.status !== 'pending') {
      return res.status(400).json({
        success: false,
        message: 'Response already marked'
      });
    }
    
    const now = new Date();
    tracker.response.actualTime = now;
    tracker.response.metBy = req.user._id;
    
    if (now <= tracker.response.targetTime) {
      tracker.response.status = 'met';
    } else {
      tracker.response.status = 'breached';
      tracker.response.breachTime = Math.round((now - tracker.response.targetTime) / 60000); // Minutes
      tracker.metrics.breachCount += 1;
    }
    
    tracker.metrics.responseTimeMinutes = Math.round((now - tracker.timeline.startTime) / 60000);
    
    await tracker.save();
    
    res.status(200).json({
      success: true,
      message: 'Response time marked',
      data: tracker
    });
  } catch (error) {
    next(error);
  }
};

// Mark resolution as met
exports.markResolutionMet = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const tracker = await SLATracker.findById(id);
    
    if (!tracker) {
      return res.status(404).json({
        success: false,
        message: 'SLA tracker not found'
      });
    }
    
    if (tracker.resolution.status !== 'pending') {
      return res.status(400).json({
        success: false,
        message: 'Resolution already marked'
      });
    }
    
    const now = new Date();
    tracker.resolution.actualTime = now;
    tracker.resolution.resolvedBy = req.user._id;
    tracker.timeline.completedAt = now;
    
    if (now <= tracker.resolution.targetTime) {
      tracker.resolution.status = 'met';
      tracker.status = 'met';
    } else {
      tracker.resolution.status = 'breached';
      tracker.resolution.breachTime = Math.round((now - tracker.resolution.targetTime) / 60000);
      tracker.status = 'breached';
      tracker.metrics.breachCount += 1;
    }
    
    tracker.metrics.resolutionTimeMinutes = Math.round((now - tracker.timeline.startTime - tracker.timeline.totalPausedTime * 60000) / 60000);
    
    await tracker.save();
    
    res.status(200).json({
      success: true,
      message: 'Resolution time marked',
      data: tracker
    });
  } catch (error) {
    next(error);
  }
};

// Pause SLA tracker
exports.pauseSLATracker = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const tracker = await SLATracker.findById(id);
    
    if (!tracker) {
      return res.status(404).json({
        success: false,
        message: 'SLA tracker not found'
      });
    }
    
    if (tracker.status === 'paused') {
      return res.status(400).json({
        success: false,
        message: 'Tracker is already paused'
      });
    }
    
    tracker.status = 'paused';
    tracker.timeline.pausedAt = new Date();
    await tracker.save();
    
    res.status(200).json({
      success: true,
      message: 'SLA tracker paused',
      data: tracker
    });
  } catch (error) {
    next(error);
  }
};

// Resume SLA tracker
exports.resumeSLATracker = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const tracker = await SLATracker.findById(id);
    
    if (!tracker) {
      return res.status(404).json({
        success: false,
        message: 'SLA tracker not found'
      });
    }
    
    if (tracker.status !== 'paused') {
      return res.status(400).json({
        success: false,
        message: 'Tracker is not paused'
      });
    }
    
    const pausedDuration = Math.round((new Date() - tracker.timeline.pausedAt) / 60000);
    tracker.timeline.totalPausedTime += pausedDuration;
    tracker.timeline.resumedAt = new Date();
    tracker.status = 'in_progress';
    
    // Adjust target times
    const adjustment = pausedDuration * 60000;
    tracker.response.targetTime = new Date(tracker.response.targetTime.getTime() + adjustment);
    tracker.resolution.targetTime = new Date(tracker.resolution.targetTime.getTime() + adjustment);
    
    await tracker.save();
    
    res.status(200).json({
      success: true,
      message: 'SLA tracker resumed',
      data: tracker
    });
  } catch (error) {
    next(error);
  }
};

// Get SLA statistics
exports.getSLAStats = async (req, res, next) => {
  try {
    const { companyId, startDate, endDate } = req.query;
    
    let query = {};
    
    const userRole = await Role.findById(req.user.roleId);
    if (userRole.scope !== 'global') {
      query.companyId = req.user.companyId;
    } else if (companyId) {
      query.companyId = companyId;
    }
    
    if (startDate && endDate) {
      query.createdAt = {
        $gte: new Date(startDate),
        $lte: new Date(endDate)
      };
    }
    
    const totalTrackers = await SLATracker.countDocuments(query);
    const metTrackers = await SLATracker.countDocuments({ ...query, status: 'met' });
    const breachedTrackers = await SLATracker.countDocuments({ ...query, status: 'breached' });
    const activeTrackers = await SLATracker.countDocuments({ ...query, status: { $in: ['pending', 'in_progress'] } });
    
    const complianceRate = totalTrackers > 0 ? ((metTrackers / totalTrackers) * 100).toFixed(2) : 0;
    
    // By priority
    const byPriority = await SLATracker.aggregate([
      { $match: query },
      {
        $group: {
          _id: '$priority',
          total: { $sum: 1 },
          met: { $sum: { $cond: [{ $eq: ['$status', 'met'] }, 1, 0] } },
          breached: { $sum: { $cond: [{ $eq: ['$status', 'breached'] }, 1, 0] } }
        }
      }
    ]);
    
    // By entity type
    const byEntityType = await SLATracker.aggregate([
      { $match: query },
      {
        $group: {
          _id: '$entityType',
          total: { $sum: 1 },
          met: { $sum: { $cond: [{ $eq: ['$status', 'met'] }, 1, 0] } },
          breached: { $sum: { $cond: [{ $eq: ['$status', 'breached'] }, 1, 0] } }
        }
      }
    ]);
    
    // Average response and resolution times
    const avgTimes = await SLATracker.aggregate([
      { $match: { ...query, status: { $in: ['met', 'breached'] } } },
      {
        $group: {
          _id: null,
          avgResponseTime: { $avg: '$metrics.responseTimeMinutes' },
          avgResolutionTime: { $avg: '$metrics.resolutionTimeMinutes' }
        }
      }
    ]);
    
    // Breach trends (last 30 days)
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
    
    const breachTrend = await SLATracker.aggregate([
      {
        $match: {
          ...query,
          createdAt: { $gte: thirtyDaysAgo },
          status: 'breached'
        }
      },
      {
        $group: {
          _id: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt' } },
          count: { $sum: 1 }
        }
      },
      { $sort: { _id: 1 } }
    ]);
    
    res.status(200).json({
      success: true,
      data: {
        overview: {
          total: totalTrackers,
          met: metTrackers,
          breached: breachedTrackers,
          active: activeTrackers,
          complianceRate: parseFloat(complianceRate)
        },
        byPriority,
        byEntityType,
        averageTimes: {
          responseMinutes: avgTimes[0]?.avgResponseTime || 0,
          resolutionMinutes: avgTimes[0]?.avgResolutionTime || 0
        },
        breachTrend
      }
    });
  } catch (error) {
    next(error);
  }
};

// Helper function to calculate target time
const calculateTargetTime = (startTime, duration, businessHours) => {
  let targetTime = new Date(startTime);
  let minutesToAdd = duration.value;
  
  // Convert to minutes
  if (duration.unit === 'hours') {
    minutesToAdd *= 60;
  } else if (duration.unit === 'days') {
    minutesToAdd *= 24 * 60;
  }
  
  if (!businessHours || !businessHours.enabled) {
    // Simple calculation without business hours
    targetTime.setMinutes(targetTime.getMinutes() + minutesToAdd);
    return targetTime;
  }
  
  // Complex calculation with business hours
  // This is a simplified version - in production, you'd need more sophisticated logic
  const workHoursPerDay = 9; // 9 AM to 6 PM = 9 hours
  const businessDays = businessHours.workingDays.length;
  
  targetTime.setMinutes(targetTime.getMinutes() + minutesToAdd);
  return targetTime;
};