const { empty } = require("../Helpers/Common");
const Product = require("../models/product");
const Recipe = require("../models/recipe");
const ProjectTask = require("../models/projectTask");
const Admin = require("../models/admin");
const moment = require("moment");
const crypto = require("crypto");
const { timeStamp, log } = require("console");
const JobCost = require("../models/jobCost");
const Projects = require("../models/Projects");
const Products = require("../models/product");
const CustomerOrderProduct=require('../models/customerOrderProduct')
const Request=require('../models/request')
const RequestUpdates = require('../models/requestUpdates')
const SplitManagementModel = require('../models/SplitManagementModel');
const { filteredSignwiseInstalations } = require("../Helpers/SplitManagementHelper");
const { FACILITY_ID } = require("../config/SplitManagementDb");
const { default: axios } = require("axios");
const Onboard = require("../models/onboard");
const OnboardWorkflowStages = require("../models/onboardWorkFlowStages");
const OnboardWorkflowFields = require("../models/onboardWorkflowFields");
// const UNITS = [
//   { id: 1, name: 'Square feet', short_name: 'Sq ft', unit_id: 1 },
//   { id: 2, name: 'Linear feet', short_name: 'Lin ft', unit_id: 1 },
//   { id: 3, name: 'Square inches', short_name: 'Sq in', unit_id: 1 },
//   { id: 4, name: 'Unit', short_name: 'Unit', unit_id: 2 },
//   { id: 5, name: 'Gallons', short_name: 'Gal', unit_id: 3 },
//   { id: 6, name: 'Quarts', short_name: 'Qt', unit_id: 3 },
//   { id: 7, name: 'Milliliter', short_name: 'ml', unit_id: 3 },
//   { id: 8, name: 'Ounce', short_name: 'oz', unit_id: 3 },
//   { id: 9, name: 'Kilogram', short_name: 'kg', unit_id: 4 },
//   { id: 10, name: 'Gram', short_name: 'g', unit_id: 4 },
//   { id: 11, name: 'Milligram', short_name: 'mg', unit_id: 4 },
// ];

const UNITS = [
  { id: 1, name: 'Square feet', short_name: 'Sq ft', unit_id: 1 },
  { id: 2, name: 'Linear feet', short_name: 'Lin ft', unit_id: 1 },
  { id: 3, name: 'Square inches', short_name: 'Sq in', unit_id: 1 },
  { id: 4, name: 'Unit', short_name: 'Unit', unit_id: 2 },
  { id: 5, name: 'Gallons', short_name: 'Gal', unit_id: 3 },
  { id: 6, name: 'Quarts', short_name: 'Qt', unit_id: 3 },
  { id: 7, name: 'Milliliter', short_name: 'ml', unit_id: 3 },
  { id: 8, name: 'Liter', short_name: 'L', unit_id: 3 },
  { id: 9, name: 'Pint', short_name: 'pt', unit_id: 3 },
  { id: 10, name: 'Half Pint', short_name: 'half-pt', unit_id: 3 },
  { id: 11, name: 'Ounce', short_name: 'oz', unit_id: 3 },
  { id: 12, name: 'Kilogram', short_name: 'kg', unit_id: 4 },
  { id: 13, name: 'Gram', short_name: 'g', unit_id: 4 },
  { id: 14, name: 'Milligram', short_name: 'mg', unit_id: 4 },
  { id: 15, name: 'Each', short_name: 'EA', unit_id: 2 },

];

// ✅ Function to Get Dynamic Conversion Factor Based on Units & Width
const getConversionFactor = (toUnitId, fromUnitId, width = 1) => {
  if (!toUnitId) {
    toUnitId = fromUnitId;
  }
  if (!fromUnitId) {
    fromUnitId = toUnitId;
  }
  const conversionFactors = {
      1: { 1: 1, 2: width, 3: 1 / 144 }, // Square Feet → (itself, Linear Feet by width, Square Inches)
      2: { 1: 1 / width, 2: 1, 3: 1 / (width * 144) }, // Linear Feet → (Square Feet by width, itself, Square Inches)
      3: { 1: 144, 2: width * 144, 3: 1 }, // Square Inches → (Square Feet, Linear Feet, itself)
      4: { 4: 1 }, // Unit → itself (for standalone items)
      5: { 5: 1, 6: 1 / 4, 7: 1 / 3785.41, 8: 1 / 128 }, // Gallons → (itself, Quarts, Milliliters, Ounces)
      6: { 5: 4, 6: 1, 7: 1 / 946.35, 8: 1 / 32 }, // Quarts → (Gallons, itself, Milliliters, Ounces)
      7: { 5: 3785.41, 6: 946.35, 7: 1, 8: 29.5735 }, // Milliliters → (Gallons, Quarts, itself, Ounces)
      8: { 5: 128, 6: 32, 7: 1 / 29.5735, 8: 1 }, // Ounces → (Gallons, Quarts, Milliliters, itself)
      9: { 9: 1, 10: 1 / 1000, 11: 1 / 1000000 }, // Kilograms → (itself, Grams, Milligrams)
      10: { 9: 1000, 10: 1, 11: 1 / 1000 }, // Grams → (Kilograms, itself, Milligrams)
      11: { 9: 1000000, 10: 1000, 11: 1 }, // Milligrams → (Kilograms, Grams, itself)
  };

  return conversionFactors[fromUnitId]?.[toUnitId] || 1; // Default to 1 if invalid
};

// ✅ Function to Convert Unit Cost Based on Unit Type
const getConvertedUnitCost = (originalCost, fromUnitId, toUnitId, width = 1) => {
  const conversionFactor = getConversionFactor(fromUnitId, toUnitId, width);
  if (conversionFactor === 0) {
      console.error("Conversion factor is zero, invalid conversion.");
      return originalCost; // Default to original cost if invalid
  }
  return originalCost / conversionFactor; // Adjust cost based on conversion
};

const getAllRecipe = async (req, res) => {
  try {
    let recipes = await Recipe.getAllReceipes();
    const otherInstallationSplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations();
    const otherInstallationSplitInstallLabour = await SplitManagementModel.getAllInstallLabourFromOtherInstallations();
    const currentFacilitySplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettings();

    const stripTimestampIfSplit = (rec) => {
      if (!rec) return rec;
      const name = rec.name || '';
      if (rec.isSplitEdited && /-\d{14}$/.test(name)) {
        return { ...rec, name: name.replace(/-\d{14}$/, '') };
      }
      return rec;
    };
    recipes = recipes?.map(stripTimestampIfSplit) || [];
    const otherInstallationSplitManagementSettingsSanitized = otherInstallationSplitManagementSettings;
    const otherInstallationSplitInstallLabourSanitized = otherInstallationSplitInstallLabour;
    const currentFacilitySplitManagementSettingsSanitized = currentFacilitySplitManagementSettings;
    if (recipes) {
      res.send({
        status: 1,
        message: 'Product Formulas fetched Successfully',
        recipes,
        otherInstallationSplitManagementSettings: otherInstallationSplitManagementSettingsSanitized,
        otherInstallationSplitInstallLabour: otherInstallationSplitInstallLabourSanitized,
        currentFacilitySplitManagementSettings: currentFacilitySplitManagementSettingsSanitized
      })
    }
    else {
      res.send({ status: 0, message: 'Error fetching Product Formulas' })
    }
  }
  catch (error) {
    console.log("Product Formula fetch error - ", error);
    res.send({ status: 500, message: 'Error', error: error })
  }
}

const addRecipe = async (req, res) => {
  try {
    let { recipeName, values, services, userId, expectedCost, expectedProductCost, expectedServiceCost, expectedTotalServiceCost,expectedProductCostWithMarkup,expectedServiceCostWithMarkup,expectedCostWithMarkup,type ,expectedInstallLaborCost,expectedInstallLaborCostWithMarkup,nameOrDescription,formulaView,isSplitEdited,selectedWorkflow } = req.body;

    // Normalize recipe name
    recipeName = (recipeName || '').trim();
    if (!recipeName) {
      return res.send({ status: 0, message: 'Product Formula name is required.' });
    }

    let currentTime = moment().unix();

    const autoWorkflowRequired = !selectedWorkflow && services.length > 0;
    const originalRecipeName = recipeName;
    if (autoWorkflowRequired) {
      recipeName = `${originalRecipeName}-${currentTime}`;
    }

    // Check for existing recipe with same name (case-insensitive)
    const existingRecipes = await Recipe.getAllReceipes();
    const nameExists = existingRecipes.some(
      (r) => r.name && r.name.toLowerCase() === recipeName.toLowerCase()
    );

    // For split-edited flow, if the name collides, append a timestamp to make it unique
    if (nameExists) {
      if (isSplitEdited) {
        recipeName = `${recipeName}-${moment().format('YYYYMMDDHHmmss')}`;
      } else {
        return res.send({
          status: 0,
          message: `Product Formula "${recipeName}" already exists.`,
        });
      }
    }
    // Persist the UI toggle (1 = name, 2 = description)
    const nameOrDescriptionValue = (() => {
      const raw = nameOrDescription ?? formulaView;
      if (raw === 'description' || Number(raw) === 2) return 2;
      return 1; // default to name
    })();

    let data = {
      name: recipeName,
      expected_cost: expectedCost,
      expected_material_cost: expectedProductCost,
      expected_service_cost: expectedServiceCost,
      expected_total_service_cost: expectedTotalServiceCost,
      added_by: userId,
      expected_material_cost_with_markup:expectedProductCostWithMarkup,
      expected_service_cost_with_markup:expectedServiceCostWithMarkup,
      expected_cost_with_markup:expectedCostWithMarkup,
      // total_retail_markup: totalRetailMarkUp,
      timestamp: currentTime,
      type: type || 1,
      name_or_description: nameOrDescriptionValue,
      expected_install_labor_cost:Number(expectedInstallLaborCost),
      expected_install_labor_cost_with_markup:Number(expectedInstallLaborCostWithMarkup),
      isNew: 1,
      isSplitEdited: isSplitEdited ? 1 : 0,
      workflow_id: selectedWorkflow ? selectedWorkflow.id : null,
    };

    let isWorfkflowAutoGenerated = 0;
    if(!selectedWorkflow &&  services.length > 0){
      // Add workflow with timestamp to ensure unique name
      const autoWorkflowName = recipeName;
      let workflow = {
        name: autoWorkflowName,
        is_default: 0,
        timestamp: currentTime,
        updated_on: currentTime,
        added_by: userId,
        status: 4,
        is_auto_generated: 1,
      };
      let workflowId = await Onboard.addWorkFlow(workflow);
      data.workflow_id = workflowId.id;
      isWorfkflowAutoGenerated = 1;
    }

    let recipeId = await Recipe.CreateRecipe(data);

    if (recipeId) {
      for (let i = 0; i < values.length; i++) {
        let ingredients = {
          recipe_id: recipeId,
          product_id: values[i].productId,
          added_by: userId,
          quantity: values[i].quantity,
          status: 1,
          timestamp: currentTime,
          stockType: values[i]?.stockType || 1,
          width: values[i]?.stockType == 1 ? null : values[i]?.width ? parseFloat(values[i].width).toFixed(4) : null,
          convertionUnit: values[i]?.stockType == 1 ? null : values[i]?.convertionUnit ? values[i].convertionUnit : null,
          convertionQty: values[i]?.stockType == 1 ? null : values[i]?.convertionQty? parseFloat(values[i].convertionQty).toFixed(4) : null,
        };
        await Recipe.CreateIngredients(ingredients);
        let ingredientsHistory = {
          recipe_id: recipeId,
          name : values[i].ing_name,
          material_id	: values[i].productId,
          added_by: userId,
          old_price: values[i].unitPrice,
          new_price: values[i].unitPrice,
          new_qty: values[i].quantity,
          timestamp: currentTime
        };
        await Recipe.CreateRecipeHistory(ingredientsHistory);
      }

      for (let i = 0; i < services.length; i++) {
        let service = {
          recipe_id: recipeId,
          service_id: services[i].serviceId,
          added_by: userId,
          quantity: services[i].quantity,
          facility_id: (type === 3 && services[i].serviceId === 10) ? services[i].facility_id : null,
          status: 1,
          timestamp: currentTime
        };
        await Recipe.CreateServicesRecipe(service);
        let ingredientsHistory = {
          recipe_id: recipeId,
          name : services[i].ing_name,
          service_id	: services[i].serviceId,
          added_by: userId,
          old_price: services[i].unitPrice,
          new_price: services[i].unitPrice,
          new_qty: services[i].quantity,
          timestamp: currentTime
        };
        await Recipe.CreateRecipeHistory(ingredientsHistory);

        if(!selectedWorkflow &&  services.length > 0){
        //add stages
        let skillsIdFromService = await Product.getSkillsIdFromService(services[i].serviceId);

        let StageData={
          workflow_id: data.workflow_id,
          name: services[i].ing_name,
          stage_type: 1,
          flow_type: 1,
          internal_stage: 0,
          message: services[i].ing_name,
          partner_type: 0,
          group_ids: '',
          skill_ids: skillsIdFromService.join(','),
          service_id: services[i].serviceId,
          position_order: i + 1,
          timestamp: currentTime,
          updated_on: currentTime,
          added_by: userId,
          status: 1,
        }
        let stageId = await OnboardWorkflowStages.addStage(StageData);

        //add onboard workflow stage fields
        let onboardWorkflowStageFieldIds = await Product.getOnboardWorkflowFieldTemplateIdFromService(services[i].serviceId);
        for (let j = 0; j < onboardWorkflowStageFieldIds.length; j++) {
          let onboardWorkflowStageField = {
            stage_id: stageId.id,
            field_template_id: onboardWorkflowStageFieldIds[j],
            timestamp: currentTime,
            updated_on: currentTime,
            added_by: userId,
            status: 1,
          }
          let onboardWorkflowStageFieldId = await OnboardWorkflowFields.addOnboardworkFlowFields(onboardWorkflowStageField);
        }
      }
      }

      // Notification
      let notificationMsg = `The product formula "${recipeName}" has been added.`;
      let notificationUrl = "recipe";

      // Alert to user of added recipe
      await Admin.incrementField(userId, "noti_count", 1);
      let notificationData_user = {
        user_id: userId,
        url: notificationUrl,
        timestamp: currentTime,
        message: notificationMsg,
        status: 1,
        from_id: userId
      };
      await Admin.addNotification(notificationData_user);

      // Alert to admins of added recipe
      let adminIds = await Admin.getAllAdminIds();
      for (const adminId of adminIds) {
        if (adminId !== userId) {
          await Admin.incrementField(adminId, "noti_count", 1);
          let notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentTime,
            message: notificationMsg,
            status: 1,
            from_id: userId
          };
          await Admin.addNotification(notificationData);
        }
      }
      let recipes = await Recipe.getAllReceipes();
      let recipe = recipes.find(recipe => recipe.recipe_id === recipeId);

      // Strip timestamp suffix for display (keep stored name intact)
      const cleanRecipe = recipe?.isSplitEdited && /-\d{14}$/.test(recipe?.name || '')
        ? { ...recipe, name: (recipe.name || '').replace(/-\d{14}$/, '') }
        : recipe;
      const cleanRecipes = (recipes || []).map((r) =>
        r?.isSplitEdited && /-\d{14}$/.test(r?.name || '') ? { ...r, name: (r.name || '').replace(/-\d{14}$/, '') } : r
      );

      res.send({ status: 1, message: 'Product Formula Added Successfully',recipe: cleanRecipe,recipes: cleanRecipes,isWorfkflowAutoGenerated });
    } else {
      res.send({ status: 0, message: 'Error Adding Product Formula' });
    }
  } catch (error) {
    console.error('Error: ', error);
    res.send({ status: 0, message: 'An error occurred while adding the product formula.' });
  }
};

// const addRecipe = async (req, res) => {
//   let { recipeName, values, services, userId, expectedCost, expectedProductCost, expectedServiceCost, expectedTotalServiceCost } = req.body
//   let data = {
//     name: recipeName,
//     expected_cost: expectedCost,
//     expected_material_cost: expectedProductCost,
//     expected_service_cost: expectedServiceCost,
//     expected_total_service_cost: expectedTotalServiceCost,
//     added_by: userId,
//     timestamp: moment().unix()
//   }
//   console.log("$$$$$$$$$$$Recipe", data);
//   let recipeId = await Recipe.CreateRecipe(data)
//   for (let i = 0; i < values.length; i++) {
//     let ingredients = {
//       recipe_id: recipeId,
//       product_id: values[i].productId,
//       added_by: userId,
//       quantity: values[i].quantity,
//       status: 1,
//       timestamp: moment().unix()
//     }
//     await Recipe.CreateIngredients(ingredients)
//   }
//   for (let i = 0; i < services.length; i++) {
//     let service = {
//       recipe_id: recipeId,
//       service_id: services[i].serviceId,
//       added_by: userId,
//       quantity: services[i].quantity,
//       status: 1,
//       timestamp: moment().unix()
//     }
//     await Recipe.CreateServicesRecipe(service)
//   }
//   if (recipeId) {
//     res.send({ status: 1, message: 'Product Formula Added Successfully' })
//   }
//   else {
//     res.send({ status: 0, message: 'Error Adding Product Formula' })
//   }
// }

const duplicateRecipe = async (req, res) => {
  try {
    let { id, userId } = req.body;
    let selectedMaterials = await Recipe.getRecipeById(id);
    let currentTime = moment().unix();

    let data = {
      name: selectedMaterials[0].name + currentTime,
      expected_cost: selectedMaterials[0].expected_cost,
      expected_material_cost: selectedMaterials[0].expected_material_cost,
      expected_service_cost: selectedMaterials[0].expected_service_cost,
      expected_total_service_cost: selectedMaterials[0].expected_total_service_cost,
      expected_material_cost_with_markup:selectedMaterials[0].expected_material_cost_with_markup,
      expected_service_cost_with_markup:selectedMaterials[0].expected_service_cost_with_markup,
      expected_cost_with_markup:selectedMaterials[0].expected_cost_with_markup,
      added_by: selectedMaterials[0].added_by,
      timestamp: currentTime,
      type:selectedMaterials[0].type || 1,
      workflow_id: selectedMaterials[0]?.workflow_id || null,
    };

    console.log("$$$$$$$$$$$Recipe selectedMaterials", selectedMaterials);
    console.log("$$$$$$$$$$$Recipe selectedMaterials ingredients", selectedMaterials[0].ingredients);
    console.log("$$$$$$$$$$$Recipe selectedMaterials services", selectedMaterials[0].services);

    let recipeId = await Recipe.CreateRecipe(data);

    if (recipeId) {
      let values = selectedMaterials[0].ingredients;
      for (let i = 0; i < values.length; i++) {
        let ingredients = {
          recipe_id: recipeId,
          product_id: values[i].product_id,
          added_by: values[i].added_by,
          quantity: values[i].quantity,
          status: 1,
          timestamp: currentTime,
          stockType: values[i]?.stockType || 1,
          width: values[i]?.stockType == 1 ? null : values[i]?.width ? parseFloat(values[i].width).toFixed(4) : null,
          convertionUnit: values[i]?.stockType == 1 ? null : values[i]?.convertionUnit ? values[i].convertionUnit : null,
          convertionQty: values[i]?.stockType == 1 ? null : values[i]?.convertionQty? parseFloat(values[i].convertionQty).toFixed(4) : null,
        };
        await Recipe.CreateIngredients(ingredients);
        let ingredientsHistory = {
          recipe_id: recipeId,
          name : values[i].ing_name,
          material_id	: values[i].product_id,
          added_by:  userId,
          old_price: values[i].materialCost,
          new_price: values[i].materialCost,
          new_qty: values[i].quantity,
          timestamp: currentTime
        };
        await Recipe.CreateRecipeHistory(ingredientsHistory);
      }

      let services = selectedMaterials[0].services;
      for (let i = 0; i < services.length; i++) {
        let service = {
          recipe_id: recipeId,
          service_id: services[i].service_id,
          added_by: services[i].added_by,
          quantity: services[i].quantity,
          status: 1,
          timestamp: currentTime
        };
        await Recipe.CreateServicesRecipe(service);
        let ingredientsHistory = {
          recipe_id: recipeId,
          name : services[i].ing_name,
          service_id	: services[i].service_id,
          added_by: userId,
          old_price: services[i].serviceValue,
          new_price: services[i].serviceValue,
          new_qty: services[i].quantity,
          timestamp: currentTime
        };
        await Recipe.CreateRecipeHistory(ingredientsHistory);
      }

      // Notification
      let notificationMsg = `The product formula "${selectedMaterials[0].name}" has been duplicated.`;
      let notificationUrl = "recipe";

      // Alert to user of duplicated recipe
      await Admin.incrementField(selectedMaterials[0].added_by, "noti_count", 1);
      let notificationData_user = {
        user_id: selectedMaterials[0].added_by,
        url: notificationUrl,
        timestamp: currentTime,
        message: notificationMsg,
        status: 1,
        from_id: selectedMaterials[0].added_by
      };
      await Admin.addNotification(notificationData_user);

      // Alert to admins of duplicated recipe
      let adminIds = await Admin.getAllAdminIds();
      for (const adminId of adminIds) {
        if (adminId !== selectedMaterials[0].added_by) {
          await Admin.incrementField(adminId, "noti_count", 1);
          let notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentTime,
            message: notificationMsg,
            status: 1,
            from_id: selectedMaterials[0].added_by
          };
          await Admin.addNotification(notificationData);
        }
      }

      res.send({ status: 1, message: `Product Formula ${selectedMaterials[0].name} has been duplicated` });
    } else {
      res.send({ status: 0, message: 'Failed to duplicate Product Formula' });
    }
  } catch (error) {
    console.error('Error: ', error);
    res.send({ status: 0, message: 'An error occurred while duplicating the product formula.' });
  }
};

// Save Recipe as Copy specifically for AddProducts Edit Formula flow
// - Appends "-copy" to the name instead of a timestamp
// - Returns the full recipes list and the newly created recipe record
const saveRecipeAsCopy = async (req, res) => {
  try {
    const { id, userId, name } = req.body;

    if (!id) {
      return res.send({ status: 0, message: 'Recipe id is required' });
    }

    const selectedMaterials = await Recipe.getRecipeById(id);
    if (!selectedMaterials || !selectedMaterials[0]) {
      return res.send({ status: 0, message: 'Recipe not found' });
    }

    const currentTime = moment().unix();
    const baseName = selectedMaterials[0].name || 'Recipe';

    const existingRecipes = await Recipe.getAllReceipes();

    let copyName;
    if (name && name.trim()) {
      // If a custom name is provided, ensure it does not already exist
      const trimmedName = name.trim();
      const nameExists = existingRecipes.some(
        (r) => r.name && r.name.toLowerCase() === trimmedName.toLowerCase()
      );

      if (nameExists) {
        return res.send({
          status: 0,
          message: `Product Formula "${trimmedName}" already exists.`,
        });
      }

      copyName = trimmedName;
    } else {
      // Auto-generate a unique "-copy", "-copy(2)", "-copy(3)", ... name
      // Normalize base to strip any existing "-copy" or "-copy(n)" so we never get "-copy-copy"
      let rootBase = baseName;
      const copyNamePattern = /(.*?)(?:-copy(?:\(\d+\))?)$/i;
      const baseMatch = baseName.match(copyNamePattern);
      if (baseMatch && baseMatch[1]) {
        rootBase = baseMatch[1];
      }

      const baseCopyName = `${rootBase}-copy`;

      const regex = new RegExp(`^${baseCopyName}(?:\\((\\d+)\\))?$`);
      let maxSuffix = 0;

      existingRecipes.forEach((r) => {
        if (!r.name) return;
        const match = r.name.match(regex);
        if (match) {
          const num = match[1] ? parseInt(match[1], 10) : 1;
          if (!Number.isNaN(num) && num > maxSuffix) {
            maxSuffix = num;
          }
        }
      });

      if (maxSuffix === 0) {
        copyName = baseCopyName; // first copy: "basename-copy"
      } else {
        copyName = `${baseCopyName}(${maxSuffix + 1})`; // next copies: "basename-copy(2)", "basename-copy(3)", ...
      }
    }

    const data = {
      name: copyName,
      expected_cost: selectedMaterials[0].expected_cost,
      expected_material_cost: selectedMaterials[0].expected_material_cost,
      expected_service_cost: selectedMaterials[0].expected_service_cost,
      expected_total_service_cost: selectedMaterials[0].expected_total_service_cost,
      expected_material_cost_with_markup: selectedMaterials[0].expected_material_cost_with_markup,
      expected_service_cost_with_markup: selectedMaterials[0].expected_service_cost_with_markup,
      expected_cost_with_markup: selectedMaterials[0].expected_cost_with_markup,
      expected_install_labor_cost: selectedMaterials[0].expected_install_labor_cost,
      expected_install_labor_cost_with_markup: selectedMaterials[0].expected_install_labor_cost_with_markup,
      added_by: selectedMaterials[0].added_by,
      timestamp: currentTime,
      type: selectedMaterials[0].type || 1,
      workflow_id: selectedMaterials[0]?.workflow_id || null,
    };

    const recipeId = await Recipe.CreateRecipe(data);

    if (!recipeId) {
      return res.send({ status: 0, message: 'Failed to save Product Formula copy' });
    }

    const values = selectedMaterials[0].ingredients || [];
    for (let i = 0; i < values.length; i++) {
      const ingredients = {
        recipe_id: recipeId,
        product_id: values[i].product_id,
        added_by: values[i].added_by,
        quantity: values[i].quantity,
        status: 1,
        timestamp: currentTime,
        stockType: values[i]?.stockType || 1,
        width: values[i]?.stockType == 1 ? null : values[i]?.width ? parseFloat(values[i].width).toFixed(4) : null,
        convertionUnit: values[i]?.stockType == 1 ? null : values[i]?.convertionUnit ? values[i].convertionUnit : null,
        convertionQty: values[i]?.stockType == 1 ? null : values[i]?.convertionQty ? parseFloat(values[i].convertionQty).toFixed(4) : null,
      };
      await Recipe.CreateIngredients(ingredients);

      const ingredientsHistory = {
        recipe_id: recipeId,
        name: values[i].ing_name,
        material_id: values[i].product_id,
        added_by: userId,
        old_price: values[i].materialCost,
        new_price: values[i].materialCost,
        new_qty: values[i].quantity,
        timestamp: currentTime,
      };
      await Recipe.CreateRecipeHistory(ingredientsHistory);
    }

    const services = selectedMaterials[0].services || [];
    for (let i = 0; i < services.length; i++) {
      const service = {
        recipe_id: recipeId,
        service_id: services[i].service_id,
        added_by: services[i].added_by,
        quantity: services[i].quantity,
        status: 1,
        timestamp: currentTime,
      };
      await Recipe.CreateServicesRecipe(service);

      const ingredientsHistory = {
        recipe_id: recipeId,
        name: services[i].ing_name,
        service_id: services[i].service_id,
        added_by: userId,
        old_price: services[i].serviceValue,
        new_price: services[i].serviceValue,
        new_qty: services[i].quantity,
        timestamp: currentTime,
      };
      await Recipe.CreateRecipeHistory(ingredientsHistory);
    }

    // Fetch updated recipes and locate the new one
    const recipes = await Recipe.getAllReceipes();
    const recipe = recipes.find((r) => r.recipe_id === recipeId);

    return res.send({
      status: 1,
      message: `Product Formula "${baseName}" has been saved as copy "${copyName}".`,
      recipe,
      recipes,
    });
  } catch (error) {
    console.error('Error (saveRecipeAsCopy): ', error);
    return res.send({
      status: 0,
      message: 'An error occurred while saving the product formula as copy.',
    });
  }
};

// const duplicateRecipe = async (req, res) => {
//   let { id } = req.body
//   selectedMaterials = await Recipe.getRecipeById(id)
//   let currentTime = moment().unix()
//   let data = {
//     name: selectedMaterials[0].name + currentTime,
//     expected_cost: selectedMaterials[0].expected_cost,
//     expected_material_cost: selectedMaterials[0].expected_material_cost,
//     expected_service_cost: selectedMaterials[0].expected_service_cost,
//     expected_total_service_cost: selectedMaterials[0].expected_total_service_cost,
//     added_by: selectedMaterials[0].added_by,
//     timestamp: moment().unix()
//   }
//   console.log("$$$$$$$$$$$Recipe", data);
//   let recipeId = await Recipe.CreateRecipe(data)
//   let values = selectedMaterials[0].ingredients
//   for (let i = 0; i < values.length; i++) {
//     let ingredients = {
//       recipe_id: recipeId,
//       product_id: values[i].product_id,
//       added_by: values[i].added_by,
//       quantity: values[i].quantity,
//       status: 1,
//       timestamp: moment().unix()
//     }
//     await Recipe.CreateIngredients(ingredients)
//   }
//   let services = selectedMaterials[0].services
//   for (let i = 0; i < services.length; i++) {
//     let service = {
//       recipe_id: recipeId,
//       service_id: services[i].service_id,
//       added_by: services[i].added_by,
//       quantity: services[i].quantity,
//       status: 1,
//       timestamp: moment().unix()
//     }
//     await Recipe.CreateServicesRecipe(service)
//   }
//   if (recipeId) {
//     res.send({ status: 1, message: `Product Formula ${selectedMaterials[0].name} has been duplicated` })
//   }
//   else {
//     res.send({ status: 0, message: 'Failed to Product Formula' })
//   }
// }

const getAllMeterialRecipe = async (request, reply) => {
  try {
    let { params } = request.body
    let results = await Product.getAllMaterials(true);
    let dataset = await Product.getDataSet()
    let selectedMaterials = []
    if (params) {
      selectedMaterials = await Recipe.getRecipeById(params)
    }

    if (!empty(results)) {
      reply.send({ status: 1, dataset, results, selectedMaterials, message: "Data fetched successfully" });
    } else {
      reply.send({ status: 0, results, message: "Error fetching data" });
    }
  } catch (error) {
    console.log("Server error", error);
    reply.status(500).send("Server error" + error);
  }
};

const getAllServiceRecipe = async (request, reply) => {
  try {
    let { params } = request.body
    let results = await Product.getAllLabourCostServices();
    let selectedMaterials = []
    if (params) {
      selectedMaterials = await Recipe.getRecipeById(params)
    }

    // Get other installation split install labour data for split formulas
    const otherInstallationSplitInstallLabour = await SplitManagementModel.getAllInstallLabourFromOtherInstallations();

    if (!empty(results)) {
      reply.send({ 
        status: 1, 
        results, 
        selectedMaterials, 
        otherInstallationSplitInstallLabour,
        message: "Data fetched successfully" 
      });
    } else {
      reply.send({ status: 0, results, message: "Error fetching data" });
    }
  } catch (error) {
    console.log("Server error", error);
    reply.status(500).send("Server error" + error);
  }
};

const updateRecipe = async (req, res) => {
  try {
    let { recipeName, values, services, userId, params, expectedCost, expectedProductCost, expectedServiceCost, expectedTotalServiceCost,expectedProductCostWithMarkup,expectedServiceCostWithMarkup,expectedCostWithMarkup,expectedInstallLaborCost,expectedInstallLaborCostWithMarkup,isSplit,nameOrDescription,formulaView,isSplitEdited,selectedWorkflow,previosWorkflowId } = req.body;
    console.log("######################nameOrDescription",nameOrDescription,formulaView);
    // Normalize recipe name
    recipeName = (recipeName || '').trim(); 
    if (!recipeName) {
      return res.send({ status: 0, message: 'Product Formula name is required.' });
    }

    // Get current recipe to check its type
    const currentRecipe = await Recipe.getRecipeById(params);
    if (!currentRecipe || !currentRecipe[0]) {
      return res.send({ status: 0, message: 'Recipe not found.' });
    }

    const nameOrDescriptionValue = (() => {
      const raw = nameOrDescription ?? formulaView;
      if (raw === 'description' || Number(raw) === 2) return 2;
      return 1; // default to name
    })();

    const currentRecipeType = currentRecipe[0].type || 1;
    const currentRecipeNameRaw = (currentRecipe[0].name || '').trim();
    const currentRecipeName = currentRecipeNameRaw.toLowerCase();

    // If split edit and name unchanged, append timestamp to make it unique and flag edit
    if (isSplitEdited && recipeName.toLowerCase() === currentRecipeName) {
      const timestampSuffix = moment().format('YYYYMMDDHHmmss');
      recipeName = `${recipeName}-${timestampSuffix}`;
    }

    // Check for duplicate name (only if name is actually changing and not split-edit forced)
    if (!isSplitEdited && recipeName.toLowerCase() !== currentRecipeName) {
      const existingRecipes = await Recipe.getAllReceipes();
      const nameExists = existingRecipes.some(
        (r) => {
          // Exclude the current recipe being updated
          if (r.recipe_id === params) return false;
          
          const rName = (r.name || '').trim().toLowerCase();
          const rType = r.type || 1;
          
          // For production formulas (type 1), only check against other production formulas
          if (currentRecipeType === 1) {
            return rType === 1 && rName === recipeName.toLowerCase();
          }
          
          // For other types, check against all recipes
          return rName === recipeName.toLowerCase();
        }
      );

      if (nameExists) {
        return res.send({
          status: 0,
          message: `Product Formula "${recipeName}" already exists.`,
        });
      }
    }
    
    let data = {
      name: recipeName,
      expected_cost: expectedCost,
      expected_material_cost: expectedProductCost,
      expected_service_cost: expectedServiceCost,
      expected_total_service_cost: expectedTotalServiceCost,
      expected_material_cost_with_markup:expectedProductCostWithMarkup,
      expected_service_cost_with_markup:expectedServiceCostWithMarkup,
      expected_cost_with_markup:expectedCostWithMarkup,
      added_by: userId,
      // total_retail_markup: totalRetailMarkUp,
      timestamp: moment().unix(),
      expected_install_labor_cost:Number(expectedInstallLaborCost),
      expected_install_labor_cost_with_markup:Number(expectedInstallLaborCostWithMarkup),
      name_or_description: nameOrDescription,
      isSplitEdited: isSplitEdited ? 1 : 0,
      workflow_id: selectedWorkflow ? selectedWorkflow.id : null,
    };



    let previosdata =previosWorkflowId ? await Onboard.getWorkFlowbyId(previosWorkflowId) : null;

    let isWorfkflowAutoGenerated = 0
    if((!previosWorkflowId && !selectedWorkflow) || (previosWorkflowId && !selectedWorkflow && previosdata[0].is_auto_generated == 0)){
            //add workflow
            let workflow = {
              name: recipeName,
              is_default: 0,
              timestamp: moment().unix(),
              updated_on: moment().unix(),
              added_by: userId,
              status: 4,
              is_auto_generated: 1,
            };
            let workflowId = await Onboard.addWorkFlow(workflow);
            data.workflow_id = workflowId.id;
            isWorfkflowAutoGenerated =1
    }else if(!selectedWorkflow &&  services.length > 0 && previosWorkflowId){
      //add workflow
      let workflow = {
        name: recipeName,
        timestamp: moment().unix(),
        updated_on: moment().unix(),
        status: 4,
      };
      let workflowId = await Onboard.updateWorkFlow(previosWorkflowId,workflow);
      data.workflow_id = previosWorkflowId;
      let stageId = await OnboardWorkflowStages.deleteWorkFlowStageByWorkflowId(previosWorkflowId);
    }
    // Update the recipe
    let recipeId = await Recipe.updateRecipe(data, params);
    
    // Delete existing ingredients and services for the recipe
    await Recipe.deleteRecipe(params);
    await Recipe.deleteServicesRecipe(params);

    // Add new ingredients
    for (let i = 0; i < values.length; i++) {
      let ingredient = {
        recipe_id: params,
        product_id: values[i].productId,
        added_by: userId,
        quantity: values[i].quantity,
        status: 1,
        timestamp: moment().unix(),
        stockType: values[i]?.stockType || 1,
        width: values[i]?.stockType == 1 ? null : values[i]?.width ? parseFloat(values[i].width).toFixed(4) : null,
        convertionUnit: values[i]?.stockType == 1 ? null : values[i]?.convertionUnit ? values[i].convertionUnit : null,
        convertionQty: values[i]?.stockType == 1 ? null : values[i]?.convertionQty? parseFloat(values[i].convertionQty).toFixed(4) : null,
      };
      await Recipe.CreateIngredients(ingredient);
      let ingredientsHistory = {
        recipe_id: params,
        name : values[i].ing_name,
        material_id	: values[i].productId,
        added_by: userId,
        old_price: values[i].unitPrice,
        new_price: values[i].unitPrice,
        new_qty: values[i].quantity,
        timestamp: moment().unix()
      };
      await Recipe.CreateRecipeHistory(ingredientsHistory);
    }

    // Add new services
    for (let i = 0; i < services.length; i++) {
      let service = {
        recipe_id: params,
        service_id: services[i].serviceId,
        added_by: userId,
        quantity: services[i].quantity,
        facility_id: (isSplit && services[i].serviceId === 10) ? services[i].facility_id : null,
        status: 1,
        timestamp: moment().unix()
      };
      await Recipe.CreateServicesRecipe(service);
      let ingredientsHistory = {
        recipe_id: params,
        name : services[i].ing_name,
        service_id	: services[i].serviceId,
        added_by: userId,
        old_price: services[i].unitPrice,
        new_price: services[i].unitPrice,
        new_qty: services[i].quantity,
        timestamp: moment().unix()
      };
      await Recipe.CreateRecipeHistory(ingredientsHistory);

      if(!selectedWorkflow &&  services.length > 0){
        //add stages
        let skillsIdFromService = await Product.getSkillsIdFromService(services[i].serviceId);

        let StageData={
          workflow_id: data.workflow_id,
          name: services[i].ing_name,
          stage_type: 1,
          flow_type: 1,
          internal_stage: 0,
          message: services[i].ing_name,
          partner_type: 0,
          group_ids: '',
          skill_ids: skillsIdFromService.join(','),
          service_id: services[i].serviceId,
          position_order: i + 1,
          timestamp:  moment().unix(),
          updated_on:  moment().unix(),
          added_by: userId,
          status: 1,
        }
        let stageId = await OnboardWorkflowStages.addStage(StageData);

        //add onboard workflow stage fields
        let onboardWorkflowStageFieldIds = await Product.getOnboardWorkflowFieldTemplateIdFromService(services[i].serviceId);
        for (let j = 0; j < onboardWorkflowStageFieldIds.length; j++) {
          let onboardWorkflowStageField = {
            stage_id: stageId.id,
            field_template_id: onboardWorkflowStageFieldIds[j],
            timestamp:  moment().unix(),
            updated_on:  moment().unix(),
            added_by: userId,
            status: 1,
          }
          let onboardWorkflowStageFieldId = await OnboardWorkflowFields.addOnboardworkFlowFields(onboardWorkflowStageField);
        }
      }
      
    }

    // Update products with new recipe data
    if (recipeId) {
      let allProductsWithCategory = await Product.fetchAllProductsByRecipe(true, false, params);

      for (let i = 0; i < allProductsWithCategory.length; i++) {
        let cost = allProductsWithCategory[i].expected_material_cost + allProductsWithCategory[i].expected_total_service_cost;
        let product = {
          price: cost + (allProductsWithCategory[i].expected_material_cost * allProductsWithCategory[i].wholesaleMarkup / 100),
          // retail_price: cost + (allProductsWithCategory[i].expected_material_cost * allProductsWithCategory[i].retailMarkup / 100),
          // retail_price: cost + (allProductsWithCategory[i].expected_material_cost * allProductsWithCategory[i].totalRetailMarkUp  / 100),
          // retail_price: parseFloat(Number(expectedProductCostWithMarkup) + Number(expectedServiceCostWithMarkup)).toFixed(2),
          formula_cost: parseFloat(Number(expectedProductCostWithMarkup) + Number(expectedServiceCostWithMarkup) + Number(expectedInstallLaborCostWithMarkup)).toFixed(2),
          // special_price: allProductsWithCategory[i].expected_material_cost, previos
          special_price: expectedProductCostWithMarkup,
          // pf_retail_markup:totalRetailMarkUp
        };
        const updatedFormulaCost = parseFloat(Number(expectedProductCostWithMarkup) + Number(expectedServiceCostWithMarkup)).toFixed(2) //  + Number(expectedInstallLaborCostWithMarkup)
        const currentRetailPrice = allProductsWithCategory[i].actual_retail_price;
        if(updatedFormulaCost > currentRetailPrice){
          product.actual_retail_price = updatedFormulaCost;
          product.retail_price = parseFloat(updatedFormulaCost) + parseFloat(allProductsWithCategory[i].install_cost || 0);
          console.log('expectedCostWithMarkup1', parseFloat(updatedFormulaCost) + parseFloat(allProductsWithCategory[i].install_cost))
        }
        console.log('productproductproduct', product)
        await Product.updateProduct(allProductsWithCategory[i].id, product);
      }

      // Notification
      let currentDate = moment().unix();
      let notificationMsg = `The product formula "${recipeName}" has been updated.`;
      let notificationUrl = "recipe";
      
      // Alert to user of updated recipe
      await Admin.incrementField(userId, "noti_count", 1);
      let notificationData_user = {
        user_id: userId,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId
      };
      await Admin.addNotification(notificationData_user);

      // Alert to admins of updated recipe
      let adminIds = await Admin.getAllAdminIds();
      for (const adminId of adminIds) {
        if (adminId !== userId) {
          await Admin.incrementField(adminId, "noti_count", 1);
          let notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId
          };
          await Admin.addNotification(notificationData);
        }
      }

      // Fetch updated recipes and locate the updated one
      let recipes = await Recipe.getAllReceipes();
      let recipe = recipes.find((r) => r.recipe_id === params);

      // Strip timestamp suffix for display (keep stored name intact)
      const cleanRecipe = recipe?.isSplitEdited && /-\d{14}$/.test(recipe?.name || '')
        ? { ...recipe, name: (recipe.name || '').replace(/-\d{14}$/, '') }
        : recipe;
      const cleanRecipes = (recipes || []).map((r) =>
        r?.isSplitEdited && /-\d{14}$/.test(r?.name || '') ? { ...r, name: (r.name || '').replace(/-\d{14}$/, '') } : r
      );

      res.send({ status: 1, message: 'Product Formula updated Successfully', recipe: cleanRecipe, recipes: cleanRecipes,isWorfkflowAutoGenerated });
    } else {
      res.send({ status: 0, message: 'Error updating Product Formula' });
    }
  } catch (error) {
    console.error('Error: ', error);
    res.send({ status: 0, message: 'An error occurred while updating the product formula.' });
  }
};

const markRecipeViewed = async (req, res) => {
  try {
    let { id } = req.body;
    if (!id) {
      return res.send({ status: 0, message: 'Invalid recipe id' });
    }

    const recipeId = Array.isArray(id) ? id[0] : id;
    const updated = await Recipe.updateRecipe({ isNew: 0 }, recipeId);

    if (updated) {
      return res.send({ status: 1, message: 'Recipe marked as viewed', id: recipeId });
    }

    return res.send({ status: 0, message: 'Recipe not found' });
  } catch (error) {
    console.error('Error in markRecipeViewed:', error);
    return res.send({ status: 500, message: 'Server error' });
  }
};

// const updateRecipe = async (req, res) => {
//   let { recipeName, values, services, userId, params, expectedCost, expectedProductCost, expectedServiceCost, expectedTotalServiceCost } = req.body
//   let data = {
//     name: recipeName,
//     expected_cost: expectedCost,
//     expected_material_cost: expectedProductCost,
//     expected_service_cost: expectedServiceCost,
//     expected_total_service_cost: expectedTotalServiceCost,
//     added_by: userId,
//     timestamp: moment().unix()
//   }
//   // let recipeId=await Recipe.CreateRecipe(data)
//   let recipeId = await Recipe.updateRecipe(data, params);

//   let deleteData = await Recipe.deleteRecipe(params)
//   let deleteServiceData = await Recipe.deleteServicesRecipe(params)
//   for (let i = 0; i < values.length; i++) {
//     let ingredients = {
//       recipe_id: params,
//       product_id: values[i].productId,
//       added_by: userId,
//       quantity: values[i].quantity,
//       status: 1,
//       timestamp: moment().unix()
//     }
//     await Recipe.CreateIngredients(ingredients)
//   }
//   for (let i = 0; i < services.length; i++) {
//     let ingredients = {
//       recipe_id: params,
//       service_id: services[i].serviceId,
//       added_by: userId,
//       quantity: services[i].quantity,
//       status: 1,
//       timestamp: moment().unix()
//     }
//     await Recipe.CreateServicesRecipe(ingredients)
//   }
//   if (recipeId) {
//     let allProductsWithCategory = await Product.fetchAllProductsByRecipe(true, false, params)
//     console.log("#################ProductCategory", allProductsWithCategory);
//     for (let i = 0; i < allProductsWithCategory.length; i++) {
//       let cost = allProductsWithCategory[i].expected_material_cost + allProductsWithCategory[i].expected_total_service_cost
//       let product = {}
//       product.price = cost + (allProductsWithCategory[i].expected_material_cost * allProductsWithCategory[i].wholesaleMarkup / 100)
//       product.retail_price = cost + (allProductsWithCategory[i].expected_material_cost * allProductsWithCategory[i].retailMarkup / 100)
//       product.special_price = allProductsWithCategory[i].expected_material_cost
//       let updated = await Product.updateProduct(allProductsWithCategory[i].id, product);

//     }
//     res.send({ status: 1, message: 'Product Formula Added Successfully' })
//   }
//   else {
//     res.send({ status: 0, message: 'Error Adding Product Formula' })
//   }
// }

const deleteRecipe = async (req, res) => {
  try {
    let { id, status, userId } = req.body;

    let oldData = await Recipe.getRecipeById(id);
    let recipeId = await Recipe.updateRecipe({ status: status }, id);

    if (recipeId) {
      // Notification
      let currentTime = moment().unix();
      let notificationMsg = `The product formula ${oldData[0]?.name} has been deleted.`;
      let notificationUrl = "recipe";

      // Alert to user of deleted recipe
      await Admin.incrementField(userId, "noti_count", 1);
      let notificationData_user = {
        user_id: userId,
        url: notificationUrl,
        timestamp: currentTime,
        message: notificationMsg,
        status: 1,
        from_id: userId
      };
      await Admin.addNotification(notificationData_user);

      // Alert to admins of deleted recipe
      let adminIds = await Admin.getAllAdminIds();
      for (const adminId of adminIds) {
        if (adminId !== userId) {
          await Admin.incrementField(adminId, "noti_count", 1);
          let notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentTime,
            message: notificationMsg,
            status: 1,
            from_id: userId
          };
          await Admin.addNotification(notificationData);
        }
      }

      res.send({ status: 1, message: `Product Formula ${oldData[0]?.name} Deleted Successfully` });
    } else {
      res.send({ status: 0, message: 'Failed to delete Product Formula' });
    }
  } catch (error) {
    console.error('Error: ', error);
    res.send({ status: 0, message: 'An error occurred while deleting the Product Formula.' });
  }
};

// const deleteRecipe = async (req, res) => {
//   let { id, status } = req.body
//   let recipeId = await Recipe.updateRecipe({ status: status }, id);
//   if (recipeId) {
//     res.send({ status: 1, message: 'Product Formula Deleted Successfully' })
//   }
//   else {
//     res.send({ status: 0, message: 'Failed to delete' })
//   }
// }

const processStaffTimeLogs = async (stafftimelogs, userId) => {
  const aggregatedData = {};
  const taskPayOuts = {};

  // Deduplicate and aggregate hours by `task_id`, `team_id`, and `user_id`
  for (const subData of stafftimelogs) {
    console.log("subDatasubDatasubData", subData);
    const key = `${subData.job_id || "null"}_${subData.task_id || "null"}_${subData.service_id || "null"}_${subData.team_id || "null"}_${subData.user_id}`;
    const startMoment = moment.unix(subData.start_time); // Convert start_time to moment
    const endMoment = subData.end_time
      ? moment.unix(subData.end_time)
      : moment(); // Convert end_time or use current time

    // Calculate duration in hours
    const duration = moment.duration(endMoment.diff(startMoment)); // Duration between start and end
    const hours = duration.asSeconds() / 3600; // Convert seconds to hours
    // let subTaskArray = subData?.sub_task_id.split(",");
    let subTaskArray = subData?.sub_task_id ? subData.sub_task_id.split(",") : [];
 
    let noOfSubTask =subTaskArray.length
    if(subTaskArray==0){
      noOfSubTask = 1
    }
    // const totalHours = hours * subTaskArray.length;
    const totalHours = hours * noOfSubTask;

    console.log('totalHours',totalHours,subTaskArray,hours,noOfSubTask)

    if (!aggregatedData[key]) {
      aggregatedData[key] = {
        job_id: subData.job_id,
        task_id: subData.task_id,
        sub_task_id: subData.sub_task_id,
        team_id: subData.team_id,
        // team_id: userId || null,
        user_id: subData.user_id,
        isDriver: subData.isDriver || null,
        staff_name: subData.staff_name,
        task_name: subData.task_name,
        service_name : subData.service_name,
        service_id : subData.service_id,
        hours: 0,
        hourly_rate: subData.hourly_rate || 20,
        total_cost: 0, // Will be updated with payout
      };
    }
										  
    aggregatedData[key].hours += totalHours;
  }

  // Extract unique task-team combinations for payouts
  const uniqueTaskTeamPairs = [
    ...new Set(
      Object.values(aggregatedData).map(
        (log) =>
          `${log.job_id || "null"}_${log.task_id || "null"}_${log.service_id || "null"}_${log.team_id || "null"}`
      )
    ),
  ];

  // Fetch payouts for unique `task_id` and `team_id`
  console.log('uniqueTaskTeamPairs',uniqueTaskTeamPairs)
  await Promise.all(
    uniqueTaskTeamPairs.map(async (pair) => {
      const [jobId, taskId, teamId] = pair.split("_");
      const jobIdValue = jobId === "null" ? null : jobId;
      const taskIdValue = taskId === "null" ? null : taskId;
      const teamIdValue = teamId === "null" ? null : teamId;

      // Fetch payouts
      const payouts = await JobCost.getPayOutByTaskId(
        jobIdValue,
        taskIdValue,
        teamIdValue,
        (jobcostTimelog = true)
      );

      console.log('payoutspayoutspayouts',payouts)
      // Aggregate total payout
      const totalPayout = payouts.reduce(
        (sum, payout) => sum + parseFloat(payout.amount || 0),
        0
      );
      taskPayOuts[pair] = totalPayout;
    })
  );
  console.log("aggregatedData===", aggregatedData);
  // Update total cost in aggregated data
  for (const key in aggregatedData) {
    const log = aggregatedData[key];
    const taskTeamKey = `${log.job_id || "null"}_${log.task_id || "null"}_${
      log.team_id || "null"
    }`;
    log.total_cost = taskPayOuts[taskTeamKey] || 0;
  }

  // Convert aggregated data to an array
  console.log("aggregatedData===final", aggregatedData);

  return Object.values(aggregatedData);
};

function formatHoursToHHMMSS(hours) {
  const duration = moment.duration(hours, "hours");
  const formattedDuration = moment
    .utc(duration.asMilliseconds())
    .format("HH:mm:ss");
  return formattedDuration;
}

const processStaffTimeLogsDesignOrder = async (stafftimelogs,userId) => {
  const aggregatedData = {};
  const taskPayOuts = {};

console.log('stafftimelogsstafftimelogs',stafftimelogs)
  // Deduplicate and aggregate hours by `task_id`, `team_id`, and `user_id`
  for (const subData of stafftimelogs) {
    const key = `${subData.job_id || 'null'}_${subData.task_id || 'null'}_${subData.team_id || 'null'}_${subData.user_id}`;
    const startMoment = moment.unix(subData.start_time); // Convert start_time to moment
    const endMoment = subData.end_time ? moment.unix(subData.end_time) : moment(); // Convert end_time or use current time


    // Calculate duration in hours
    const duration = moment.duration(endMoment.diff(startMoment)); // Duration between start and end
    const hours = duration.asSeconds() / 3600; // Convert seconds to hours
    let subTaskArray = subData?.sub_task_id? subData?.sub_task_id?.split(",") :[];
    const totalHours = hours * subTaskArray.length;

    if (!aggregatedData[key]) {
      aggregatedData[key] = {
        design_order_id: subData.design_order_id,
        task_id: subData.task_id,
        // team_id: subData.user_id,
        team_id: userId || null,
        sub_task_id: subData.sub_task_id,
        user_id: subData.user_id,
        staff_name: subData.staff_name,
        task_name: subData.task_name,
        hours: 0,
        hourly_rate: subData.hourly_rate || 20,
        total_cost: 0, // Will be updated with payout
      };
    }
    // aggregatedData[key].hours += hours;
    aggregatedData[key].hours += totalHours;
  }


  // Extract unique task-team combinations for payouts
  const uniqueTaskTeamPairs = [
    ...new Set(
      Object.values(aggregatedData).map((log) => `${log.job_id || 'null'}_${log.task_id || 'null'}_${log.team_id || 'null'}`)
    ),
  ];


  // Fetch payouts for unique `task_id` and `team_id`
  await Promise.all(
    uniqueTaskTeamPairs.map(async (pair) => {
      const [design_order_id, taskId, teamId] = pair.split('_');
      const jobIdValue = design_order_id === 'null' ? null : design_order_id;
      const taskIdValue = taskId === 'null' ? null : taskId;
      const teamIdValue = teamId === 'null' ? null : teamId;


      // Fetch payouts
      const payouts = await Request.getDesignOrderPayOutByTaskId( taskIdValue, teamIdValue, jobcostTimelog=true);
      console.log('payoutspayouts===',payouts)

 console.log('payoutspayoutspayoutspayouts',payouts)
      // Aggregate total payout
      const totalPayout = payouts.reduce((sum, payout) => sum + parseFloat(payout.amount || 0), 0);
      taskPayOuts[pair] = totalPayout;
    })
  );

  // Update total cost in aggregated data
  for (const key in aggregatedData) {
    const log = aggregatedData[key];
    const taskTeamKey = `${log.job_id || 'null'}_${log.task_id || 'null'}_${log.team_id || 'null'}`;
    log.total_cost = taskPayOuts[taskTeamKey] || 0;
  }

console.log('aggregatedData===',aggregatedData)



  // Convert aggregated data to an array
  return Object.values(aggregatedData);
};

const fetchMirroredJobCostFromFacility = async ({
  originRequestId,
  userId,
  userType,
  targetFacilityId,
}) => {
  const targetFacility = filteredSignwiseInstalations.find(
    f => f.id === Number(targetFacilityId)
  );

  if (!targetFacility?.url) return null;

  // Resolve project ID from the other facility
  const targetProjectId = await SplitManagementModel.getProjectIdFromOtherFacility(targetFacilityId, originRequestId);

  if (!targetProjectId) return null;

  // Fetch job costs from the other facility for the mirrored project
  const jobCostResponse = await axios.post(
    `${targetFacility.url}project/fetch-all-job-cost-recipe`,
    {
      projectId: targetProjectId,
      orderId: originRequestId,
      userId,
      userType
    }
  );

  return jobCostResponse?.data || null;
};


const fetchAllJobCostByProjectIdRecipe = async (req, reply) => {
  let { projectId, userType, userId, orderId } = req.body;
  let recipe = 1
  let projects = await Projects.getProjectById(projectId);
  // for split mirroring
  const originRequestId = projects?.request_id;
  let isSplit = projects?.is_split_order;
  const facilityTypes = (projects?.facility_type || "")
    .split(",")
    .map(v => v.trim())
    .filter(v => v !== "");

  let mirrorFacility = projects?.for_parent_facility == 2 ? true : false;
  const targettedFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));

   // If the project is split and a mirror facility exists, fetch data from the mirror facility
   if (isSplit && mirrorFacility && targettedFacilityId.length > 0) {
    const mirroredData = await fetchMirroredJobCostFromFacility({
      originRequestId: projects?.origin_facility_request_id,
      userId,
      userType,
      targetFacilityId: targettedFacilityId[0], // Usually one target
    });

    if (mirroredData) {
      reply.send({
        status: 1,
        jobcostUpdates: mirroredData.jobcostUpdates,
        selectedMaterials: mirroredData.selectedMaterials,
        stockProducts: mirroredData.stockProducts,
        stafftimelogs: mirroredData.stafftimelogs,
        newstafftimelogs: mirroredData.newstafftimelogs,
        projectTask: mirroredData.projectTask,
        projectMembers: mirroredData.projectMembers,
        results: mirroredData.results,
        additionalServices: mirroredData.additionalServices,
        allPayOut: mirroredData.allPayOut,
        allServices: mirroredData.allServices,
        projects: mirroredData.projects,
        allMaterials: mirroredData.allMaterials,
        allRecipies: mirroredData.allRecipies,
        materials: mirroredData.materials
      });
      return;
    }    
  }

  let selectedMaterials = await JobCost.fetchAllJobCostByProjectId(projectId, userId, recipe,false,false);
  let stockProducts = [];
  if(projects?.project_category !== 15){
    // stockProducts = await JobCost.fetchAllProductsStock(projectId, userId, recipe)
  }else{
    stockProducts =[]
  }
  console.log('stockProducts', stockProducts)
  recipe = 0
  let materials = await JobCost.fetchAllJobCostByProjectId(projectId, userId, recipe);
  let allPayOut = await JobCost.fetchAllPayOutByProjectId(projectId, userId);
  let projectMembers = await Projects.getAllProjectMembers(projectId);
  let projectTask = await ProjectTask.getAllProjectTasks(projectId, userId, userType, true);
  for (const newData of projectTask) {
    let projectSubTask = await ProjectTask.getAllProjectSubTaskBytaskId(
      newData.ID,
      userType,
      userId
    );
    let newUserId = (userType == 1 || userType == 8) ? null : userId;
    let stafftimelogs = await ProjectTask.fetchStaffTaskTimelog(
      false,
      newUserId,
      newData.ID,
      false,
      false
    );
    newData.total_hours = 0;
    let newhours = 0;
    if (stafftimelogs) {
      for (const subData of stafftimelogs) {
        let subTaskArray =subData?.sub_task_id? subData?.sub_task_id?.split(","):[] || [];
        for (const sub of subTaskArray) {
          let hours = 0;
          if (parseInt(subData.task_id) === newData.ID) {
            const startMoment = moment.unix(subData.start_time);
            let endMoment;
            if (subData.end_time !== null) {
              endMoment = moment.unix(subData.end_time);
            } else {
              endMoment = moment();
            }
            const duration = moment.duration(endMoment.diff(startMoment));
            hours = duration.asHours();
            if (!isNaN(hours)) {
              newhours += hours;
            }
          }
        }
      }
    }
    if(projectSubTask?.length > 0){
      const newhours = projectSubTask.reduce(
        (total, subtask) => total + (subtask.total_hours || 0),
        0
      );
      newData.total_hours = formatHoursToHHMMSS(newhours);
    }
    newData.total_hours = formatHoursToHHMMSS(newhours);
  }
  if (userType != 3 && userType != 1 && userType != 8) {
    let projectTaskFilteredPromises = projectTask.map(async (task) => {
      let projectSubTask = await ProjectTask.getAllProjectSubTaskBytaskId(task.ID,userType,userId);
      return projectSubTask.length > 0 || true;
    });

    let projectTaskFilteredResults = await Promise.all(projectTaskFilteredPromises);
    projectTask = projectTask.filter((_, index) => projectTaskFilteredResults[index]);
  }
  console.log("Fetching project with ID:", projectId);
  // let projects = await Projects.getProjectById(projectId);
  // let results = await Products.getAllMaterials(true);
  let allMaterials = await Products.getAllMaterials(true)
  let allProducts =[]
  if(projects?.project_category === 15){
    allProducts = await Products.getAllLocations(orderId)
  }else{
    allProducts = await Product.getNonStockProducts(orderId)
  }
  let jobcostUpdates = await RequestUpdates.fetchAllJobcostUpdates(projects?.request_id)
  let groupedMaterials = materials?.reduce((acc, material) => {
    let existing = acc.find(item => item.product_id  === material.product_id);
    if (existing) {
      existing.actual_unit_price = material.actual_unit_price
      existing.quantity += material.quantity
      existing.actual_quantity += material.actual_quantity
      existing.total += material.total;
      existing.actual_total += material.actual_total;
    } else {
      acc.push({
        ...material
      });
    }

    return acc;
  }, []);
  console.log("#####groupedMaterials",groupedMaterials);
  materials = groupedMaterials

  console.log('allProducts===',allProducts)
  const allProductIds = allProducts?.split(',').map(id => parseInt(id, 10));
  console.log('allProductIds 6', allProductIds)
  const updatedMaterials = materials?.map(material => {
    return {
      ...material,
      notFromRecipe: !allProductIds.includes(material.product_id),
    };
  });
  materials = updatedMaterials;
  // console.log('materials===',materials)
  let allRecipies = []
  if(allProducts && allProducts != ""){
    allRecipies = await Recipe.getAllReceipesByProjectId(allProducts, orderId);
  }
  let results = await Products.getFinishedProducts();
  let allServices = await Recipe.fetchAllServicesByRequest(orderId)
  const filteredServices = allServices.filter(service => {
    // If product_id is not null or undefined, include only if it's in allProductIds
    if (service.product_id != null) {
      return allProductIds.includes(service.product_id);
    }
    // If product_id is null or undefined, include by default
    // return true;
    return service.facility_id == FACILITY_ID || service.facility_id == null;

  });
  console.log('servicesproduct_iddd', allServices.map(s => s.product_id));
  console.log('Filtered Services:', filteredServices);
  console.log('allServicesssss', allServices)
  let additionalServices = await Recipe.fetchAllAdditionalServicesByRequest(orderId)
  let newUserId = (userType == 1 || userType == 8) ? null : userId;
  let stafftimelogs = await ProjectTask.fetchAllStaffTaskTimelogByProjectId(newUserId, projectId);
  console.log('stafftimelogs===',stafftimelogs)
  let newstafftimelogs = stafftimelogs
  newstafftimelogs?.map((subData)=>{
    const startMoment = moment.unix(subData.start_time);
    const endMoment = subData.end_time ? moment.unix(subData.end_time) : moment();
    const duration = moment.duration(endMoment.diff(startMoment));
    const hours = duration.asSeconds() / 3600;
    subData['hours'] = hours
    subData['duration'] = duration
    subData['formattedHours'] = formatHoursToHHMMSS(hours);

  })
  const finalData = await processStaffTimeLogs(stafftimelogs, newUserId);

  reply.send({ status: 1,jobcostUpdates, selectedMaterials,stockProducts, stafftimelogs:finalData, newstafftimelogs,  projectTask, projectMembers, 
    results, additionalServices, allPayOut, allServices:filteredServices, projects, allMaterials, allRecipies, materials });
};

const updateJobCostAuto = async (req, reply) => {
  try {
    let requestId = req.body.requestId
    let projectId = req.body.projectId
    let userId = req.body.userId
    let name = req.body.name
    let flag = 0
    let recipe = 1
    let insertedstatus = null;
    let allMaterials = await Product.getAllMaterials(true)
    let allProductsData = await Product.getAllLocations(requestId)
    let allRecipies = []
    if(allProductsData && allProductsData != ""){
      allRecipies = await Recipe.getAllReceipesByProjectId(allProductsData, requestId);
    }
    let selectedMaterials = await JobCost.fetchAllJobCostByProjectId(projectId, userId = 1, recipe);
    let reduceStockResult, deleted;
    // console.log("########SElectedMaterial", allRecipies);
    // console.log("########selectedMaterials", selectedMaterials);
    if (selectedMaterials.length > 0) {
      for (const element of selectedMaterials) {
        const material = await Product.getMaterialById(element.product_id)
        console.log('material---', material);
        if (!empty(material)) {
          //const matchingValue = newMaterialObjects?.find(val => val?.id === material[0]?.id);
          // let quantity = Math.max(element?.quantity || 0, element?.actual_quantity || 0);
          // let newQty = material[0]?.qty + quantity
          let newQty = material[0]?.qty + (element.quantity || 0)
          let totalCost = material[0]?.unit_cost * newQty
          // if (matchingValue ? newQty >= matchingValue.quantity : true) {
          if (newQty >= parseInt(material[0].qty)) {
            const data = {
              qty: newQty,
              total_cost: totalCost
            };
            console.log('data=1======>', element.product_id, data)
            const updated = await Product.updateMaterialStock(element.product_id, data)
            if (updated) {
              deleted = await JobCost.deleteJobCost(element.id, recipe);
            } else {
              console.log('Failed to update stock');
              reply.send({ status: 0, message: 'Failed to update stock' });
            }
          } else {
            // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${material[0]?.name} (${material[0].barcode})`, resultId: material[0].id });
            // return;
          }
          // } else {
          //   reply.send({ status: 3, message: `Stock is lesser than entered qty` });
          // }

        }
      }
      flag = 1
    }

    let results = await Product.getFinishedProducts(0,true);
    let newMaterialObjects = [];
    let jobCost = 0;
    let notifyJobcost = ""


    for (const recipeItem of allRecipies) {
      for (const ingredient of recipeItem.ingredients) {
        // let quantityNeeded = ingredient.quantity * recipeItem.quantity * recipeItem.location_quantity;
        // let materialItems = results.filter((mat) => mat.material_id === ingredient.product_id);
        // let totalAvailableQuantity = materialItems.reduce((total, item) => total + item.qty, 0);

        // if (totalAvailableQuantity >= quantityNeeded) {
        //   // Sufficient quantity available in existing packages
        //   let remainingQuantity = quantityNeeded;

        
        let materialItems = results?.filter((mat) => mat.material_id === ingredient.product_id);
        let filteredMaterialItems = materialItems?.filter(mat => 
          (ingredient.stockType === 2 ? mat.partial === 1 : mat.partial === 0)
        );
        console.log('recipeItem==ingredient=====',ingredient);
        console.log('recipeItem==filteredMaterialItems=====',filteredMaterialItems);
        let quantityInInitialUnit = 0;
        quantityInInitialUnit = ingredient?.quantity;
        // if(ingredient.stockType === 1 || materialItems[0]?.unit == 4){
        //   quantityInInitialUnit = ingredient?.quantity;
        // }else{
        //   let conversionFactorToInitial = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, ingredient?.width || 1);
        //   quantityInInitialUnit = ingredient?.convertionQty * conversionFactorToInitial;
        // }
        let quantityNeeded = quantityInInitialUnit * recipeItem?.quantity * recipeItem?.location_quantity;
        // let quantityNeeded = ingredient?.quantity*recipeItem?.quantity*recipeItem?.location_quantity;
        // let totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item.qty, 0);
        let totalAvailableQuantity = 0;
        if(ingredient.stockType === 1){
          totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item?.qty, 0);
        }else{
          totalAvailableQuantity = filteredMaterialItems?.filter((item)=>item.partial_unit==ingredient?.convertionUnit)?.reduce((total, item) => total + item?.qty, 0);
        }
        // console.log(`Converted Ingredient Quantity to Initial Unit (${ingredient.convertionUnit} → ${materialItems[0]?.unit}), width : ${ingredient?.width}:`, quantityInInitialUnit);
        // console.log('filteredMaterialItems===', filteredMaterialItems);
        // console.log('ingredient?.quantity===', ingredient?.quantity);
        // console.log('recipeItem?.quantity===', recipeItem?.quantity);
        // console.log('recipeItem?.location_quantity===', recipeItem?.location_quantity);
        // console.log('Total Available Quantity (Before Conversion) ===', totalAvailableQuantity);
        // console.log('Quantity Needed (Before Conversion) ===', quantityNeeded);
        
        // // Get actual width and quantity from material stock
        // let width = ingredient?.width || 1;

        // // Convert quantityNeeded dynamically using width
        // let conversionFactor = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, width);
        // let convertedQuantityNeeded = quantityNeeded * conversionFactor;

        // if (totalAvailableQuantity >= quantityNeeded) {
        //   // Sufficient quantity available in existing packages
        //   let remainingQuantity = quantityNeeded;
        console.log('recipeItem==totalAvailableQuantity=====',totalAvailableQuantity);
        console.log('recipeItem==quantityNeeded=====',quantityInInitialUnit);
        console.log('recipeItem==quantityNeeded=====',recipeItem?.quantity);
        console.log('recipeItem==quantityNeeded=====',recipeItem?.location_quantity);
        console.log('recipeItem==quantityNeeded=====',quantityNeeded);

        if (totalAvailableQuantity >= quantityNeeded) {
        // if (totalAvailableQuantity >= convertedQuantityNeeded) {
          // ✅ Sufficient stock available
          // let remainingQuantity = convertedQuantityNeeded;
          let remainingQuantity = quantityNeeded;
          filteredMaterialItems?.forEach((materialItem) => {
            // Convert unit cost based on unit type
            let originalUnitCost = Number(materialItem?.unit_cost) || 0;
            // let convertedUnitCost = getConvertedUnitCost(originalUnitCost, materialItem?.unit, ingredient?.convertionUnit, width);

            console.log(`Unit Cost (Before Conversion):`, originalUnitCost);
            // console.log(`Converted Unit Cost (${materialItems[0]?.unit} → ${ingredient.convertionUnit} at width ${width}):`, convertedUnitCost);
            console.log(`totalAvailableQuantity :`, totalAvailableQuantity);
            // console.log(`convertedQuantityNeeded :`, convertedQuantityNeeded);
            if (remainingQuantity > 0) {
              let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
              // Find existing quantity from selectedMaterials
              let existingQuantity = 0;
              let existingTotal = 0;
              const existingMaterial = selectedMaterials.find(mat => mat.product_id === materialItem.id);
              if (existingMaterial) {
                existingQuantity = existingMaterial.quantity || 0;
                existingTotal = existingMaterial.total || 0;
              }

              newMaterialObjects.push({
                id: materialItem.id,
                productId: materialItem.material_id,
                quantity: quantityToPull,
                unit_cost: materialItem?.inhouse == 0 ? 0 : originalUnitCost,
                total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(originalUnitCost),
                recipe: recipeItem.id,
                recipeName: recipeItem.name,
                customer_order_product_id: recipeItem?.customer_order_product_id,
                existingQuantity: existingQuantity,
                existingTotal: existingTotal,
              });
              results = results.map(item => item.id === materialItem.id ? { ...item, qty: item.qty - quantityToPull } : item);
              remainingQuantity -= quantityToPull;
            }
          });
          console.log(`Added ${quantityNeeded} units of ${ingredient.name}`);
        } else {
          // Insufficient quantity available in existing packages, pull from additional packages
          console.log(`Insufficient quantity of ${ingredient.name} available in existing packages.`);
          console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...`);
          // Your logic to pull additional quantities from packages goes here
          // notifyJobcost+=` Insufficient quantity of ${ingredient.name} available in existing packages.`
          if (empty(notifyJobcost)) {
            notifyJobcost += `There is insuffient quantity on materials: ${ingredient.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
          }else {
            notifyJobcost += ` ,${ingredient.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
          }
          jobCost = 1
        }
      };
    };

    console.log("########newMaterialObjects", newMaterialObjects);


    if ((flag === 1 && deleted) || (flag === 0)) {
      for (let i = 0; i < newMaterialObjects?.length; i++) {
        const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
        if (!empty(materialData)) {
          console.log("#############MaterialData", materialData);
          let newQty = materialData[0].qty - (newMaterialObjects[i].existingQuantity || newMaterialObjects[i].quantity)
          console.log("#############newQty", newQty);
          if (newQty >= 0) {
            let totalCost = newMaterialObjects[i].unit_cost * newQty
            let data = {};
            data['qty'] = newQty
            data['total_cost'] = parseInt(totalCost)
            let jobCostData = {
              product_id: newMaterialObjects[i].id,
              project_id: projectId,
              quantity: newMaterialObjects[i].existingQuantity,
              unit_price: newMaterialObjects[i].unit_cost,
              total: newMaterialObjects[i].existingTotal,
              actual_quantity: newMaterialObjects[i].quantity,
              actual_unit_price: newMaterialObjects[i].unit_cost,
              actual_total: newMaterialObjects[i].total_cost,
              recipe: 1,
              recipe_name: newMaterialObjects[i].recipeName,
              timestamp: moment().unix(),
              added_by: userId,
              customer_order_product_id: newMaterialObjects[i]?.customer_order_product_id,
            };
            if (jobCostData?.actual_quantity > 0) {
              insertedstatus = await JobCost.addJobCost(jobCostData);
              console.log("#####insertedStatus--- 44", insertedstatus);
              console.log('data=2======>', newMaterialObjects[i].id, data)
              reduceStockResult = await Product.updateMaterialStock(newMaterialObjects[i].id, data)
              console.log("#####reduceStockResult", reduceStockResult);
              // Autofill stock if stock is 0
              // if(newQty <= 0){
              //   if(materialData[0]?.autofill == 1){
              //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
              //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
              //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
              //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
              //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
              //       let parentData = {
              //         qty: reducedQty,
              //         total_cost: parseInt(parentTotalCost)
              //       };
              //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
    
              //       let addingQty = materialData[0]?.actual_qty
              //       let totalCost = materialData[0]?.actual_total_cost
              //       let autoFilldata = {
              //         qty: addingQty,
              //         total_cost: totalCost
              //       };
              //       console.log('autofillPartialStock--',autoFilldata);
              //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
              //     }
              //   }
              // }
            }
          } else {
            console.log(`Stock is lesser than entered qty`);
            // Autofill stock if stock is 0
            // if(newQty <= 0){
            //   if(materialData[0]?.autofill == 1){
            //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
            //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
            //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
            //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
            //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
            //       let parentData = {
            //         qty: reducedQty,
            //         total_cost: parseInt(parentTotalCost)
            //       };
            //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
  
            //       let addingQty = materialData[0]?.actual_qty
            //       let totalCost = materialData[0]?.actual_total_cost
            //       let autoFilldata = {
            //         qty: addingQty,
            //         total_cost: totalCost
            //       };
            //       console.log('autofillPartialStock--',autoFilldata);
            //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
            //     }
            //   }
            // }
            //   reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
          }
        }
      }
    }
    console.log("#################newMaterialObjects", newMaterialObjects);
    let deleteRfpJobcost = await JobCost.deleteRfpJobcost(projectId)
    let rfpProduct = await CustomerOrderProduct.getRFPProductByRequest(requestId)

    console.log("###############vk1888888888888888888", rfpProduct);
    for (let i = 0; i < rfpProduct.length; i++) {
      let jobCostData = {
        product_id: rfpProduct[i].product_id,
        project_id: projectId,
        quantity: rfpProduct[i].product_qty * rfpProduct[i].location_quantity,
        unit_price: rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price,
        total: rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price * rfpProduct[i].product_qty * rfpProduct[i].location_quantity,
        actual_quantity: rfpProduct[i].product_qty * rfpProduct[i].location_quantity,
        actual_unit_price: rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price,
        actual_total: rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price * rfpProduct[i].product_qty * rfpProduct[i].location_quantity,
        recipe: 1,
        recipe_name: null,
        timestamp: moment().unix(),
        added_by: userId,
        rfp: 1,
        customer_order_product_id: rfpProduct[i].id,
      };
      insertedstatus = await JobCost.addJobCost(jobCostData);
      console.log("#####insertedStatus--- 77", insertedstatus);
    }
    console.log("#################newMaterialObjects22222222222");
    let newMaterialRfqObjects = [];
    let materialRfq = await CustomerOrderProduct.getMaterialProductByRequest(requestId)
    let requestData = await Request.getRequestById(requestId)
    if(requestData[0]['job_category_name'] == 'Time & Material'){
      for(let i=0; i<materialRfq.length; i++){
        let results = await Product.getFinishedProducts(materialRfq[i].product_id,true);
        if(results?.length > 0){
          let jobCostData = {
            product_id: results[0].id,
            project_id: projectId,
            quantity: 0,
            unit_price: materialRfq[i].product_price,
            total: 0,
            // quantity: materialRfq[i].quantity,
            // unit_price: materialRfq[i].unit_cost,
            // total: materialRfq[i].total_cost,
            actual_quantity: materialRfq[i].product_qty,
            actual_unit_price: materialRfq[i].product_price,
            actual_total: materialRfq[i].product_total,
            recipe: 0,
            recipe_name: '',
            timestamp: moment().unix(),
            added_by: userId,
          };
          insertedstatus = await JobCost.addJobCost(jobCostData);
        } else {
          console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.2`);
          if(empty(notifyJobcost)){
            notifyJobcost+=`There is insuffient quantity on materials: ${materialRfq[i].product_name} (Finished)`
          }
          else{
            notifyJobcost+=` ,${materialRfq[i].product_name} (Finished)`
          }
          jobCost=1
        }
      }
    }else{
      for (let i = 0; i < materialRfq.length; i++) {
        let quantityNeeded = materialRfq[i].product_qty;
        let materialItems = results.filter((mat) => mat.material_id === materialRfq[i].product_id);
        let totalAvailableQuantity = materialItems.reduce((total, item) => total + item.qty, 0);

        if (totalAvailableQuantity >= quantityNeeded) {
          // Sufficient quantity available in existing packages
          let remainingQuantity = quantityNeeded;
          console.log('materialRfq---',materialRfq)
          console.log('materialItems---',materialItems)
          materialItems.forEach((materialItem) => {
            if (remainingQuantity > 0) {
              let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
              // Find existing quantity from selectedMaterials
              let existingQuantity = 0;
              let existingTotal = 0;
              const existingMaterial = selectedMaterials.find(mat => mat.product_id === materialItem.id);
              if (existingMaterial) {
                existingQuantity = existingMaterial.quantity || 0;
                existingTotal = existingMaterial.total || 0;
              }

              newMaterialRfqObjects.push({
                id: materialItem.id,
                productId: materialItem.material_id,
                quantity: quantityToPull,
                unit_cost: materialItem?.inhouse == 0 ? 0 : materialItem.unit_cost,
                total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(materialItem.unit_cost),
                recipe: 0,
                recipeName: null,
                existingQuantity: existingQuantity,
                existingTotal: existingTotal,
              });
              results = results.map(item => item.id === materialItem.id ? { ...item, qty: item.qty - quantityToPull } : item);
              remainingQuantity -= quantityToPull;
            }
          });

        } else {
          // Insufficient quantity available in existing packages, pull from additional packages
          console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.`);
          console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...`);
          // Your logic to pull additional quantities from packages goes here


          if (empty(notifyJobcost)) {
            notifyJobcost += `There is insuffient quantity on materials: ${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
          }
          else {
            notifyJobcost += ` ,${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
          }
          jobCost = 1
        }
      }
      console.log("#################newMaterialObjects22222222222", newMaterialRfqObjects);
      for (let i = 0; i < newMaterialRfqObjects.length; i++) {
        const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
        if (!empty(materialData)) {
          let newQty = materialData[0].qty - (newMaterialRfqObjects[i].existingQuantity || newMaterialRfqObjects[i].quantity)
          console.log('newQty====>', newQty)
          if (newQty >= 0) {
            let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
            let data = {};
            data['qty'] = newQty
            data['total_cost'] = parseInt(totalCost)
            let jobCostData = {
              product_id: newMaterialRfqObjects[i].id,
              project_id: projectId,
              quantity: newMaterialRfqObjects[i].existingQuantity,
              unit_price: newMaterialRfqObjects[i].unit_cost,
              total: newMaterialRfqObjects[i].existingTotal,
              actual_quantity: newMaterialRfqObjects[i].quantity,
              actual_unit_price: newMaterialRfqObjects[i].unit_cost,
              actual_total: newMaterialRfqObjects[i].total_cost,
              recipe: 0,
              recipe_name: newMaterialRfqObjects[i].recipeName,
              timestamp: moment().unix(),
              added_by: userId,
            };
            if (jobCostData?.actual_quantity > 0) {
              insertedstatus = await JobCost.addJobCost(jobCostData);
              console.log("#####insertedStatus--- 55", insertedstatus);
              console.log('data=3======>', newMaterialRfqObjects[i].id, data)
              reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
              // console.log("#####reduceStockResult", reduceStockResult);
            }
          } else {
            // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
          }
        }
      }
    }

    
    if ((flag === 1 && deleted) || (flag === 0)) {
      for (let i = 0; i < newMaterialObjects?.length; i++) {
        const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
        if (!empty(materialData)) {
          // let newQty = materialData[0].qty + newMaterialObjects[i].quantity
          let newQty = materialData[0].qty + (newMaterialObjects[i].existingQuantity ? 0 : newMaterialObjects[i].quantity)
          if (newQty >= 0) {
            let totalCost = newMaterialObjects[i].unit_cost * newQty
            let data = {};
            data['qty'] = newQty
            data['total_cost'] = parseInt(totalCost)
            console.log('data=4======>', newMaterialObjects[i].id, data)
            const reduceStockResult = await Product.updateMaterialStock(newMaterialObjects[i].id, data)
          } else {
            console.log(`Stock is lesser than entered qty`);
          }
        }
      }
    }

    if(requestData[0]['job_category_name'] == 'Time & Material'){
      for (let i = 0; i < newMaterialRfqObjects.length; i++) {
        const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
        if (!empty(materialData)) {
          let newQty = materialData[0].qty + newMaterialRfqObjects[i].quantity
          if (newQty >= 0) {
            let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
            let data = {};
            data['qty'] = newQty
            data['total_cost'] = parseInt(totalCost)
            console.log('data=5======>', newMaterialRfqObjects[i].id, data)
            const reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
          }
        }
      }
    }

    let notificationMsg = ` Job "${name}" has been updated.`;
    let notificationUrl = "Jobs";
    const incrementResult_User = await Admin.incrementField(
      userId,
      "noti_count",
      1
    );
    let currentDate = moment().unix()
    let notificationData_user = {
      user_id: userId,
      url: notificationUrl,
      timestamp: currentDate,
      message: notificationMsg,
      status: 1,
      from_id: userId,
    };
    const addNotificationResult_user = await Admin.addNotification(
      notificationData_user
    );

    // Alert to all admins of new added project
    let adminIds = await Admin.getAllAdminIds();
    let notificationData = {};
    for (const adminId of adminIds) {
      if (adminId != userId) {
        const incrementResult = await Admin.incrementField(
          adminId,
          "noti_count",
          1
        );

        notificationData = {
          user_id: adminId,
          url: notificationUrl,
          timestamp: currentDate,
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };

        const addNotificationResult = await Admin.addNotification(
          notificationData
        );

      }
    }
    projectTaskData = {}
    if (jobCost === 1) {

      projectTaskData.jobcost = 1
      projectTaskData.notifyJobcost = notifyJobcost

    }
    else {
      projectTaskData.jobcost = 0
      projectTaskData.notifyJobcost = notifyJobcost
    }
    let resultStatus = await Projects.updateProject(projectId, projectTaskData);
    reply.send({ status: 1, message: "Job cost updated!" });
  }
  catch (error) {
    console.log("########################Errr", error);
  }

}

const fetchRecipeHistoryById = async (req,reply)=>{
  try{
    const {editId} = req.body;
    const recipeLog = await Recipe.fetchRecipeHistoryById(editId);
    if(recipeLog){
      reply.send({ status: 1, message: "Log Found",logData:recipeLog  });
    }else{
      reply.send({ status: 0, message: "No Log Found",logData:[] });
    }
  }catch (error) {
    console.log("fetchRecipeHistoryById Errr", error);
    reply.send({ status: 500, message: "No Log Found",logData:[] });
  }
}


const fetchAllDesignOrderJobCostByDesignOrderIdRecipe = async (req, reply) => {
  let { projectId, userType, userId, orderId } = req.body;
  let recipe = 1
  recipe = 0

  let allPayOut = await Request.fetchAllPayOutByDesignOrderId(orderId);
  // let projects = await Projects.getProjectById(projectId);
 

  let allServices = await Request.fetchAllServicesByDesignOrderId(orderId)
  let additionalServices = await Request.fetchAllAdditionalServicesByDesignOrderId(orderId)

  let newUserId = (userType == 1 || userType == 8) ? null : userId;
  // let stafftimelogs = await Request.getAllDesignOrderStaffTaskTimelogs(newUserId, orderId);
  let stafftimelogs = await Request.getAllDesignOrderStaffTaskTimelogs(newUserId, orderId);
  console.log('stafftimelogsstafftimelogs',stafftimelogs)
  let newstafftimelogs = stafftimelogs
  const finalData = await processStaffTimeLogsDesignOrder(stafftimelogs,newUserId);

  reply.send({ status: 1, stafftimelogs:finalData, newstafftimelogs, 
    additionalServices, allServices,allPayOut  });
};


module.exports = {
  getAllRecipe, addRecipe, getAllMeterialRecipe, updateRecipe, deleteRecipe,
  fetchAllJobCostByProjectIdRecipe, getAllServiceRecipe, duplicateRecipe, updateJobCostAuto,
  fetchRecipeHistoryById,fetchAllDesignOrderJobCostByDesignOrderIdRecipe, saveRecipeAsCopy,
  markRecipeViewed
}