/**
 * Business Rule Validation Service
 * Provides centralized business rule validation for all ERP modules
 * Following SAP Advanced, Oracle ERP, and D365 patterns
 */

const db = require('../models');
const { Op } = require('sequelize');
const { sumReleasedBatchQuantity } = require('./mrpService');

/**
 * Customer PO Business Rules
 */
const customerPOBusinessRules = {
  /**
   * Validate if Customer PO can be submitted
   * Rules:
   * - Must be in DRAFT status
   * - Must have at least one item
   * - All items must have required fields
   */
  canSubmit: async (customerPO) => {
    const errors = [];

    if (customerPO.status !== 'DRAFT') {
      errors.push('Only DRAFT Customer POs can be submitted');
    }

    if (!customerPO.items || customerPO.items.length === 0) {
      errors.push('Cannot submit Customer PO without items');
    }

    // Validate items
    if (customerPO.items) {
      customerPO.items.forEach((item, index) => {
        if (!item.material_id) {
          errors.push(`Item ${index + 1}: Material is required`);
        }
        if (!item.quantity || item.quantity <= 0) {
          errors.push(`Item ${index + 1}: Quantity must be greater than 0`);
        }
        if (!item.unit_price || item.unit_price < 0) {
          errors.push(`Item ${index + 1}: Unit price must be greater than or equal to 0`);
        }
      });
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Customer PO can be approved
   * Rules:
   * - Must be in SUBMITTED status
   */
  canApprove: async (customerPO) => {
    const errors = [];

    if (customerPO.status !== 'SUBMITTED') {
      errors.push('Only SUBMITTED Customer POs can be approved');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Customer PO can be released
   * Rules:
   * - Must be in APPROVED status
   */
  canRelease: async (customerPO) => {
    const errors = [];

    if (customerPO.status !== 'APPROVED') {
      errors.push('Only APPROVED Customer POs can be released');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Customer PO can be updated
   * Rules:
   * - Must be in DRAFT status
   */
  canUpdate: async (customerPO) => {
    const errors = [];

    if (customerPO.status !== 'DRAFT') {
      errors.push('Only DRAFT Customer POs can be updated');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Customer PO can be deleted
   * Rules:
   * - Must be in DRAFT status
   * - Must not have related Production Orders
   */
  canDelete: async (customerPO) => {
    const errors = [];

    if (customerPO.status !== 'DRAFT') {
      errors.push('Only DRAFT Customer POs can be deleted');
    }

    // Check for related Production Orders
    const productionOrders = await db.ProductionOrder.findAll({
      where: {
        customer_po_id: customerPO.id
      },
      limit: 1
    });

    if (productionOrders.length > 0) {
      errors.push('Cannot delete Customer PO with related Production Orders');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  }
};

/**
 * Production Order Business Rules
 */
const productionOrderBusinessRules = {
  /**
   * Validate if Production Order can be released
   * Rules:
   * - Must be in DRAFT status
   * - Must have BOM
   * - Must have at least one component
   */
  canRelease: async (productionOrder, options = {}) => {
    const { transaction } = options;
    const errors = [];

    if (productionOrder.status !== 'DRAFT') {
      errors.push('Only DRAFT Production Orders can be released');
    }

    if (!productionOrder.bom_id) {
      errors.push('Production Order must have a BOM');
    }

    // Check if BOM exists and is active
    if (productionOrder.bom_id) {
      const bom = await db.BOMHeader.findByPk(productionOrder.bom_id, {
        ...(transaction ? { transaction } : {})
      });
      if (!bom) {
        errors.push('BOM not found');
      } else if (bom.status !== 'ACTIVE') {
        errors.push('BOM must be active');
      }
    }

    // Check for components
    const components = await db.ProductionOrderComponent.findAll({
      where: {
        production_order_id: productionOrder.id
      },
      ...(transaction ? { transaction } : {})
    });

    if (components.length === 0) {
      errors.push('Production Order must have at least one component');
    }

    if (productionOrder.batch_planning_enabled) {
      const batches = await db.ProductionOrderBatch.findAll({
        where: { production_order_id: productionOrder.id },
        attributes: ['quantity', 'status'],
        ...(transaction ? { transaction } : {})
      });
      if (sumReleasedBatchQuantity(batches) <= 0) {
        errors.push(
          'When batch planning is enabled, enter the quantity to release in the release dialog, or release at least one batch first.'
        );
      }
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Production Order can be started (IN_PROGRESS)
   * Rules:
   * - Must be in RELEASED status
   * - Materials should be received (optional, configurable)
   */
  canStart: async (productionOrder, requireMaterials = false) => {
    const errors = [];

    if (productionOrder.status !== 'RELEASED') {
      errors.push('Only RELEASED Production Orders can be started');
    }

    if (requireMaterials) {
      // Check if materials have been received
      const materialIssues = await db.MaterialIssue.findAll({
        where: {
          production_order_id: productionOrder.id,
          status: { [Op.in]: ['RECEIVED', 'POSTED'] }
        }
      });

      if (materialIssues.length === 0) {
        errors.push('Materials must be received before starting production');
      }
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Production Order can be confirmed
   * Rules:
   * - Must be in IN_PROGRESS or PARTIALLY_CONFIRMED status
   * - Confirmed quantity cannot exceed order quantity (with tolerance)
   */
  canConfirm: async (productionOrder, confirmedQuantity, tolerance = 0.1) => {
    const errors = [];

    if (!['IN_PROGRESS', 'PARTIALLY_CONFIRMED'].includes(productionOrder.status)) {
      errors.push('Production Order must be IN_PROGRESS or PARTIALLY_CONFIRMED to confirm');
    }

    const totalConfirmed = (productionOrder.confirmed_quantity || 0) + confirmedQuantity;
    const maxAllowed = productionOrder.order_quantity * (1 + tolerance);

    if (totalConfirmed > maxAllowed) {
      errors.push(`Confirmed quantity (${totalConfirmed}) exceeds order quantity (${productionOrder.order_quantity}) with tolerance`);
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Production Order can be closed
   * Rules:
   * - Must be in CONFIRMED status
   * - All materials must be issued
   * - Finished goods must be received
   */
  canClose: async (productionOrder) => {
    const errors = [];

    if (productionOrder.status !== 'CONFIRMED') {
      errors.push('Only CONFIRMED Production Orders can be closed');
    }

    // Check if all materials are issued
    const components = await db.ProductionOrderComponent.findAll({
      where: {
        production_order_id: productionOrder.id
      }
    });

    for (const component of components) {
      if (component.issue_type !== 'BACKFLUSH') {
        const issuedQty = component.issued_quantity || 0;
        const requiredQty = component.required_quantity || 0;
        if (issuedQty < requiredQty) {
          errors.push(`Component ${component.id}: Not all materials issued (${issuedQty}/${requiredQty})`);
        }
      }
    }

    // Check if finished goods are received
    const grn = await db.InventoryGoodsReceipt.findOne({
      where: {
        production_order_id: productionOrder.id,
        status: 'POSTED'
      }
    });

    if (!grn) {
      errors.push('Finished goods must be received before closing');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Production Order can be updated
   * Rules:
   * - Must be in DRAFT status
   */
  canUpdate: async (productionOrder) => {
    const errors = [];

    if (productionOrder.status !== 'DRAFT') {
      errors.push('Only DRAFT Production Orders can be updated');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Production Order can be cancelled
   * Rules:
   * - Cannot be cancelled if CLOSED
   */
  canCancel: async (productionOrder) => {
    const errors = [];

    if (productionOrder.status === 'CLOSED') {
      errors.push('CLOSED Production Orders cannot be cancelled');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  }
};

/**
 * Material Request Business Rules
 */
const materialRequestBusinessRules = {
  /**
   * Validate if Material Request can be sent to stores
   * Rules:
   * - Must be in DRAFT status
   * - Must have at least one item
   */
  canSendToStores: async (materialRequest) => {
    const errors = [];

    if (materialRequest.status !== 'DRAFT') {
      errors.push('Only DRAFT Material Requests can be sent to stores');
    }

    const items = await db.MaterialRequestItem.findAll({
      where: {
        request_id: materialRequest.id
      }
    });

    if (items.length === 0) {
      errors.push('Material Request must have at least one item');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Material Request can be reviewed
   * Rules:
   * - Must be in SENT_TO_STORES status
   */
  canReview: async (materialRequest) => {
    const errors = [];

    if (!['SENT_TO_STORES', 'DRAFT'].includes(materialRequest.status)) {
      errors.push('Only DRAFT or SENT_TO_STORES Material Requests can be reviewed');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Material Request can be completed
   * Rules:
   * - Must be in REVIEWED, SENT_TO_PURCHASE, or PARTIALLY_ISSUED status
   * - All items must be issued or sent to purchase
   */
  canComplete: async (materialRequest) => {
    const errors = [];

    if (!['REVIEWED', 'SENT_TO_PURCHASE', 'PARTIALLY_ISSUED'].includes(materialRequest.status)) {
      errors.push('Material Request must be REVIEWED, SENT_TO_PURCHASE, or PARTIALLY_ISSUED to complete');
    }

    // Check if all items are processed
    const items = await db.MaterialRequestItem.findAll({
      where: {
        request_id: materialRequest.id
      }
    });

    for (const item of items) {
      const pendingQty = item.pending_quantity || 0;
      const shortfallQty = item.shortfall_quantity || 0;
      
      if (pendingQty > 0 && shortfallQty > 0) {
        errors.push(`Item ${item.id}: Still has pending quantity and shortfall`);
      }
    }

    return {
      valid: errors.length === 0,
      errors
    };
  }
};

/**
 * Purchase Order Business Rules
 */
const purchaseOrderBusinessRules = {
  /**
   * Validate if Purchase Order can be approved
   * Rules:
   * - Must be in SUBMITTED status (DRAFT allowed for backward compatibility)
   * - Must have at least one item
   */
  canApprove: async (purchaseOrder) => {
    const errors = [];

    if (!['SUBMITTED', 'DRAFT'].includes(purchaseOrder.status)) {
      errors.push('Only SUBMITTED or DRAFT Purchase Orders can be approved');
    }

    const items = await db.PurchaseOrderItem.findAll({
      where: {
        po_id: purchaseOrder.id
      }
    });

    if (items.length === 0) {
      errors.push('Purchase Order must have at least one item');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Purchase Order can be released
   * Rules:
   * - Must be in APPROVED status
   */
  canRelease: async (purchaseOrder) => {
    const errors = [];

    if (purchaseOrder.status !== 'APPROVED') {
      errors.push('Only APPROVED Purchase Orders can be released');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  },

  /**
   * Validate if Purchase Order can receive goods
   * Rules:
   * - Must be in RELEASED, PARTIALLY_RECEIVED status
   * - Received quantity cannot exceed ordered quantity (with tolerance)
   */
  canReceive: async (purchaseOrder, receivedQuantity, orderedQuantity, tolerance = 0.05) => {
    const errors = [];

    if (!['RELEASED', 'PARTIALLY_RECEIVED'].includes(purchaseOrder.status)) {
      errors.push('Purchase Order must be RELEASED or PARTIALLY_RECEIVED to receive goods');
    }

    const maxAllowed = orderedQuantity * (1 + tolerance);

    if (receivedQuantity > maxAllowed) {
      errors.push(`Received quantity (${receivedQuantity}) exceeds ordered quantity (${orderedQuantity}) with tolerance`);
    }

    return {
      valid: errors.length === 0,
      errors
    };
  }
};

module.exports = {
  customerPO: customerPOBusinessRules,
  productionOrder: productionOrderBusinessRules,
  materialRequest: materialRequestBusinessRules,
  purchaseOrder: purchaseOrderBusinessRules
};

