const db = require('../models');
const { sendResponse, sendError } = require('../utils/response');
const asyncHandler = require('../middlewares/asyncHandler');
const { getPagination, getPagingData } = require('../utils/pagination');
const { Op } = require('sequelize');

// Get approval masters for a module
exports.getApprovalMasters = asyncHandler(async (req, res) => {
  const { moduleName, transactionType, status, search, page, limit } = req.query;
  const companyId = req.user?.companyId;

  if (!companyId) {
    return sendError(res, 'Company ID is required', 400);
  }

  const where = {
    company_id: companyId
  };

  if (moduleName) {
    where.module_name = moduleName;
  }

  if (transactionType) {
    where.transaction_type = transactionType;
  }

  if (status) {
    where.status = status;
  }

  // Search functionality
  if (search) {
    where[Op.or] = [
      { module_name: { [Op.like]: `%${search}%` } },
      { transaction_type: { [Op.like]: `%${search}%` } }
    ];
  }

  const { limit: pageSize, offset } = getPagination(page, limit);

  const { count, rows } = await db.ApprovalMaster.findAndCountAll({
    where,
    include: [
      { model: db.Role, as: 'approverRole', attributes: ['id', 'role_name'], required: false },
      { model: db.User, as: 'approverUser', attributes: ['id', 'username', 'first_name', 'last_name'], required: false }
    ],
    order: [['sequence', 'ASC'], ['approval_level', 'ASC']],
    limit: pageSize,
    offset: offset
  });

  const response = getPagingData({ count, rows }, page, pageSize);
  sendResponse(res, 200, true, 'Approval masters retrieved successfully', response);
});

// Get approval transactions for a transaction
exports.getApprovalTransactions = asyncHandler(async (req, res) => {
  const { moduleName, transactionId } = req.query;
  const companyId = req.user?.companyId;

  if (!companyId || !moduleName || !transactionId) {
    return sendError(res, 'Company ID, Module Name, and Transaction ID are required', 400);
  }

  const approvalTransactions = await db.ApprovalTransaction.findAll({
    where: {
      company_id: companyId,
      module_name: moduleName,
      transaction_id: transactionId
    },
    include: [
      { model: db.ApprovalMaster, as: 'approvalMaster' },
      { model: db.Role, as: 'approverRole', attributes: ['id', 'role_name'] },
      { model: db.User, as: 'approver', attributes: ['id', 'username', 'first_name', 'last_name'] }
    ],
    order: [['approval_level', 'ASC'], ['created_at', 'ASC']]
  });

  sendResponse(res, 200, true, 'Approval transactions retrieved successfully', approvalTransactions);
});

// Create approval transactions when a transaction is submitted
exports.createApprovalTransactions = asyncHandler(async (req, res) => {
  const { moduleName, transactionId, transactionNumber, transactionAmount } = req.body;
  const companyId = req.user?.companyId;
  const userId = req.user?.userId || null;

  if (!companyId || !moduleName || !transactionId) {
    return sendError(res, 'Company ID, Module Name, and Transaction ID are required', 400);
  }

  // Get approval masters for this module
  const approvalMasters = await db.ApprovalMaster.findAll({
    where: {
      company_id: companyId,
      module_name: moduleName,
      status: 'ACTIVE',
      is_required: true
    },
    order: [['sequence', 'ASC'], ['approval_level', 'ASC']]
  });

  if (approvalMasters.length === 0) {
    return sendResponse(res, 200, true, 'No approval required for this transaction', []);
  }

  // Filter by amount thresholds if provided
  const applicableApprovals = approvalMasters.filter(am => {
    if (transactionAmount !== undefined && transactionAmount !== null) {
      const amount = parseFloat(transactionAmount);
      if (am.min_amount && amount < parseFloat(am.min_amount)) {
        return false;
      }
      if (am.max_amount && amount > parseFloat(am.max_amount)) {
        return false;
      }
    }
    return true;
  });

  if (applicableApprovals.length === 0) {
    return sendResponse(res, 200, true, 'No approval required for this transaction amount', []);
  }

  // Create approval transactions
  const approvalTransactions = await Promise.all(
    applicableApprovals.map((am, index) => {
      return db.ApprovalTransaction.create({
        company_id: companyId,
        approval_master_id: am.id,
        module_name: moduleName,
        transaction_id: transactionId,
        transaction_number: transactionNumber,
        approval_level: am.approval_level,
        status: index === 0 ? 'PENDING' : 'PENDING', // First level is pending, others wait
        approver_role_id: am.approver_role_id,
        approver_user_id: am.approver_user_id,
        transaction_amount: transactionAmount,
        created_by: userId,
        updated_by: userId
      });
    })
  );

  sendResponse(res, 200, true, 'Approval transactions created successfully', approvalTransactions);
});

// Approve a transaction
exports.approve = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const { comments } = req.body;
  const userId = req.user?.userId || null;
  const companyId = req.user?.companyId;

  if (!userId) {
    return sendError(res, 'User ID is required', 400);
  }

  const approvalTransaction = await db.ApprovalTransaction.findByPk(id, {
    include: [
      { model: db.ApprovalMaster, as: 'approvalMaster' },
      { model: db.User, as: 'approver', attributes: ['id', 'username'] }
    ]
  });

  if (!approvalTransaction) {
    return sendError(res, 'Approval transaction not found', 404);
  }

  if (approvalTransaction.company_id !== companyId) {
    return sendError(res, 'Unauthorized', 403);
  }

  if (approvalTransaction.status !== 'PENDING') {
    return sendError(res, `Approval transaction is already ${approvalTransaction.status}`, 400);
  }

  // Check if user has permission to approve
  const userRoles = await db.UserRole.findAll({
    where: { user_id: userId },
    include: [{ model: db.Role, as: 'role' }]
  });

  const userRoleIds = userRoles.map(ur => ur.role_id) || [];
  
  // Get approval master if not included
  const approvalMaster = approvalTransaction.approvalMaster || await db.ApprovalMaster.findByPk(approvalTransaction.approval_master_id);
  
  if (!approvalMaster) {
    return sendError(res, 'Approval master not found', 404);
  }
  
  const canApprove = 
    approvalMaster.approver_user_id === userId ||
    (approvalMaster.approver_role_id && userRoleIds.includes(approvalMaster.approver_role_id));

  if (!canApprove) {
    return sendError(res, 'You do not have permission to approve this transaction', 403);
  }

  // Update approval transaction
  await approvalTransaction.update({
    status: 'APPROVED',
    approver_user_id: userId,
    approved_at: new Date(),
    comments: comments || null,
    updated_by: userId
  });

  // Check if there are more approval levels
  const nextApproval = await db.ApprovalTransaction.findOne({
    where: {
      company_id: companyId,
      module_name: approvalTransaction.module_name,
      transaction_id: approvalTransaction.transaction_id,
      approval_level: approvalTransaction.approval_level + 1,
      status: 'PENDING'
    }
  });

  // Get transaction creator for notification
  let transactionCreatorId = null;
  try {
    const moduleName = approvalTransaction.module_name;
    const transactionId = approvalTransaction.transaction_id;
    
    // Get creator based on module
    if (moduleName === 'PurchaseOrder') {
      const po = await db.PurchaseOrder.findByPk(transactionId);
      transactionCreatorId = po?.created_by || null;
    } else if (moduleName === 'SalesOrder') {
      const so = await db.SalesOrder.findByPk(transactionId);
      transactionCreatorId = so?.created_by || null;
    } else if (moduleName === 'CustomerPO') {
      const cpo = await db.CustomerPO.findByPk(transactionId);
      transactionCreatorId = cpo?.created_by || null;
    }
  } catch (error) {
    console.error('Error getting transaction creator:', error);
  }

  // If no more approvals, update the main transaction status
  if (!nextApproval) {
    const approvalService = require('../services/approvalService');
    const allApproved = await approvalService.checkAllApprovalsComplete(
      approvalTransaction.module_name,
      approvalTransaction.transaction_id
    );
    
    if (allApproved) {
      await approvalService.updateTransactionStatus(
        approvalTransaction.module_name,
        approvalTransaction.transaction_id,
        'APPROVED',
        userId
      );
      
      // Create approval approved notification
      try {
        const notificationService = require('../services/notificationService');
        await notificationService.createApprovalApprovedNotification({
          companyId,
          moduleName: approvalTransaction.module_name,
          transactionId: approvalTransaction.transaction_id,
          transactionNumber: approvalTransaction.transaction_number,
          approvedBy: userId,
          approverUserId: userId,
          transactionCreatorId
        });
      } catch (error) {
        console.error('Error creating approval approved notification:', error);
      }
    }
  }

  const updatedApproval = await db.ApprovalTransaction.findByPk(id, {
    include: [
      { model: db.ApprovalMaster, as: 'approvalMaster' },
      { model: db.User, as: 'approver', attributes: ['id', 'username', 'first_name', 'last_name'] }
    ]
  });

  sendResponse(res, 200, true, 'Transaction approved successfully', updatedApproval);
});

// Reject a transaction
exports.reject = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const { rejectionReason } = req.body;
  const userId = req.user?.userId || null;
  const companyId = req.user?.companyId;

  if (!userId) {
    return sendError(res, 'User ID is required', 400);
  }

  if (!rejectionReason) {
    return sendError(res, 'Rejection reason is required', 400);
  }

  const approvalTransaction = await db.ApprovalTransaction.findByPk(id, {
    include: [{ model: db.ApprovalMaster, as: 'approvalMaster' }]
  });

  if (!approvalTransaction) {
    return sendError(res, 'Approval transaction not found', 404);
  }

  if (approvalTransaction.company_id !== companyId) {
    return sendError(res, 'Unauthorized', 403);
  }

  if (approvalTransaction.status !== 'PENDING') {
    return sendError(res, `Approval transaction is already ${approvalTransaction.status}`, 400);
  }

  // Check if user has permission to reject
  const userRoles = await db.UserRole.findAll({
    where: { user_id: userId },
    include: [{ model: db.Role, as: 'role' }]
  });

  const userRoleIds = userRoles.map(ur => ur.role_id) || [];
  
  // Get approval master if not included
  const approvalMaster = approvalTransaction.approvalMaster || await db.ApprovalMaster.findByPk(approvalTransaction.approval_master_id);
  
  if (!approvalMaster) {
    return sendError(res, 'Approval master not found', 404);
  }
  
  const canReject = 
    approvalMaster.approver_user_id === userId ||
    (approvalMaster.approver_role_id && userRoleIds.includes(approvalMaster.approver_role_id));

  if (!canReject) {
    return sendError(res, 'You do not have permission to reject this transaction', 403);
  }

  // Get transaction creator for notification
  let transactionCreatorId = null;
  try {
    const moduleName = approvalTransaction.module_name;
    const transactionId = approvalTransaction.transaction_id;
    
    // Get creator based on module
    if (moduleName === 'PurchaseOrder') {
      const po = await db.PurchaseOrder.findByPk(transactionId);
      transactionCreatorId = po?.created_by || null;
    } else if (moduleName === 'SalesOrder') {
      const so = await db.SalesOrder.findByPk(transactionId);
      transactionCreatorId = so?.created_by || null;
    } else if (moduleName === 'CustomerPO') {
      const cpo = await db.CustomerPO.findByPk(transactionId);
      transactionCreatorId = cpo?.created_by || null;
    }
  } catch (error) {
    console.error('Error getting transaction creator:', error);
  }

  // Update approval transaction
  await approvalTransaction.update({
    status: 'REJECTED',
    approver_user_id: userId,
    rejected_at: new Date(),
    rejection_reason: rejectionReason,
    updated_by: userId
  });

  // Reject all pending approvals for this transaction
  await db.ApprovalTransaction.update(
    {
      status: 'CANCELLED',
      updated_by: userId
    },
    {
      where: {
        company_id: companyId,
        module_name: approvalTransaction.module_name,
        transaction_id: approvalTransaction.transaction_id,
        status: 'PENDING'
      }
    }
  );

  // Update the main transaction status
  const approvalService = require('../services/approvalService');
  await approvalService.updateTransactionStatus(
    approvalTransaction.module_name,
    approvalTransaction.transaction_id,
    'REJECTED',
    userId
  );

  // Create approval rejected notification
  try {
    const notificationService = require('../services/notificationService');
    await notificationService.createApprovalRejectedNotification({
      companyId,
      moduleName: approvalTransaction.module_name,
      transactionId: approvalTransaction.transaction_id,
      transactionNumber: approvalTransaction.transaction_number,
      rejectedBy: userId,
      approverUserId: userId,
      transactionCreatorId,
      rejectionReason
    });
  } catch (error) {
    console.error('Error creating approval rejected notification:', error);
  }

  const updatedApproval = await db.ApprovalTransaction.findByPk(id, {
    include: [
      { model: db.ApprovalMaster, as: 'approvalMaster' },
      { model: db.User, as: 'approver', attributes: ['id', 'username', 'first_name', 'last_name'] }
    ]
  });

  sendResponse(res, 200, true, 'Transaction rejected successfully', updatedApproval);
});

// Get approval master by ID
exports.getApprovalMasterById = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const companyId = req.user?.companyId;

  if (!companyId) {
    return sendError(res, 'Company ID is required', 400);
  }

  const approvalMaster = await db.ApprovalMaster.findOne({
    where: {
      id,
      company_id: companyId
    },
    include: [
      { model: db.Role, as: 'approverRole', attributes: ['id', 'role_name'] },
      { model: db.User, as: 'approverUser', attributes: ['id', 'username', 'first_name', 'last_name'] }
    ]
  });

  if (!approvalMaster) {
    return sendError(res, 'Approval master not found', 404);
  }

  sendResponse(res, 200, true, 'Approval master retrieved successfully', approvalMaster);
});

// Create approval master
exports.createApprovalMaster = asyncHandler(async (req, res) => {
  const companyId = req.user?.companyId;
  const userId = req.user?.userId || null;

  if (!companyId) {
    return sendError(res, 'Company ID is required', 400);
  }

  const {
    module_name,
    transaction_type,
    approval_level,
    approver_role_id,
    approver_user_id,
    min_amount,
    max_amount,
    is_required = true,
    sequence = 1,
    status = 'ACTIVE',
    effective_from,
    effective_to
  } = req.body;

  if (!module_name || !approval_level) {
    return sendError(res, 'Module name and approval level are required', 400);
  }

  if (!approver_role_id && !approver_user_id) {
    return sendError(res, 'Either approver role or approver user must be specified', 400);
  }

  const approvalMaster = await db.ApprovalMaster.create({
    company_id: companyId,
    module_name,
    transaction_type: transaction_type || null,
    approval_level,
    approver_role_id: approver_role_id || null,
    approver_user_id: approver_user_id || null,
    min_amount: min_amount || 0,
    max_amount: max_amount || null,
    is_required,
    sequence,
    status,
    effective_from: effective_from || null,
    effective_to: effective_to || null,
    created_by: userId,
    updated_by: userId
  });

  const created = await db.ApprovalMaster.findByPk(approvalMaster.id, {
    include: [
      { model: db.Role, as: 'approverRole', attributes: ['id', 'role_name'] },
      { model: db.User, as: 'approverUser', attributes: ['id', 'username', 'first_name', 'last_name'] }
    ]
  });

  sendResponse(res, 201, true, 'Approval master created successfully', created);
});

// Update approval master
exports.updateApprovalMaster = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const companyId = req.user?.companyId;
  const userId = req.user?.userId || null;

  if (!companyId) {
    return sendError(res, 'Company ID is required', 400);
  }

  const approvalMaster = await db.ApprovalMaster.findOne({
    where: {
      id,
      company_id: companyId
    }
  });

  if (!approvalMaster) {
    return sendError(res, 'Approval master not found', 404);
  }

  const {
    module_name,
    transaction_type,
    approval_level,
    approver_role_id,
    approver_user_id,
    min_amount,
    max_amount,
    is_required,
    sequence,
    status,
    effective_from,
    effective_to
  } = req.body;

  if (module_name !== undefined) approvalMaster.module_name = module_name;
  if (transaction_type !== undefined) approvalMaster.transaction_type = transaction_type;
  if (approval_level !== undefined) approvalMaster.approval_level = approval_level;
  if (approver_role_id !== undefined) approvalMaster.approver_role_id = approver_role_id;
  if (approver_user_id !== undefined) approvalMaster.approver_user_id = approver_user_id;
  if (min_amount !== undefined) approvalMaster.min_amount = min_amount;
  if (max_amount !== undefined) approvalMaster.max_amount = max_amount;
  if (is_required !== undefined) approvalMaster.is_required = is_required;
  if (sequence !== undefined) approvalMaster.sequence = sequence;
  if (status !== undefined) approvalMaster.status = status;
  if (effective_from !== undefined) approvalMaster.effective_from = effective_from;
  if (effective_to !== undefined) approvalMaster.effective_to = effective_to;
  approvalMaster.updated_by = userId;

  await approvalMaster.save();

  const updated = await db.ApprovalMaster.findByPk(id, {
    include: [
      { model: db.Role, as: 'approverRole', attributes: ['id', 'role_name'] },
      { model: db.User, as: 'approverUser', attributes: ['id', 'username', 'first_name', 'last_name'] }
    ]
  });

  sendResponse(res, 200, true, 'Approval master updated successfully', updated);
});

// Delete approval master
exports.deleteApprovalMaster = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const companyId = req.user?.companyId;

  if (!companyId) {
    return sendError(res, 'Company ID is required', 400);
  }

  const approvalMaster = await db.ApprovalMaster.findOne({
    where: {
      id,
      company_id: companyId
    }
  });

  if (!approvalMaster) {
    return sendError(res, 'Approval master not found', 404);
  }

  // Check if there are any approval transactions using this master
  const transactionCount = await db.ApprovalTransaction.count({
    where: {
      approval_master_id: id
    }
  });

  if (transactionCount > 0) {
    return sendError(res, 'Cannot delete approval master that has associated transactions', 400);
  }

  await approvalMaster.destroy();

  sendResponse(res, 200, true, 'Approval master deleted successfully', null);
});


