const { default: axios } = require("axios");
const { PRODUCTION, getNewAccessToken, realmId, locationInventoryCode, departmentId } = require("../quickbook/quickbook");
const { getAccountIdByName, getAccounts } = require("../quickbook/quickbook");
const moment = require('moment')



const findItemByNamewithLocationInventoryCode = async (itemName) => {
    try {
      let token = await getNewAccessToken();
  
      const apiUrl = PRODUCTION
        ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
        : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;
  
      const response = await axios.get(apiUrl, {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/text',
        },
        params: {
          query: `SELECT * FROM Item WHERE Name = '${itemName}'`,
        },
      });
  
      const items = response.data.QueryResponse.Item;
  
      if (items && items.length > 0) {
        return items[0]; // Return the first matching item
      } else {
        console.log(`Item with name ${itemName} not found in QuickBooks.`);
      }
    } catch (error) {
      console.error('Error finding item by name:', error.response ? error.response.data : error.message);
      throw error;
    }
  };


  const editEstimateInQuickBooks = async (estimateData, estimateId) => {
    const fetchLatestEstimate = async () => {
      const token = await getNewAccessToken();
      // const url = `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate/${estimateId}?minorversion=65`;
  
      const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;
  
      const url = `${apiUrl}/estimate/${estimateId}?minorversion=65`;
      try {
        const response = await axios.get(url, {
          headers: {
            Authorization: `Bearer ${token}`,
            Accept: 'application/json'
          },
          timeout: 10000 
        });
        console.log("Fetched latest estimate:", response.data);
        return response.data;
      } catch (error) {
        console.error("Error fetching latest estimate data:", error.response ? error.response.data : error.message);
        throw error;
      }
    };
  
    const updateEstimate = async (retryCount = 0) => {
      const maxRetries = 1;
      const backoffFactor = 2;
      const baseDelay = 1000; // 1 second
  
      try {
        const token = await getNewAccessToken();
        const latestEstimate = await fetchLatestEstimate();
        const latestSyncToken = latestEstimate.Estimate.SyncToken;
  
        // Ensure all items have valid inventory start dates
        // for (let item of estimateData.items) {
        //   // const itemDetails = await findItemByName(item.name);
        //   const itemDetails = await findItemById(item.itemId);
  
        //   item.inventoryStartDate = itemDetails.InventoryStartDate;
        // }
  
        // const txnDate = moment.max(estimateData.items.map(item => moment(item.inventoryStartDate)))
        //   .add(10, 'days')
        //   .format('YYYY-MM-DD');
  
        const txnDate = moment().format('YYYY-MM-DD');
  
        const discountAccounts = await getAccounts('Income', 'DiscountsRefundsGiven');
        const dynamicDiscountAccRefId = discountAccounts?.length > 0 ? discountAccounts[0].Id : null;
  
        const discountAccId=await getAccountIdByName("Discounts") || dynamicDiscountAccRefId || discountAccRefId
  
       
        let quickBooksEstimateData = {
          sparse: false, 
          Id: estimateId,
          SyncToken: latestSyncToken,
          CustomerRef: {
            value: estimateData.customerId || latestEstimate.Estimate.CustomerRef.value
          },
          TxnDate: txnDate,
          DepartmentRef: { value: departmentId },
          Line: [
            ...estimateData.items.map(item => ({
              Amount: item.amount,
              DetailType: "SalesItemLineDetail",
              SalesItemLineDetail: {
                ItemRef: { value: item.itemId },
                Qty: item.quantity,
                UnitPrice: item.unitPrice,
                TaxCodeRef: { value: 'TAX' } 
  
              },
            })), 
            {
              Amount: estimateData.discount || 0, // Apply a fixed discount amount
              DetailType: "DiscountLineDetail",
              DiscountLineDetail: {
                DiscountAccountRef: {
                  value: discountAccId, // Your Discount Account ID
                },
              },
            },
          ],
        //   BillEmail: {
        //     Address: estimateData?.clientData?.email || ""
        //   },
        //   BillAddr: {
        //     Line1: estimateData.clientData?.quickbookCustomerName || "",
        //     Line2: estimateData.clientData?.address || "",
        //     City: estimateData.clientData?.city || "",
        //     CountrySubDivisionCode: estimateData.clientData?.state || "",
        //     PostalCode: estimateData.clientData?.zip_code || "",
        //     // Country: estimateData.clientData?.country || ""
        //   },
        
        //   ShipAddr: {
        //     Line1: estimateData.ProjectData?.name || "",
        //     Line2: estimateData.ProjectData?.address || "",
        //     City: estimateData.ProjectData?.city || "",
        //     CountrySubDivisionCode: estimateData.ProjectData?.state || "",
        //     PostalCode: estimateData.ProjectData?.zip || estimateData.ProjectData?.zip_code || "",
        //     // Country: estimateData.ProjectData?.country || ""
        //   },
          PrivateNote: estimateData.name || estimateData.description,
          // TxnTaxDetail: { no need tax from  here in qb already the client linked qb account with avalara
          //   TxnTaxCodeRef: { value: 'TAX' },
          //   TotalTax: estimateData.taxAmount // Set the total tax amount here
          // },
          CustomField: [
            {
              DefinitionId: "1", // Replace this with your actual custom field DefinitionId
              Name: "P.O. Number", // The name of your custom field
              Type: "StringType",
              StringValue: (estimateData.poNumber || "")?.slice(0, 30) // The value you want to set in the custom field
            },
            {
              DefinitionId: "2", // Replace this with your actual custom field DefinitionId
              Name: "Project", // The name of your custom field
              Type: "StringType",
              StringValue: (estimateData.projectName || "")?.slice(0, 30)// The value you want to set in the custom field
            },
            {
              DefinitionId: "3", // Replace this with your actual custom field DefinitionId
              Name: "Sales Rep Name", // The name of your custom field
              Type: "StringType",
              StringValue: (estimateData.salesRepName || "")?.slice(0, 30)// The value you want to set in the custom field
            },
            // {
            //   DefinitionId: "4", // Replace this with your actual custom field DefinitionId
            //   Name: "Sales Rep", // The name of your custom field
            //   Type: "ReferenceType",
            //   Value: 400000033 || "" // The value you want to set in the custom field
            // },
          ],
        };
        if(estimateData.jobClass){
          quickBooksEstimateData.ClassRef = { value: estimateData.jobClass }
          // quickBooksEstimateData.SalesRepRef = {value: "400000033"};
        }

        if(estimateData?.clientData){
            quickBooksEstimateData.BillEmail = {
                Address: estimateData?.clientData?.email || ""
              }
              quickBooksEstimateData.BillAddr = {
                Line1: estimateData.clientData?.quickbookCustomerName || "",
                Line2: estimateData.clientData?.address || "",
                City: estimateData.clientData?.city || "",
                CountrySubDivisionCode: estimateData.clientData?.state || "",
                PostalCode: estimateData.clientData?.zip_code || "",
              }
        }

        if( estimateData.ProjectData){
            quickBooksEstimateData.ShipAddr = {
                Line1: estimateData.ProjectData?.name || "",
                Line2: estimateData.ProjectData?.address || "",
                City: estimateData.ProjectData?.city || "",
                CountrySubDivisionCode: estimateData.ProjectData?.state || "",
                PostalCode: estimateData.ProjectData?.zip || estimateData.ProjectData?.zip_code || "",
              }
        }
  
        
  
        const apiUrlUpdate = PRODUCTION
        ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
        : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;
    
  
        console.log("QuickBooks Estimate Data:", JSON.stringify(quickBooksEstimateData, null, 2));
        const response = await axios.post(
          // `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate?operation=update`,
          `${apiUrlUpdate}/estimate?operation=update`,
          quickBooksEstimateData,
          {
            headers: {
              Authorization: `Bearer ${token}`,
              'Content-Type': 'application/json',
              Accept: 'application/json'
            },
            timeout: 10000
          }
        );
  
        if (response.status === 200) {
          console.log("Estimate has been successfully updated in QuickBooks:", response.data);
          doc_number = response.data.Estimate.DocNumber;
          return response.data;
        } else {
          console.error("Failed to update the estimate in QuickBooks:", response.data);
          return null;
        }
  
      } catch (error) {
        // DNS resolution error handling with exponential backoff
        if (error.code === 'ENOTFOUND' && retryCount < maxRetries) {
          const delay = baseDelay * Math.pow(backoffFactor, retryCount); // Exponential backoff
          console.log(`DNS lookup failed, retrying in ${delay / 1000} seconds... (Attempt ${retryCount + 1})`);
          await new Promise(resolve => setTimeout(resolve, delay));
          return updateEstimate(retryCount + 1); // Retry
        }
  
        console.error("Error updating estimate in QuickBooks:", error.response ? error.response.data : error.message);
        const faultType = error.response?.data?.Fault?.type;
  
        if (faultType === 'ValidationFault') {
          if (retryCount < maxRetries) {
            const delay = baseDelay * Math.pow(backoffFactor, retryCount); // Exponential backoff for validation faults
            console.log(`Validation fault, retrying update in ${delay / 1000} seconds... (Attempt ${retryCount + 1})`);
            await new Promise(resolve => setTimeout(resolve, delay));
            await updateEstimate(retryCount + 1); // Retry with incremented count
          } else {
            console.error('Max retry attempts reached.');
          }
        } else {
          console.error("Other error updating estimate in QuickBooks:", error.response?.data || error.message);
        }
      }
    };
  
    await updateEstimate();
  };


  const fetchTaxAmountFromEstimateById = async (estimateId) => {
    let token = await getNewAccessToken();
    try {
      const apiUrl = PRODUCTION
        ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/estimate/${estimateId}`
        : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate/${estimateId}`;
  
      const response = await axios.get(apiUrl, {
        headers: {
          Authorization: `Bearer ${token}`,
          'Accept': 'application/json',
        },
      });
  
      if (response.status === 200) {
        // Extract the estimate object
        const estimate = response.data.Estimate;
  
        // Filter for the Sales Tax Line with ItemRef value == 3428
        const salesTaxLine = estimate.Line.find(item => 
          item.SalesItemLineDetail && item.SalesItemLineDetail.ItemRef && item.SalesItemLineDetail.ItemRef.value == '3428'
        );
  
        // Check if the Sales Tax Line exists
        if (salesTaxLine) {
          // Return the sales tax amount directly
          const salesTaxAmount = salesTaxLine.Amount || 0;
          console.log("Sales Tax Amount:", salesTaxAmount);
          return salesTaxAmount;
        } else {
          console.log("Sales Tax Line with ItemRef value 3428 not found.");
          return 0;
        }
      } else {
        console.log("Failed to fetch the estimate.");
        return 0;
      }
    } catch (error) {
      if (error.response) {
        console.error('Error fetching estimate. Status:', error.response.status);
        console.error('Error data:', error.response.data);
      } else {
        console.error('Error message:', error.message);
      }
      return 0;
    }
  };

  
const validateInventoryItemsInQbForPo = async (itemData, qbIdField) => {
  const token = await getNewAccessToken();
  const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;

  try {
    // Directly access the item ID from the itemData object
    const itemId = qbIdField ? itemData[qbIdField] : itemData.quickbookId;  // Dynamically using the qbIdField or quickbookId
    console.log("itemData itemId", itemId);

    if (!itemId) {
      return { status: false, message: "Item ID not found.", itemData };
    }

    const response = await axios.get(apiUrl, {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/text',
      },
      params: {
        query: `SELECT * FROM Item WHERE Id = '${itemId}'`,
      },
    });

    const items = response.data.QueryResponse.Item;

    if (items && items.length > 0) {
      const item = items[0];

      // Check if item is active
      if (!item.Active) {
        console.log(`Item with ID ${itemId} is not active.`);
        return { status: false, message: `Item with ID ${itemId} is not active.`, itemData };
      } else {
        console.log(`Item with ID ${itemId} is active.`);
        return { status: true, message: `Item with ID ${itemId} is active.` };
      }
    } else {
      console.log(`Item with ID ${itemId} not found.`);
      return { status: false, message: `Item with ID ${itemId} not found.`, itemData };
    }

  } catch (err) {
    console.error('Error validating inventory item:', err.response ? err.response.data : err.message);
    return { status: false, message: 'Error validating item.' };
  }
};


const validateInventoryItemsInQb2 = async (invData,qbIdField) => {
  const token = await getNewAccessToken();
  const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;

  try {
    // Loop through inventory data to check each item's existence and status
    for (const itemData of invData) {
      const itemId =qbIdField? itemData[qbIdField] : itemData.quickbookId; // Assuming itemData contains the item id
      console.log("itemData itemId",itemId)
      const response = await axios.get(apiUrl, {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/text',
        },
        params: {
          query: `SELECT * FROM Item WHERE Id = '${itemId}'`,
        },
      });

      const items = response.data.QueryResponse.Item;

      if (items && items.length > 0) {
        const item = items[0];
        
        // Check if item is active
        if (!item.Active) {
          console.log(`Item with ID ${itemId} is not active.`);
          return {status: false, message: `Item with ID ${itemId} is not active.`,itemData};
        } else {
          console.log(`Item with ID ${itemId} is active.`);
          return {status: true, message: `Item with ID ${itemId} is active.`};
        }
      } else {
        console.log(`Item with ID ${itemId} not found.`);
        return {status: false, message: `Item with ID ${itemId} is not active.`,itemData};
      }
    }

    // If all items are valid and active
    console.log('All items are valid and active.');
    return true;
  } catch (err) {
    console.error('Error validating inventory items:', err.response ? err.response.data : err.message);
    return false;
  }
};


const validateVendorInQb = async (vendorId) => {
  const token = await getNewAccessToken(); // Ensure this function gets the correct access token
  const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;

  try {
    // Query to check if the vendor exists by ID
    const response = await axios.get(apiUrl, {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/text',
      },
      params: {
        query: `SELECT * FROM Vendor WHERE Id = '${vendorId}'`,
      },
    });

    const vendors = response.data.QueryResponse.Vendor;

    if (vendors && vendors.length > 0) {
      const vendor = vendors[0];

      // Check if the vendor is active
      if (!vendor.Active) {
        console.log(`Vendor with ID ${vendorId} is not active.`);
        return { status: false, message: `Vendor with ID ${vendorId} is not active.`,removeQbid :true };
      } else {
        console.log(`Vendor with ID ${vendorId} is active.`);
        return { status: true, message: `Vendor with ID ${vendorId} is active.` ,removeQbid:false};
      }
    } else {
      console.log(`Vendor with ID ${vendorId} not found.`,);
      return { status: false, message: `Vendor with ID ${vendorId} not found.`,removeQbid:true };
    }

  } catch (err) {
    console.error('Error validating vendor:', err.response ? err.response.data : err.message);
    return { status: false, message: 'Error validating vendor.',removeQbid:false };
  }
};

const findItemByNameWithLocationCode = async (name) => {
  try {
    let productName = name.replace(/[:\t\n\r]+/g, "-").trim();
    if (productName.length > 95) {
      productName = productName.substring(0, 95);
    }
    let token = await getNewAccessToken();
    const itemName = productName + locationInventoryCode
    const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;

    const response = await axios.get(apiUrl, {
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/text',
      },
      params: {
        query: `SELECT * FROM Item WHERE Name = '${itemName}'`,
      },
    });

    const items = response.data.QueryResponse.Item;

    if (items && items.length > 0) {
      return items[0].Id; // Return the first matching item
    } else {
      console.log(`Item with name ${itemName} not found in QuickBooks.`);
      return null
    }
  } catch (error) {
    console.error('Error finding item by name:', error.response ? error.response.data : error.message);
    return null
  }
};

  // (async () => {
  //   const item = await findItemByNameWithLocationCode('Test service');
  //   console.log("dataaaaaaaaaaaaaaaaaa",item)
  // })();

const validateClientInQb = async (customerId) => {
  const token = await getNewAccessToken(); // Ensure this function gets the correct access token
  const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;

  try {
    // Query to check if the customer exists by ID
    const response = await axios.get(apiUrl, {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/text',
      },
      params: {
        query: `SELECT * FROM Customer WHERE Id = '${customerId}'`,
      },
    });

    const customers = response.data.QueryResponse.Customer;

    if (customers && customers.length > 0) {
      const customer = customers[0];

      // Check if the customer is active
      if (!customer.Active) {
        console.log(`Customer with ID ${customerId} is not active.`);
        return { status: false, message: `Customer with ID ${customerId} is not active.`, removeQbid: true };
      } else {
        console.log(`Customer with ID ${customerId} is active.`);
        return { status: true, message: `Customer with ID ${customerId} is active.`, removeQbid: false };
      }
    } else {
      console.log(`Customer with ID ${customerId} not found.`);
      return { status: false, message: `Customer with ID ${customerId} not found.`, removeQbid: true };
    }

  } catch (err) {
    console.error('Error validating customer:', err.response ? err.response.data : err.message);
    return { status: false, message: 'Error validating customer.', removeQbid: false };
  }
};





// (async () => {
//   const estimate = await findEmployeeIdByName('Devin Castro');
// })()

const findItemByName = async (itemName) => {
  try {
    let token = await getNewAccessToken();

    const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;

    const response = await axios.get(apiUrl, {
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/text',
      },
      params: {
        query: `SELECT * FROM Item WHERE Name = '${itemName}'`,
      },
    });

    const items = response.data.QueryResponse.Item;

    if (items && items.length > 0) {
      return items[0]; // Return the first matching item
    } else {
      console.log(`Item with name ${itemName} not found in QuickBooks.`);
    }
  } catch (error) {
    console.error('Error finding item by name:', error.response ? error.response.data : error.message);
    return null
  }
};

// (async () => {
//   const estimate = await findItemByName('Install Price');
//   console.log(estimate.Id,"estimate")
// })()

const findItemById = async (id) => {
  const token = await getNewAccessToken();
  const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;

  try {
    const response = await axios.get(
      apiUrl,
      {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/text'
        },
        params: {
          query: `SELECT * FROM Item WHERE Id = '${id}'`
        }
      }
    );

    console.log('response.data', response.data);
    const items = response.data.QueryResponse.Item;
    if (items && items.length > 0) {
      return items[0];
    } else {
     console.log(`Item with ID ${id} not found`);
     return null;
    }
  } catch (err) {
    console.error('Error finding item by ID:', id, err.response ? err.response.data : err.message);
     return null;
  }
};
// (async () => {
//   const item = await findItemById('3657');
//   console.log(item,"item");
// })();

const findItemIdByName = async (itemName) => {
  let name =itemName + locationInventoryCode
  try {

    const normalize = (str) => str?.replace(/\s+/g, ' ').trim() || '';
    const trimmedName = normalize(name.trim());

    console.log(trimmedName,"productnameproductnameproductname")
    const token = await getNewAccessToken(); // Await the token retrieval

    const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;

    const response = await axiosRetry(() => axios.get(
      apiUrl,
      {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/text'
        },
        params: {
          query: `SELECT * FROM Item WHERE Name = '${trimmedName + locationInventoryCode}'`
        }
      }
    ));

    const items = response.data.QueryResponse.Item;
    console.log(items, "itemsssssssssssssssssss");

    if (items && items.length > 0) {
      return items[0].Id;
    } else {
      // throw new Error(`Item with name ${name} not found`);
      console.warn(`Warning: Item with name "${name}" not found.`);
      return null;
    }
  } catch (err) {
    // console.error('Error finding item id by name:', name, err.response ? err.response.data : err.message);
    // throw err;
    console.error('Error finding item id by name:', name, err.response ? err.response.data : err.message);
    return null;
  }
};


const updateInvStockInQuickBooks = async (materialProductData) => {
  let token = null;

  const postUpdateMaterial = async (useNewToken = true) => {
    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    try {
      const queryParam = `select * from Item where Id='${materialProductData.quickbookId}'`;
     const searchUrl = PRODUCTION
        ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query?query=${encodeURIComponent(queryParam)}`
        : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query?query=${encodeURIComponent(queryParam)}`;

      // Step 1: Search for the material by name
      const searchResponse = await axios.get(
        searchUrl,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json',
          },
        }
      );

      if (searchResponse.status === 200 && searchResponse.data.QueryResponse.Item && searchResponse.data.QueryResponse.Item.length > 0) {
        const existingMaterial = searchResponse.data.QueryResponse.Item[0];


        // Prepare the updated material product data
        const quickBooksMaterialData = {
          ...existingMaterial,
          QtyOnHand: materialProductData.unit_qty || existingMaterial?.QtyOnHand || 0,
        };

        console.log("QuickBooks Material Data for Update:", JSON.stringify(quickBooksMaterialData, null, 2));

        const updateUrl = PRODUCTION
          ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/item?operation=update&minorversion=4`
          : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/item?operation=update&minorversion=4`;
        // Step 3: Make the update request
        const updateResponse = await axios.post(
          updateUrl,
          quickBooksMaterialData,
          {
            headers: {
              Authorization: `Bearer ${token}`,
              'Content-Type': 'application/json',
              Accept: 'application/json',
            },
          }
        );

        if (updateResponse.status === 200) {
          console.log("Material has been successfully updated in QuickBooks:", updateResponse.data);
          quickbookId = updateResponse.data.Item.Id;
        } else {
          console.error("Failed to update the material in QuickBooks:", updateResponse.data);
        }
      } else {
        console.log(`Material with name '${materialProductData.name}' not found in QuickBooks.`);
      }
    } catch (error) {
      console.error("Error updating material in QuickBooks:", error.response ? error.response.data : error.message);
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        try {
          // Retry the API call with a new access token
          await postUpdateMaterial(true);
        } catch (refreshError) {
          console.error('Error refreshing token:', refreshError.response?.data || refreshError.message);
        }
      } else if (faultType === 'ValidationFault') {
        console.error("Validation error updating material in QuickBooks:", JSON.stringify(error.response?.data, null, 2));
      } else {
        console.error("Other error updating material in QuickBooks:", error.response?.data || error.message);
      }
    }
  };

  await postUpdateMaterial();
};

  module.exports = {
    findItemByNamewithLocationInventoryCode,
    editEstimateInQuickBooks,
    fetchTaxAmountFromEstimateById,
    validateInventoryItemsInQbForPo,
    validateVendorInQb,
    findItemByNameWithLocationCode,
    validateClientInQb,
    findItemByName,
    findItemById,
    findItemIdByName,
    updateInvStockInQuickBooks
  }



  const findEmployeeIdByName = async (employeeName) => {
    const token = await getNewAccessToken();  // Assuming you have a function to get the access token
    
    // URL encode the employee name to handle special characters
    const encodedEmployeeName = encodeURIComponent(employeeName);
    
    const apiUrl = `https://quickbooks.api.intuit.com/v3/company/${realmId}/query?query=SELECT * FROM Employee WHERE DisplayName = '${encodedEmployeeName}'`;
  
    try {
      // Send a GET request to fetch employees using a query
      const response = await axios.get(apiUrl, {
        headers: {
          Authorization: `Bearer ${token}`,
          'Accept': 'application/json',
        },
      });
  
      if (response.status === 200) {
        const employees = response.data.QueryResponse.Employee;
  
        if (!employees || employees.length === 0) {
          console.log("No employees found with the name:", employeeName);
          return null;
        }
  
        // If there are multiple employees with the same name, adjust the logic as needed.
        const matchedEmployee = employees[0];  // Adjust logic if there are multiple matches.
  
        console.log(`Employee found: ${matchedEmployee.DisplayName}`);
        console.log('Employee ID:', matchedEmployee.Id);
        return matchedEmployee.Id;  // Return the employee ID
      } else {
        console.error('Failed to fetch employees: Status:', response.status);
        console.error('Response Data:', response.data);
        return null;
      }
    } catch (error) {
      // Log detailed error response from QuickBooks
      if (error.response) {
        console.error('Error fetching employees:');
        console.error('Status:', error.response.status);
        console.error('Data:', error.response.data);  // Detailed error message from QuickBooks
        console.error('Headers:', error.response.headers);
        
        // Log specific error message from Fault
        if (error.response.data.Fault && error.response.data.Fault.Error) {
          const errorMessage = error.response.data.Fault.Error[0].Message;
          const errorDetail = error.response.data.Fault.Error[0].Detail;
          console.error('QuickBooks Error:', errorMessage);
          console.error('Detail:', errorDetail);
        }
      } else {
        console.error('Error message:', error.message);
      }
      return null;
    }
  };
  
  
  const getEmployees = async () => {
    const token = await getNewAccessToken();  // Assuming you have a function to get the access token
     const apiUrl = `https://quickbooks.api.intuit.com/v3/company/${realmId}/query?query=SELECT * FROM Employee`;
  
    try {
      const response = await axios.get(apiUrl, {
        headers: {
          Authorization: `Bearer ${token}`,
          'Accept': 'application/json',
        },
      });
  
      if (response.status === 200) {
        const employees = response.data.QueryResponse.Employee;
        console.log("Employees:", employees);
        return employees;  // Return the list of employees
      } else {
        console.error('Failed to fetch employees: Status:', response.status);
        console.error('Response Data:', response.data);
        return null;
      }
    } catch (error) {
      console.error('Error fetching employees:');
      if (error.response) {
        console.error('Status:', error.response.status);
        console.error('Data:', error.response.data);  // Detailed error message from QuickBooks
        console.error('Headers:', error.response.headers);
      } else {
        console.error('Error message:', error.message);
      }
      return null;
    }
  };


  const getAllCustomFieldsFromEstimates = async () => {
    const token = await getNewAccessToken();
    // Query to fetch all custom fields from the company, including various categories (Vendor, Transaction, etc.)
    const apiUrl = `https://quickbooks.api.intuit.com/v3/company/${realmId}/query?query=SELECT Id, CustomField FROM Estimate MAXRESULTS 1000`;
  
    try {
      const response = await axios.get(apiUrl, {
        headers: {
          Authorization: `Bearer ${token}`,
          Accept: "application/json",
        },
      });
  
      const estimates = response.data?.QueryResponse?.Estimate || [];
  
      const uniqueCustomFields = new Map();
  
      // Loop through each estimate and gather all custom fields
      estimates.forEach(est => {
        if (est.CustomField) {
          est.CustomField.forEach(field => {
            // Store unique custom fields by their DefinitionId to avoid duplicates
            if (!uniqueCustomFields.has(field.DefinitionId)) {
              uniqueCustomFields.set(field.DefinitionId, {
                DefinitionId: field.DefinitionId,
                Name: field.Name,
                Type: field.Type,
                Category: field.Category,  // Add category information (Vendor, Transaction, etc.)
              });
            }
          });
        }
      });
  
      // Log all unique custom fields in the account
      console.log("All Custom Fields in Account:");
      console.log([...uniqueCustomFields.values()]);
  
      return [...uniqueCustomFields.values()];
    } catch (error) {
      console.error("Error fetching custom fields:");
      console.error(error.response?.data || error.message);
      return [];
    }
  };
  
  const findCustomFieldDefinitionId = async (fieldName) => {
    const token = await getNewAccessToken();
  
    const apiUrl = `https://quickbooks.api.intuit.com/v3/company/${realmId}/query?query=SELECT Id, CustomField FROM Estimate MAXRESULTS 100`;
  
    try {
      const response = await axios.get(apiUrl, {
        headers: {
          Authorization: `Bearer ${token}`,
          Accept: "application/json",
        },
      });
  
      const estimates = response.data?.QueryResponse?.Estimate || [];
  
      for (const est of estimates) {
        if (est.CustomField) {
          const match = est.CustomField.find(
            field => field.Name?.toLowerCase() === fieldName.toLowerCase()
          );
  
          if (match) {
            console.log("✅ Found Custom Field:");
            console.log("Name:", match.Name);
            console.log("DefinitionId:", match.DefinitionId);
            console.log("Type:", match.Type);
  
            return match.DefinitionId;
          }
        }
      }
  
      console.log(`❌ Custom field "${fieldName}" not found via API.`);
      return null;
  
    } catch (error) {
      console.error("Error fetching custom fields:");
      console.error(error.response?.data || error.message);
      return null;
    }
  };
  
  
  // (async () => {
  //   const estimate = await getAllCustomFieldsFromEstimates("Sales Rep");
  // })()


  const findCustomerIdByName = async (customerName) => {
    let name =customerName + locationInventoryCode
    try {
      // Ensure valid access token
      let token = await getNewAccessToken();
  
      const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query`;
  
  
      const response = await axios.get(
        apiUrl,
        {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/text'
          },
          params: {
            query: `SELECT * FROM Customer WHERE DisplayName = '${name.trim()}'`
          }
        }
      );
  
      const customers = response.data.QueryResponse.Customer;
  
      if (customers && customers.length > 0) {
        return customers[0].Id;
      } 
    } catch (err) {
      console.error('Error finding customer by name:', err.response ? err.response.data : err.message);
      
    }
  };


  const addSalesOrderToQuickBooks = async (salesOrderData) => {
    let token = await getNewAccessToken();
  
    const postSalesOrder = async (useNewToken = false, retryCount = 0) => {
      const maxRetries = 5; // Maximum number of retries
      const backoffFactor = 2; // Exponential backoff factor
      const baseDelay = 1000; // Initial delay of 1 second
  
      if (useNewToken) {
        console.log('Refreshing access token...');
        token = await getNewAccessToken();
      }
  
      try {
        // Ensure all items have valid inventory start dates
        for (let item of salesOrderData.items) {
          // const itemDetails = await findItemByName(item.name);
          const itemDetails = await findItemById(item.itemId);
          item.inventoryStartDate = itemDetails.InventoryStartDate;
        }
  
        // Use the latest inventory start date to set the transaction date
        // const txnDate = moment.max(salesOrderData.items.map(item => moment(item.inventoryStartDate)))
        //   .add(10, 'days')
        //   .format('YYYY-MM-DD');
        const txnDate = moment().format('YYYY-MM-DD');
  
  
        const discountAccounts = await getAccounts('Income', 'DiscountsRefundsGiven');
        const dynamicDiscountAccRefId = discountAccounts?.length > 0 ? discountAccounts[0].Id : null;
  
        const discountAccId= dynamicDiscountAccRefId || discountAccRefId
  
        const quickBooksSalesOrderData = {
          CustomerRef: { value: salesOrderData.customerId },
          TxnDate: txnDate,
          // TxnType: "SalesOrder",
          Line: [
            ...salesOrderData.items.map(item => ({
              Amount: parseFloat(item.amount.toFixed(2)), // Ensure proper precision
              DetailType: "SalesItemLineDetail",
              SalesItemLineDetail: {
                ItemRef: { value: item.itemId },
                Qty: item.quantity,
                UnitPrice: parseFloat(item.unitPrice), // Ensure proper precision
                TaxCodeRef: { value: 'TAX' } 
  
              }
            })),
            {
              Amount: salesOrderData.discount, // Apply a fixed discount amount
              DetailType: "DiscountLineDetail",
              DiscountLineDetail: {
                DiscountAccountRef: { value: discountAccId }, // Your Discount Account ID
              },
            },
          ],
          PrivateNote: salesOrderData.note,
          TxnTaxDetail: {
            TxnTaxCodeRef: { value: 'TAX' },
            TotalTax: salesOrderData.taxAmount // Set the total tax amount here
          },
          CustomField: [
            {
              DefinitionId: "2", // Replace this with your actual custom field DefinitionId
              Name: "Job Type", // The name of your custom field
              Type: "StringType",
              StringValue: salesOrderData.jobType // The value you want to set in the custom field
            },
            {
              DefinitionId: "1", // Replace this with your actual custom field DefinitionId
              Name: "No Charge Grand Total", // The name of your custom field
              Type: "StringType",
              StringValue: salesOrderData.customGrandTotal // The value you want to set in the custom field
            },
          
          ],
          ...(salesOrderData.email ? { BillEmail: { Address: salesOrderData.email } } : {}),
          ...(salesOrderData.termsId ? { TermsRef: { value: salesOrderData.termsId } } : {}),
          DueDate: moment(txnDate).add(salesOrderData.termsDays, 'days').format('YYYY-MM-DD')
        };
  
        console.log("QuickBooks salesorder Data:", JSON.stringify(quickBooksSalesOrderData, null, 2));
  
        const apiUrl = PRODUCTION
        ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/salesorder`
        : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/salesorder`;
  
        const response = await axios.post(
          apiUrl,
          quickBooksSalesOrderData,
          {
            headers: {
              Authorization: `Bearer ${token}`,
              'Content-Type': 'application/json',
              Accept: 'application/json',
            },
            timeout: 30000, // Set a 30 seconds timeout
          }
        );
  
        if (response.status === 200) {
          console.log("Sales order has been successfully added to QuickBooks:", response.data);
          console.log("Unique Sales order ID:", response.data.SalesOrder.Id);
          console.log("Document Number:", response.data.SalesOrder.DocNumber);
          UniqueEstimateId = response.data.SalesOrder.Id;
          doc_number = response.data.SalesOrder.DocNumber;
  
          return response.data;
        } else {
          console.error("Failed to add the sales order to QuickBooks:", JSON.stringify(response.data, null, 2));
          return null;
        }
      } catch (error) {
        // Retry logic on DNS resolution errors
        if (error.code === 'ENOTFOUND' && retryCount < maxRetries) {
          const delay = baseDelay * Math.pow(backoffFactor, retryCount); // Exponential backoff
          console.log(`DNS lookup failed, retrying in ${delay / 1000} seconds... (Attempt ${retryCount + 1})`);
          await new Promise(resolve => setTimeout(resolve, delay));
          return postSalesOrder(useNewToken, retryCount + 1); // Retry with incremented count
        }
  
        console.error("Error adding sales order to QuickBooks:", error.response ? error.response.data : error.message);
        const faultType = error.response?.data?.Fault?.type;
        if (faultType === 'AUTHENTICATION') {
          console.log('Access token expired, refreshing token...');
          return postSalesOrder(true); // Retry with a new access token
        } else if (faultType === 'ValidationFault') {
          console.error("Validation error adding sales order to QuickBooks:", JSON.stringify(error.response?.data, null, 2));
        } else {
          console.error("Other error adding sales order to QuickBooks:", error.response?.data || error.message);
        }
      }
    };
  
    await postSalesOrder();
  };


  const fetchAllIncomeAccounts = async () => {
    try {
      const token = await getNewAccessToken(); // Replace with your token retrieval logic
      const query = "SELECT Id, Name FROM Account WHERE AccountType='Income'";
  
      const response = await axios.get(
        `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query?query=${encodeURIComponent(query)}`,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json',
          },
        }
      );
  
      const accounts = response.data.QueryResponse.Account;
  
      if (accounts && accounts.length > 0) {
        accounts.forEach(account => {
          console.log(`Account Name: ${account.Name}, Account ID: ${account.Id}`);
        });
      } else {
        console.error("No income accounts found.");
      }
    } catch (error) {
      console.error("Error fetching accounts from QuickBooks:", error.response ? error.response.data : error.message);
    }
  };
  
  // Example usage
  // fetchAllIncomeAccounts();