// controllers/leadController.js
const Lead = require('../models/Lead');
const Company = require('../models/Company');
const Product = require('../models/Product');
const User = require('../models/User');
const Role = require('../models/Role');
const ActivityLog = require('../models/ActivityLog');
const LeadView = require('../models/LeadView');

class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

// Get all leads
const getAllLeads = async (req, res, next) => {
  try {
    const {
      status,
      priority,
      search,
      page = 1,
      limit = 20,
      sortBy = 'createdAt',
      sortOrder = 'desc'
    } = req.query;

    let query = {};

    // Filter by company/ownership
    if (req.user) {
      const userRole = await Role.findById(req.user.roleId);
      const isGlobal = userRole && userRole.scope === 'global';
      const isAdmin = userRole && (userRole.name === 'admin' || userRole.name === 'super_admin');

      if (!isGlobal) {
        if (req.user.companyId) {
          query.companyId = req.user.companyId;
        }

        const ownershipFilter = {
          $or: [
            { createdBy: req.user._id },
            { assignedTo: req.user._id }
          ]
        };

        // Combine with any existing filters
        if (Object.keys(query).length > 1 || (query.companyId && Object.keys(query).length > 1)) {
          query = { $and: [query, ownershipFilter] };
        } else {
          Object.assign(query, ownershipFilter);
        }
      }
    }

    if (status && status !== 'all') {
      query.status = status;
    }

    if (priority && priority !== 'all') {
      query.priority = priority;
    }

    if (search && search.trim()) {
      const searchRegex = new RegExp(search.trim(), 'i');
      query.$or = [
        { 'customer.name': searchRegex },
        { 'customer.email': searchRegex },
        { 'customer.company': searchRegex },
        { leadNumber: searchRegex }
      ];
    }

    const pageNum = Math.max(1, parseInt(page));
    const limitNum = Math.min(100, Math.max(1, parseInt(limit)));
    const skip = (pageNum - 1) * limitNum;

    const allowedSortFields = ['createdAt', 'updatedAt', 'customer.name', 'status', 'priority'];
    const sortField = allowedSortFields.includes(sortBy) ? sortBy : 'createdAt';
    const sortDirection = sortOrder === 'asc' ? 1 : -1;

    const sort = {};
    sort[sortField] = sortDirection;

    const [leads, totalLeads] = await Promise.all([
      Lead.find(query)
        .populate('companyId', 'name code')
        .populate('assignedTo', 'firstName lastName email')
        .populate('createdBy', 'firstName lastName email')
        .populate('updatedBy', 'firstName lastName email')
        .sort(sort)
        .limit(limitNum)
        .skip(skip)
        .lean(),
      Lead.countDocuments(query)
    ]);

    // ✅ Update LeadView to mark as seen
    if (req.user) {
      await LeadView.findOneAndUpdate(
        { userId: req.user._id },
        { lastViewedAt: new Date() },
        { upsert: true, new: true }
      );
    }

    res.status(200).json({
      success: true,
      data: {
        leads,
        pagination: {
          currentPage: pageNum,
          totalPages: Math.ceil(totalLeads / limitNum),
          totalLeads,
          limit: limitNum,
          hasNextPage: pageNum < Math.ceil(totalLeads / limitNum),
          hasPrevPage: pageNum > 1
        }
      }
    });
  } catch (error) {
    console.error('Get all leads error:', error);
    next(error);
  }
};

// Get single lead
const getLead = async (req, res, next) => {
  try {
    const { id } = req.params;

    if (!id.match(/^[0-9a-fA-F]{24}$/)) {
      throw new AppError('Invalid lead ID format', 400);
    }

    const lead = await Lead.findById(id)
      .populate('companyId', 'name code email phone address')
      .populate('assignedTo', 'firstName lastName email')
      .populate('createdBy', 'firstName lastName email')
      .populate('updatedBy', 'firstName lastName email');

    if (!lead) {
      throw new AppError('Lead not found', 404);
    }

    if (req.user && req.user.companyId) {
      const userRole = await Role.findById(req.user.roleId);
      if (userRole && userRole.scope !== 'global') {
        if (lead.companyId._id.toString() !== req.user.companyId.toString()) {
          throw new AppError('You do not have access to this lead', 403);
        }
      }
    }

    res.status(200).json({
      success: true,
      data: { lead }
    });
  } catch (error) {
    console.error('Get lead error:', error);
    next(error);
  }
};

// Create lead
const createLead = async (req, res, next) => {
  try {
    const { companyId, customer, assignedTo, priority, status, notes } = req.body;

    // Validate required fields
    if (!customer || !customer.name || !customer.email) {
      throw new AppError('Customer name and email are required', 400);
    }

    // Validate companyId
    const targetCompanyId = companyId || req.user?.companyId;
    if (!targetCompanyId) {
      throw new AppError('Company ID is required', 400);
    }

    // Verify company exists
    const company = await Company.findById(targetCompanyId);
    if (!company) {
      throw new AppError('Company not found', 404);
    }

    // Check for duplicate lead email
    const existingLead = await Lead.findOne({
      companyId: targetCompanyId,
      'customer.email': customer.email.toLowerCase().trim()
    });

    if (existingLead) {
      throw new AppError('A lead with this email already exists for this company', 409);
    }

    // Handle assignment
    let finalAssignedTo = assignedTo || null;
    if (req.body.selfAssign === true) {
      finalAssignedTo = req.user?._id;
    } else if (req.body.selfAssign === false) {
      finalAssignedTo = null;
    }

    // Create lead
    const lead = await Lead.create({
      companyId: targetCompanyId,
      customer: {
        name: customer.name.trim(),
        email: customer.email.toLowerCase().trim(),
        phone: customer.phone?.trim() || '',
        company: customer.company?.trim() || ''
      },
      assignedTo: finalAssignedTo,
      priority: priority || 'medium',
      status: status || 'new',
      notes: notes?.trim() || '',
      createdBy: req.user?._id,
      updatedBy: req.user?._id
    });

    // Log activity
    await ActivityLog.create({
      userId: req.user?._id,
      companyId: lead.companyId,
      action: 'create',
      module: 'leads',
      entityType: 'Lead',
      entityId: lead._id,
      description: `Created lead: ${lead.customer.name}`,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    }).catch(err => console.error('Activity log error:', err));

    const populatedLead = await Lead.findById(lead._id)
      .populate('companyId', 'name code')
      .populate('assignedTo', 'firstName lastName email')
      .populate('createdBy', 'firstName lastName email');

    res.status(201).json({
      success: true,
      message: 'Lead created successfully',
      data: { lead: populatedLead }
    });
  } catch (error) {
    console.error('Create lead error:', error);

    if (error.code === 11000) {
      return next(new AppError('A lead with this information already exists', 409));
    }

    if (error.name === 'ValidationError') {
      const messages = Object.values(error.errors).map(err => err.message);
      return next(new AppError(messages.join(', '), 400));
    }

    next(error);
  }
};

// Update lead
const updateLead = async (req, res, next) => {
  try {
    const { id } = req.params;
    const updates = req.body;

    if (!id.match(/^[0-9a-fA-F]{24}$/)) {
      throw new AppError('Invalid lead ID format', 400);
    }

    const lead = await Lead.findById(id);

    if (!lead) {
      throw new AppError('Lead not found', 404);
    }

    if (req.user) {
      const userRole = await Role.findById(req.user.roleId);
      const isGlobal = userRole && userRole.scope === 'global';
      const isCreator = lead.createdBy?.toString() === req.user._id.toString();
      const isAssignee = lead.assignedTo?.toString() === req.user._id.toString();

      // 1. Company access check for non-global users
      if (!isGlobal && req.user.companyId) {
        if (lead.companyId.toString() !== req.user.companyId.toString()) {
          throw new AppError('You do not have access to this lead', 403);
        }
      }

      // 2. Ownership/Assignee check
      if (!isGlobal && !isCreator && !isAssignee) {
        throw new AppError('Only the creator or assigned user can modify this lead', 403);
      }
    }

    // Handle self-assignment (set to null if true as per latest request)
    if (updates.selfAssign === true) {
      updates.assignedTo = null;
    }
    delete updates.selfAssign;

    // Don't allow updating certain fields
    delete updates.leadNumber;
    delete updates.companyId;
    delete updates.createdBy;
    delete updates.createdAt;
    delete updates._id;
    delete updates.__v;

    // Don't allow updating products anymore
    delete updates.products;

    // Trim customer fields
    if (updates.customer) {
      if (updates.customer.name) updates.customer.name = updates.customer.name.trim();
      if (updates.customer.email) updates.customer.email = updates.customer.email.toLowerCase().trim();
      if (updates.customer.phone) updates.customer.phone = updates.customer.phone.trim();
      if (updates.customer.company) updates.customer.company = updates.customer.company.trim();
    }

    if (updates.notes) {
      updates.notes = updates.notes.trim();
    }

    updates.updatedBy = req.user._id;

    const updatedLead = await Lead.findByIdAndUpdate(
      id,
      updates,
      { new: true, runValidators: true, context: 'query' }
    )
      .populate('companyId', 'name code')
      .populate('assignedTo', 'firstName lastName email')
      .populate('createdBy', 'firstName lastName email')
      .populate('updatedBy', 'firstName lastName email');

    await ActivityLog.create({
      userId: req.user._id,
      companyId: lead.companyId,
      action: 'update',
      module: 'leads',
      entityType: 'Lead',
      entityId: lead._id,
      description: `Updated lead: ${lead.customer.name}`,
      changes: updates,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    }).catch(err => console.error('Activity log error:', err));

    res.status(200).json({
      success: true,
      message: 'Lead updated successfully',
      data: { lead: updatedLead }
    });
  } catch (error) {
    console.error('Update lead error:', error);

    if (error.name === 'ValidationError') {
      const messages = Object.values(error.errors).map(err => err.message);
      return next(new AppError(messages.join(', '), 400));
    }

    next(error);
  }
};

// Update lead status
const updateLeadStatus = async (req, res, next) => {
  try {
    const { id } = req.params;
    const { status } = req.body;

    if (!id.match(/^[0-9a-fA-F]{24}$/)) {
      throw new AppError('Invalid lead ID format', 400);
    }

    if (!status) {
      throw new AppError('Status is required', 400);
    }

    const validStatuses = ['new', 'contacted', 'offer', 'won', 'lost'];
    if (!validStatuses.includes(status)) {
      throw new AppError(`Invalid status. Must be one of: ${validStatuses.join(', ')}`, 400);
    }

    const lead = await Lead.findById(id);

    if (!lead) {
      throw new AppError('Lead not found', 404);
    }

    if (req.user) {
      const userRole = await Role.findById(req.user.roleId);
      const isGlobal = userRole && userRole.scope === 'global';
      const isCreator = lead.createdBy?.toString() === req.user._id.toString();
      const isAssignee = lead.assignedTo?.toString() === req.user._id.toString();

      if (!isGlobal && req.user.companyId) {
        if (lead.companyId.toString() !== req.user.companyId.toString()) {
          throw new AppError('You do not have access to this lead', 403);
        }
      }

      if (!isGlobal && !isCreator && !isAssignee) {
        throw new AppError('Only the creator or assigned user can update the status of this lead', 403);
      }
    }

    console.log(req.user);
    lead.status = status;
    await lead.save();

    await ActivityLog.create({
      userId: req.user?._id,
      companyId: lead.companyId,
      action: 'update',
      module: 'leads',
      entityType: 'Lead',
      entityId: lead._id,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    }).catch(err => console.error('Activity log error:', err));

    const updatedLead = await Lead.findById(lead._id)
      .populate('companyId', 'name code')
      .populate('assignedTo', 'firstName lastName email')
      .populate('createdBy', 'firstName lastName email')
      .populate('updatedBy', 'firstName lastName email');

    res.status(200).json({
      success: true,
      message: 'Lead status updated successfully',
      data: { lead: updatedLead }
    });
  } catch (error) {
    console.error('Update lead status error:', error);
    next(error);
  }
};

// Delete lead
const deleteLead = async (req, res, next) => {
  try {
    const { id } = req.params;

    if (!id.match(/^[0-9a-fA-F]{24}$/)) {
      throw new AppError('Invalid lead ID format', 400);
    }

    const lead = await Lead.findById(id);

    if (!lead) {
      throw new AppError('Lead not found', 404);
    }

    if (req.user) {
      const userRole = await Role.findById(req.user.roleId);
      const isGlobal = userRole && userRole.scope === 'global';
      const isCreator = lead.createdBy?.toString() === req.user._id.toString();
      const isAssignee = lead.assignedTo?.toString() === req.user._id.toString();

      if (!isGlobal && req.user.companyId) {
        if (lead.companyId.toString() !== req.user.companyId.toString()) {
          throw new AppError('You do not have access to this lead', 403);
        }
      }

      if (!isGlobal && !isCreator && !isAssignee) {
        throw new AppError('Only the creator or assigned user can delete this lead', 403);
      }
    }

    const customerName = lead.customer.name;
    const leadNumber = lead.leadNumber;

    await Lead.findByIdAndDelete(id);

    await ActivityLog.create({
      userId: req.user?._id,
      companyId: lead.companyId,
      action: 'delete',
      module: 'leads',
      entityType: 'Lead',
      entityId: lead._id,
      description: `Deleted lead: ${customerName} (${leadNumber})`,
      ipAddress: req.ip,
      userAgent: req.get('user-agent')
    }).catch(err => console.error('Activity log error:', err));

    res.status(200).json({
      success: true,
      message: 'Lead deleted successfully'
    });
  } catch (error) {
    console.error('Delete lead error:', error);
    next(error);
  }
};

// Get lead statistics
const getLeadStats = async (req, res, next) => {
  try {
    let query = {};

    // Filter by company/ownership
    if (req.user) {
      const userRole = await Role.findById(req.user.roleId);
      const isGlobal = userRole && userRole.scope === 'global';
      const isAdmin = userRole && (userRole.name === 'admin' || userRole.name === 'super_admin');

      if (!isGlobal) {
        if (req.user.companyId) {
          query.companyId = req.user.companyId;
        }

        if (!isAdmin) {
          query.$or = [
            { createdBy: req.user._id },
            { assignedTo: req.user._id }
          ];
        }
      }
    }

    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

    const [totalLeads, byStatus, byPriority, recentLeads] = await Promise.all([
      Lead.countDocuments(query),
      Lead.aggregate([
        { $match: query },
        { $group: { _id: '$status', count: { $sum: 1 } } }
      ]),
      Lead.aggregate([
        { $match: query },
        { $group: { _id: '$priority', count: { $sum: 1 } } }
      ]),
      Lead.countDocuments({ ...query, createdAt: { $gte: thirtyDaysAgo } }),
    ]);

    res.status(200).json({
      success: true,
      data: {
        total: totalLeads,
        recentLeads,
        byStatus: byStatus.reduce((acc, item) => {
          acc[item._id] = item.count;
          return acc;
        }, {}),
        byPriority: byPriority.reduce((acc, item) => {
          acc[item._id] = item.count;
          return acc;
        }, {}),
      }
    });
  } catch (error) {
    console.error('Get lead stats error:', error);
    next(error);
  }
};

// Get unseen lead count
const getUnseenCount = async (req, res, next) => {
  try {
    const userRole = await Role.findById(req.user.roleId);
    
    // ✅ 1. If channel_partner, they don't see the unseen count
    if (userRole && userRole.name === 'channel_partner') {
      return res.status(200).json({
        success: true,
        data: { count: 0 }
      });
    }

    const view = await LeadView.findOne({ userId: req.user._id });
    const lastViewedAt = view ? view.lastViewedAt : new Date(0);

    let query = {
      status: { $nin: ['won', 'lost'] }, // Only count active leads
      createdAt: { $gt: lastViewedAt },
      createdBy: { $ne: req.user._id } // ✅ 2. Except the one who created the lead
    };

    // ✅ 3. Company Access logic
    const userCompanies = (req.user.companies || []).map(c => c._id || c);
    
    // If user has specifically assigned companies, restrict to those
    // If no companies assigned, it means "all access" (as per request)
    if (userCompanies.length > 0) {
      query.companyId = { $in: userCompanies };
    }
    // If scope is not global and no companies are assigned, 
    // it will still fall through and count for all companies based on the above logic.

    const count = await Lead.countDocuments(query);

    res.status(200).json({
      success: true,
      data: { count }
    });
  } catch (error) {
    console.error('Get unseen lead count error:', error);
    next(error);
  }
};

module.exports = {
  getAllLeads,
  getLead,
  createLead,
  updateLead,
  updateLeadStatus,
  deleteLead,
  getLeadStats,
  getUnseenCount
};