<?php

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles export and synchronization of WooCommerce products with Zoho Inventory.
 *
 * Provides methods for syncing simple, variable, and bundle products, as well as updating product data between WooCommerce and Zoho Inventory.
 */
class CMBIRD_Products_ZI_Export {

	/**
	 * Configuration options for Zoho Inventory and plugin settings.
	 *
	 * @var array
	 */
	private $config;

	/**
	 * Constructor for CMBIRD_Products_ZI_Export.
	 *
	 * Initializes configuration options for Zoho Inventory and plugin settings.
	 */
	public function __construct() {
		$this->config = array(
			'ProductZI' => array(
				'OID'    => get_option( 'cmbird_zoho_inventory_oid' ),
				'APIURL' => get_option( 'cmbird_zoho_inventory_url' ),
				'Domain' => get_option( 'cmbird_zoho_inventory_domain' ),
			),
			'Settings'  => array(
				'disable_description'     => get_option( 'cmbird_zoho_disable_description_sync_status' ),
				'disable_name'            => get_option( 'cmbird_zoho_disable_name_sync_status' ),
				'disable_price'           => get_option( 'cmbird_zoho_disable_price_sync_status' ),
				'disable_stock'           => get_option( 'cmbird_zoho_disable_stock_sync_status' ),
				'enable_accounting_stock' => get_option( 'cmbird_zoho_enable_accounting_stock_status' ),
				'enable_location_stock'   => get_option( 'cmbird_zoho_enable_locationstock_status' ),
				'zoho_location_id'        => get_option( 'cmbird_zoho_location_id_status' ),
				'disable_image'           => get_option( 'cmbird_zoho_disable_image_sync_status' ),
			),
		);
	}

	/**
	 * Prepares products for Zoho Inventory sync.
	 *
	 * @param array $product_ids List of WooCommerce product IDs to sync.
	 *
	 * @return void
	 */
	public function cmbird_zi_products_prepare_sync( $product_ids ) {
		$rate_limit = get_option( 'cmbird_zoho_rate_limit_exceeded' );
		if ( is_array( $product_ids ) ) {
			// schedule the unsynced products for tomorrow via action scheduler.
			if ( $rate_limit ) {
				$timestamp = strtotime( 'tomorrow' );
				// set the zoho rate limit option exceeded to false tomorrow if not scheduled yet.
				if ( ! wp_next_scheduled( 'cmbird_common' ) ) {
					wp_schedule_single_event( $timestamp, 'cmbird_common' );
				}
				// Get all scheduled actions with the specific hook name.
				$action_ids = as_get_scheduled_actions(
					array(
						'hook'     => 'sync_zi_product_cron',
						'status'   => ActionScheduler_Store::STATUS_PENDING,
						'per_page' => -1,
					)
				);
				// Loop through each action and reschedule it.
				if ( ! empty( $action_ids ) ) {
					foreach ( $action_ids as $action_id ) {
						// Fetch the action by ID.
						$action = as_get_scheduled_action( $action_id );
						// If the action is valid and exists, reschedule it.
						if ( $action ) {
							// Reschedule the action to run tomorrow.
							as_schedule_single_action( $timestamp, 'sync_zi_product_cron', $action->get_args(), 'ActionScheduler' );
							// Cancel the old action to avoid duplicates.
							as_unschedule_action( 'sync_zi_product_cron', $action->get_args(), $action_id );
						}
					}
				}
			} else {
				foreach ( $product_ids as $product_id ) {
					$this->cmbird_zi_product_sync( $product_id );
				}
			}
		}
	}

	/**
	 * Starting point to sync Product to Zoho Inventory. If product type is other than simple, pass the product_id to different function
	 *
	 * @param integer $post_id - Product id.
	 * @return string|void - item_id of Zoho Inventory.
	 */
	public function cmbird_zi_product_sync( $post_id ) {

		// $fd      = fopen( __DIR__ . '/product_class.txt', 'a+' );
		$item_id = '';

		if ( is_array( $post_id ) ) {
			$product_id = intval( $post_id['0'] );
			$post_id    = $product_id;
		}

		if ( 'draft' === get_post_status( $post_id ) ) {
			return;
		}

		$product = wc_get_product( $post_id );
		if ( ! $product ) {
			return;
		}
		if ( $product->is_type( 'bundle' ) ) {
			// fwrite($fd,PHP_EOL.'Inside bundle: ');
			$item_id = $this->zi_bundle_product_to_zoho( $post_id );
			return $item_id;
		} elseif ( $product->is_type( 'variable' ) ) {
			// fwrite( $fd, PHP_EOL . 'Inside variable: ' );
			$this->cmbird_zi_variation_product_to_zoho( $post_id );
		} elseif ( $product->is_type( 'variation' ) ) {
			return;
		} else {
			// fwrite($fd,PHP_EOL.'Inside Regular: ');
			// Simple product.
			$rate     = $product->get_regular_price();
			$pre_name = $product->get_name();
			$name     = sanitize_text_field( $pre_name );
			// remove any remaining emoji/4‑byte characters that sanitize_text_field doesn't strip.
			$name           = preg_replace( '/[\x{1F300}-\x{1F6FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}]/u', '', $name );
			$stock_quantity = $product->get_stock_quantity();
			$in_stock       = ( $stock_quantity > 0 ) ? $stock_quantity : 0;
			// fwrite($fd,PHP_EOL.'$product->get_stock_quantity() : '.$product->get_stock_quantity());
			$in_stock_rate = $in_stock * (int) $rate;
			$sku           = $product->get_sku();
			$tax_rates     = WC_Tax::get_base_tax_rates( $product->get_tax_class() );
			$tax_id_key    = '';
			foreach ( $tax_rates as $tax_key => $tax_value ) {
				$tax_id_key = $tax_key;
				break;
			}
			$tax_option = get_option( 'cmbird_zoho_inventory_tax_rate_' . $tax_id_key );
			$tax_id     = explode( '##', $tax_option )[0];

			$zi_status = ( 'publish' === get_post_status( $post_id ) ) ? 'active' : 'inactive';
			// request data for adding/updating value to zoho.
			$zi_disable_item_name_sync = get_option( 'cmbird_zoho_disable_name_sync_status' );
			$zoho_item_id              = get_post_meta( $post_id, 'zi_item_id', true );

			$zidata = '';
			if ( empty( $zoho_item_id ) || 'true' !== $zi_disable_item_name_sync ) {
				$zidata .= '"name" : "' . $name . '",';
			}

			if ( $product->is_virtual( 'yes' ) ) {
				$zidata .= '"product_type" : "service",';
				$zidata .= '"item_type" : "sales",';
			} else {
				$zidata .= '"product_type" : "goods",';
				$zidata .= '"item_type" : "inventory",';
			}

			$zidata .= '"sku" : "' . $sku . '",';
			// $zidata .= '"unit" : "pcs",';
			$zidata .= '"status" : "' . $zi_status . '",';
			// Initial stock update only if item sync for first time.
			if ( empty( $zoho_item_id ) ) {
				$zidata .= '"initial_stock" : ' . $in_stock . ',';
				$zidata .= '"initial_stock_rate" : "' . $in_stock_rate . '",';
			}
			$zidata .= '"rate" : "' . $rate . '",';
			$domain  = isset( $this->config['ProductZI']['Domain'] ) ? $this->config['ProductZI']['Domain'] : '';
			if ( 'in' !== strtolower( $domain ) && $tax_id ) {
				$zidata .= '"tax_id" : "' . $tax_id . '",';
			} elseif ( 'in' === strtolower( $domain ) ) {
				// For India domain, find GST and IGST tax IDs dynamically, but only if tax rate > 0%.
				$intra_tax_id      = ''; // For SGST&CGST (intra-state).
				$inter_tax_id      = ''; // For IGST (inter-state).
				$has_non_zero_rate = false;
				$tax_rate_percent  = 0;

				// First, check if we have any non-zero tax rates and get the rate percentage.
				foreach ( $tax_rates as $tax_key => $tax_value ) {
					if ( isset( $tax_value['rate'] ) && floatval( $tax_value['rate'] ) > 0 ) {
						$has_non_zero_rate = true;
						$tax_rate_percent  = floatval( $tax_value['rate'] );
						break; // Use the first non-zero rate found.
					}
				}

				// If we have non-zero rates, find the corresponding Zoho tax IDs for the same rate.
				if ( $has_non_zero_rate ) {
					// Look for tax options that match this rate percentage.
					global $wpdb;
					$all_tax_options = $wpdb->get_results(
						"SELECT option_name, option_value 
						FROM {$wpdb->options} 
						WHERE option_name LIKE 'cmbird_zoho_inventory_tax_rate_%'"
					);

					foreach ( $all_tax_options as $option ) {
						$option_str = $option->option_value;
						// Example: "2946885000000035251##SGST&CGST12##tax_group##12".
						$parts = explode( '##', $option_str );
						if ( isset( $parts[1] ) && isset( $parts[3] ) ) {
							$option_rate = floatval( $parts[3] );

							// Only consider options with the same tax rate percentage.
							if ( $option_rate === $tax_rate_percent ) {
								// Check for intra-state tax (SGST&CGST combined):
								// Look for "GST" without "IGST", or explicit "SGST" and "CGST" mentions.
								if ( ( false !== stripos( $parts[1], 'GST' ) && false === stripos( $parts[1], 'IGST' ) ) ||
									( false !== stripos( $parts[1], 'SGST' ) && false !== stripos( $parts[1], 'CGST' ) ) ) {
									$intra_tax_id = $parts[0];
								}
								// Check for IGST (inter-state) - contains "IGST".
								if ( false !== stripos( $parts[1], 'IGST' ) ) {
									$inter_tax_id = $parts[0];
								}
							}
						}
					}

					// Add both intra and inter tax preferences if we found the tax IDs.
					$item_tax_preferences = array();
					if ( ! empty( $intra_tax_id ) ) {
						$item_tax_preferences[] = array(
							'tax_id'            => $intra_tax_id,
							'tax_specification' => 'intra',
						);
					}
					if ( ! empty( $inter_tax_id ) ) {
						$item_tax_preferences[] = array(
							'tax_id'            => $inter_tax_id,
							'tax_specification' => 'inter',
						);
					}
					if ( ! empty( $item_tax_preferences ) ) {
						$zidata .= '"item_tax_preferences" : ' . wp_json_encode( $item_tax_preferences ) . ',';
					}
				} else {
					$zidata .= '"is_taxable" : false,';
					$zidata .= '"tax_exemption_code" : "exempt",';
				}
			}
				// $zidata .= '"image_name" : "' . $image . '",';

			// Get cost_price from meta data.
			$cost_price = $product->get_meta( '_cogs_total_value' );
			if ( ! empty( $cost_price ) && is_numeric( $cost_price ) ) {
				$zidata .= '"purchase_rate" : "' . $cost_price . '",';
			}

				$dimensions         = (object) array();
				$dimensions->length = $product->get_length();
				$dimensions->width  = $product->get_width();
				$dimensions->height = $product->get_height();
				$dimensions->weight = $product->get_weight();
				$zidata            .= '"package_details" : ' . wp_json_encode( $dimensions ) . '';

				// Send category only if category ID available.
				$zi_category_id = $this->cmbird_zi_get_prod_updated_category( $post_id );
			if ( $zi_category_id ) {
				$zidata .= ',"category_id" : "' . $zi_category_id . '"';
			}

				// save the request body for debugging purposes.
				$zi_body_request = '{' . $zidata . '}';
				update_post_meta( $post_id, 'zi_product_body_request', $zi_body_request );

				// $zidata .= '"image_type" : "' . $ext . '"';
			if ( ! empty( $zoho_item_id ) && ctype_digit( $zoho_item_id ) ) {
				// fwrite($fd,PHP_EOL.'Inside Update: ');
				$this->cmbird_zi_product_put( $post_id, $zoho_item_id, $zidata );
			} else {
				// fwrite($fd,PHP_EOL.'Inside Create ');
				$zoho_inventory_oid = $this->config['ProductZI']['OID'];
				$zoho_inventory_url = $this->config['ProductZI']['APIURL'];

				$data = array(
					'JSONString'      => '{' . $zidata . '}',
					'organization_id' => $zoho_inventory_oid,
				);
				$url  = $zoho_inventory_url . 'inventory/v1/items';

				$execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
				$json                     = $execute_curl_call_handle->execute_curl_call_post( $url, $data );

				$errmsg = $json->message;
				update_post_meta( $post_id, 'zi_product_errmsg', $errmsg );

				$code = $json->code;
				// fwrite($fd,PHP_EOL.'JSON Response : '.print_r($json,true));
				// Check if the the given sku has product at zoho inventory.
				if ( '1001' === $code || 1001 === $code ) {
					// fwrite($fd,PHP_EOL.'Inside SKU Check');
					$sku_check   = str_replace( ' ', '+', $sku );
					$url         = $zoho_inventory_url . 'inventory/v1/items?search_text=' . $sku_check . '&organization_id=' . $zoho_inventory_oid;
					$get_request = $execute_curl_call_handle->execute_curl_call_get( $url );

					if ( '0' === $get_request->code || 0 === $get_request->code ) {
						$item_id       = '';
						$matching_item = null;

						foreach ( $get_request->items as $zoho_item ) {
							if ( $zoho_item->sku === $sku ) {
								// SKU matched.
								$matching_item = $zoho_item;
								break;
							}
						}

						// If SKU check didn't find a match, perform name check.
						if ( ! $matching_item ) {
							$item_name_check = str_replace( ' ', '+', $name );
							$url             = $zoho_inventory_url . 'inventory/v1/items?search_text=' . $item_name_check . '&organization_id=' . $zoho_inventory_oid;
							$get_request     = $execute_curl_call_handle->execute_curl_call_get( $url );

							if ( '0' === $get_request->code || 0 === $get_request->code ) {
								foreach ( $get_request->items as $zoho_item ) {
									if ( $zoho_item->name === $name ) {
										// Name matched.
										$matching_item = $zoho_item;
										break;
									}
								}
							}
						}

						if ( $matching_item ) {
							$code       = 0;
							$json->item = $matching_item;
							update_post_meta( $post_id, 'zi_product_errmsg', 'Product "' . $matching_item->name . '" is mapped successfully with Zoho' );
						}
					}
				}
				// fwrite($fd,PHP_EOL.'After SKU Check : code '.$code);
				if ( '0' === $code || 0 === $code ) {
					foreach ( $json->item as $key => $value ) {
						if ( 'item_id' === $key ) {
							$item_id = $value;
						}
						if ( 'purchase_account_id' === $key ) {
							$purchase_account_id = $value;
						}
						if ( 'account_id' === $key ) {
							$account_id = $value;
						}
						if ( 'account_name' === $key ) {
							$account_name = $value;
						}
						if ( 'inventory_account_id' === $key ) {
							$inventory_account_id = $value;
						}
						if ( 'category_id' === $key && ! empty( $value ) ) {
							update_post_meta( $post_id, 'zi_category_id', $value );
						}
					}
					update_post_meta( $post_id, 'zi_item_id', $item_id );
					update_post_meta( $post_id, 'zi_purchase_account_id', $purchase_account_id );
					update_post_meta( $post_id, 'zi_account_id', $account_id );
					update_post_meta( $post_id, 'zi_account_name', $account_name );
					update_post_meta( $post_id, 'zi_inventory_account_id', $inventory_account_id );
				}
			}
		}
		return $item_id;
	}

	/**
	 * Function to update zoho item if already exists.
	 *
	 * @param number $proid - product number.
	 * @param number $item_id - zoho item id.
	 * @param mixed  $pdt3 - Zoho item object for post request.
	 * @return string
	 */
	public function cmbird_zi_product_put( $proid, $item_id, $pdt3 = '' ) {
		// $fd = fopen(__DIR__.'/product_class.txt','a+');
		// fwrite($fd,PHP_EOL.'Inside update : ');
		$errmsg             = '';
		$zoho_inventory_oid = $this->config['ProductZI']['OID'];
		$zoho_inventory_url = $this->config['ProductZI']['APIURL'];

		$url = $zoho_inventory_url . 'inventory/v1/items/' . $item_id;
		// fwrite($fd,PHP_EOL.'JSON Data : '.'{' . $pdt3 . '}');
		$data = array(
			'JSONString'      => '{' . $pdt3 . '}',
			'organization_id' => $zoho_inventory_oid,
		);

		// if pdt3 is empty then do GET call, else do PUT call.
		if ( empty( $pdt3 ) ) {
			$execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
			$json                     = $execute_curl_call_handle->execute_curl_call_get( $url );
			$code                     = $json->code;
			$errmsg                   = $json->message;
		} else {
			$execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
			$json                     = $execute_curl_call_handle->execute_curl_call_put( $url, $data );
			$code                     = $json->code;
			$errmsg                   = $json->message;
		}

		if ( 0 === $code || '0' === $code ) {
			$product = wc_get_product( $proid );
			// if type is not simple then return.
			if ( ! $product->is_type( 'simple' ) ) {
				return $errmsg;
			}
			$item = $json->item;
			// update price.
			$zi_disable_itemprice_sync = $this->config['Settings']['disable_price'];
			if ( ! empty( $item->rate ) && ! $zi_disable_itemprice_sync ) {
				$product->set_regular_price( $item->rate );
				$sale_price = $product->get_sale_price();
				if ( empty( $sale_price ) ) {
					$product->set_price( $item->rate );
				}
			}
			// To check status of stock sync option.
			$zi_disable_stock_sync = $this->config['Settings']['disable_stock'];
			if ( ! $zi_disable_stock_sync && isset( $item->available_for_sale_stock ) ) {
				$stock = '';
				// Update stock.
				$accounting_stock = $this->config['Settings']['enable_accounting_stock'];
				// Sync from specific location check.
				$zi_enable_locationstock = $this->config['Settings']['enable_location_stock'];
				if ( $zi_enable_locationstock && isset( $item->locations ) ) {
					$locations   = $item->locations;
					$location_id = $this->config['Settings']['zoho_location_id'];
					foreach ( $locations as $location ) {
						if ( $location->location_id === $location_id ) {
							if ( $accounting_stock ) {
								$stock = $location->location_available_for_sale_stock;
							} else {
								$stock = $location->location_actual_available_for_sale_stock;
							}
						}
					}
				} elseif ( $accounting_stock ) {
					$stock = $item->available_for_sale_stock;
				} else {
					$stock = $item->actual_available_for_sale_stock;
				}

				if ( is_numeric( $stock ) ) {
					$product->set_manage_stock( true );
					$product->set_stock_quantity( number_format( $stock, 0, '.', '' ) );
					if ( $stock > 0 ) {
						$product->set_stock_status( 'instock' );
					} else {
						$backorder_status = $product->backorders_allowed();
						$status           = ( 'yes' === $backorder_status ) ? 'onbackorder' : 'outofstock';
						$product->set_stock_status( $status );
					}
				}
			}
			$product->save();
			update_post_meta( $proid, 'zi_product_errmsg', $errmsg );
		} else {
			update_post_meta( $proid, 'zi_product_errmsg', $errmsg );
		}
		// fclose($fd);
		return $errmsg;
	}

	protected function cmbird_zi_bundle_product_data_zoho( $bundle_id ) {
		// $fd = fopen(__DIR__ . '/cmbird_zi_bundle_product_data_zoho.txt', 'w+');

		$bundled_product = new WC_Product_Bundle( $bundle_id );
		$bundle_childs   = $bundled_product->get_bundled_items();

		// Allow Bundle Product.
		$child_array = array();
		foreach ( $bundle_childs as $child ) {
			$parent_product = $child->product;
			$child_id       = $child->product_id;
			$meta_value     = WC_PB_DB::get_bundled_item_meta( $child_id, 'quantity_max' );
			$zi_child_ids   = array(); // Array to store zi_child_ids.

			if ( $parent_product->is_type( 'variable' ) ) {
				$meta_data = WC_PB_DB::get_bundled_item_meta( $child_id, 'allowed_variations' );

				foreach ( $meta_data as $meta ) {
					if ( $meta->meta_key === 'allowed_variations' ) {
						$serialized_value   = $meta->meta_value;
						$deserialized_value = maybe_unserialize( $serialized_value );

						if ( is_array( $deserialized_value ) ) {
							foreach ( $deserialized_value as $variation_id ) {
								$zi_variation_id = get_post_meta( $variation_id, 'zi_item_id', true );
								if ( $zi_variation_id ) {
									$zi_child_ids[] = $zi_variation_id;
								}
							}
						}
					}
				}
			} else {
				$zi_child_id = get_post_meta( $child_id, 'zi_item_id', true );
				if ( $zi_child_id ) {
					$zi_child_ids[] = $zi_child_id;
				} else {
					// sync the child product to zoho inventory if not synced already and get the item id.
					$zi_child_id    = $this->cmbird_zi_product_sync( $child_id );
					$zi_child_ids[] = $zi_child_id;
				}
			}

			foreach ( $zi_child_ids as $zi_child_id ) {
				$json_child = (object) array(
					'item_id'  => $zi_child_id,
					'quantity' => $meta_value[0]->meta_value,
				);
				array_push( $child_array, $json_child );
			}
		}
		$child_items = $child_array;

		// fclose($fd);
		return $child_items;
	}

	/**
	 * Create a bundle product on Zoho Inventory.
	 *
	 * @param int $post_id The ID of the product to be created on Zoho Inventory.
	 *
	 * @return string The item ID of the created bundle product on Zoho Inventory.
	 */
	protected function zi_bundle_product_to_zoho( $post_id ) {
		// $fd = fopen(__DIR__ . '/zi_bundle_product_to_zoho.txt', 'w+');

		$item = wc_get_product( $post_id );
		if ( $item->is_type( 'bundle' ) ) {

			$child_items = $this->cmbird_zi_bundle_product_data_zoho( $post_id );
		}

		$price_r = $item->get_regular_price();
		$price_s = $item->get_sale_price();

		if ( $price_s ) {
			$rate = round( $price_s, 2 );
		} else {
			$rate = round( $price_r, 2 );
		}
		// $rate = 500;
		// $proid = $item->ID;
		$pre_name = $item->get_name();
		$name     = sanitize_text_field( $pre_name );
		// remove any remaining 4‑byte Unicode characters (covers all emojis).
		$name           = preg_replace( '/[\x{10000}-\x{10FFFF}]/u', '', $name );
		$sku            = $item->get_sku();
		$stock_quantity = $item->get_stock_quantity();
		$in_stock       = ( $stock_quantity > 0 ) ? $stock_quantity : 0;
		$in_stock_rate  = ( $in_stock * $rate );

		$product_type = 'goods';
		$item_type    = 'inventory';
		$tax_rates    = WC_Tax::get_base_tax_rates( $item->get_tax_class() );
		$tax_id_key   = '';
		foreach ( $tax_rates as $tax_key => $tax_value ) {
			$tax_id_key = $tax_key;
			break;
		}
		$tax_option = get_option( 'cmbird_zoho_inventory_tax_rate_' . $tax_id_key );
		$tax_id     = explode( '##', $tax_option )[0];
		if ( ! empty( $tax_rates ) ) {
			$tax_rate = reset( $tax_rates );
		}

		$pdt1 = '"name" : "' . $name . '","mapped_items":' . wp_json_encode( $child_items ) . ', "product_type" : "' . $product_type . '","tax_id" : "' . $tax_id . '","rate" : "' . $rate . '","sku" : "' . $sku . '","item_type" : "' . $item_type . '"';
		// If zoho category id is not mapped to product, then assign mapped product category with zoho.

		// $zi_category_id = $this->cmbird_zi_get_prod_updated_category($post_id);
		// if ($zi_category_id) {.
		// $pdt1 .= ',"category_id" : "' . $zi_category_id . '"';
		// }.

		$zoho_item_id = get_post_meta( $post_id, 'zi_item_id', true );
		if ( empty( $zoho_item_id ) ) {
			$pdt1 .= ',"initial_stock" : ' . $in_stock . ',';
			$pdt1 .= '"initial_stock_rate" : "' . $in_stock_rate . '"';
		}

		// Dimensions data append to update call.
		$dimensions         = (object) array();
		$dimensions->length = $item->get_length();
		$dimensions->width  = $item->get_width();
		$dimensions->height = $item->get_height();
		$dimensions->weight = $item->get_weight();
		$pdt1              .= ',"package_details" : ' . wp_json_encode( $dimensions ) . ',';

		// save the request body for debugging purposes.
		$zi_body_request = '{' . $pdt1 . '}';
		update_post_meta( $post_id, 'zi_product_body_request', $zi_body_request );

		$zoho_inventory_oid = $this->config['ProductZI']['OID'];
		$zoho_inventory_url = $this->config['ProductZI']['APIURL'];

		if ( $zoho_item_id && ctype_digit( $zoho_item_id ) ) {
			$url_p = $zoho_inventory_url . 'inventory/v1/compositeitems/' . $zoho_item_id;
		} else {
			$url_p = $zoho_inventory_url . 'inventory/v1/compositeitems';
		}

		$data_p = array(
			'JSONString'      => '{' . $pdt1 . '}',
			'organization_id' => $zoho_inventory_oid,
		);

		// fwrite($fd, PHP_EOL . 'data_p : ' . print_r($data_p, true));

		$execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();

		if ( $zoho_item_id && ctype_digit( $zoho_item_id ) ) {

			$json   = $execute_curl_call_handle->execute_curl_call_put( $url_p, $data_p );
			$errmsg = $json->message;
			update_post_meta( $post_id, 'zi_product_errmsg', $errmsg );
		} else {

			$json = $execute_curl_call_handle->execute_curl_call_post( $url_p, $data_p );

			$code   = $json->code;
			$errmsg = $json->message;
			update_post_meta( $post_id, 'zi_product_errmsg', $errmsg );
			if ( '1001' == $code || 1001 == $code ) {
				$sku_check   = str_replace( ' ', '+', $sku );
				$url         = $zoho_inventory_url . 'inventory/v1/compositeitems/?search_text=' . $sku_check . '&organization_id=' . $zoho_inventory_oid;
				$get_request = $execute_curl_call_handle->execute_curl_call_get( $url );
				if ( '0' === $get_request->code || 0 === $get_request->code ) {
					$item_id = '';
					foreach ( $get_request->composite_items as $zoho_composite ) {
						// fwrite($fd,PHP_EOL.'ZOHO Item : '.print_r($zoho_item, true));
						if ( $zoho_composite->sku === $sku ) {
							$code                 = 0;
							$json->composite_item = $zoho_composite;
							update_post_meta( $post_id, 'zi_product_errmsg', 'Product "' . $zoho_composite->name . '" is mapped successfully with Zoho' );
							break;
						}
					}
				}
			}
			if ( '0' === $code || 0 === $code ) {
				foreach ( $json->composite_item as $key => $value ) {

					if ( 'composite_item_id' === $key ) {
						$item_id = $value;
					}
					if ( 'purchase_account_id' === $key ) {
						$purchase_account_id = $value;
					}
					if ( 'account_id' === $key ) {
						$account_id = $value;
					}
					if ( 'account_name' === $key ) {
						$account_name = $value;
					}
					if ( 'inventory_account_id' === $key ) {
						$inventory_account_id = $value;
					}
					if ( 'category_id' === $key && ! empty( $value ) ) {
						update_post_meta( $post_id, 'zi_category_id', $value );
					}
				}
				update_post_meta( $post_id, 'zi_item_id', $item_id );
				update_post_meta( $post_id, 'zi_purchase_account_id', $purchase_account_id );
				update_post_meta( $post_id, 'zi_account_id', $account_id );
				update_post_meta( $post_id, 'zi_account_name', $account_name );
				update_post_meta( $post_id, 'zi_inventory_account_id', $inventory_account_id );
			}
		}
		// fclose($fd);
		return $item_id;
	}

	/**
	 * Pushes the variation product to Zoho Inventory.
	 *
	 * @param int $post_id The post ID of the product.
	 *
	 * @return void
	 */
	protected function cmbird_zi_variation_product_to_zoho( $post_id ) {
		// $fd = fopen( __DIR__ . '/cmbird_zi_variation_product_to_zoho.txt', 'a+' );

		$product = wc_get_product( $post_id );
		if ( ! $product ) {
			return;
		}
		$zoho_group_id = $product->get_meta( 'zi_item_id', true );
		if ( ! empty( $zoho_group_id ) ) {
			return;
		}

		// Check if the the given sku has product in zoho inventory.
		$sku = $product->get_sku();
		// use the product name if sku is empty, as Zoho search can match on name as well.
		if ( empty( $sku ) ) {
			$sku = $product->get_name();
		}
		$zoho_inventory_oid       = $this->config['ProductZI']['OID'];
		$zoho_inventory_url       = $this->config['ProductZI']['APIURL'];
		$sku_check                = str_replace( ' ', '+', $sku );
		$url                      = $zoho_inventory_url . 'inventory/v1/items?search_text=' . $sku_check . '&filter_by=Status.Active&organization_id=' . $zoho_inventory_oid;
		$execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
		$get_request              = $execute_curl_call_handle->execute_curl_call_get( $url );

		// Guard against transport/errors and ensure Zoho returned a successful response with items.
		if ( ! is_wp_error( $get_request ) && 0 === (int) $get_request->code && isset( $get_request->items ) && is_array( $get_request->items ) ) {
			// build lookups keyed by SKU for quick matching.
			// A Zoho item may carry the SKU in either the `sku` field or the `name` field.
			$zoho_items_by_sku = array();
			$zoho_group_by_sku = array();
			foreach ( $get_request->items as $zi_item ) {
				// Collect candidate SKU values: prefer explicit sku field, fall back to name.
				$candidates = array();
				if ( ! empty( $zi_item->sku ) ) {
					$candidates[] = $zi_item->sku;
				}
				if ( ! empty( $zi_item->name ) ) {
					$candidates[] = $zi_item->name;
				}
				foreach ( $candidates as $candidate ) {
					$sku_lc                       = strtolower( trim( $candidate ) );
					$zoho_items_by_sku[ $sku_lc ] = $zi_item->item_id;
					if ( ! empty( $zi_item->group_id ) ) {
						$zoho_group_by_sku[ $sku_lc ] = (int) $zi_item->group_id;
					}
				}
			}
			// Match each Woo variation's SKU to a Zoho item. Save item_id on the variation and,
			// from the first match, capture the group_id to save on the parent product.
			$child_ids        = $product->get_children();
			$matched_group_id = null;
			foreach ( $child_ids as $child_id ) {
				$variation = wc_get_product( $child_id );
				if ( ! $variation ) {
					continue;
				}
				$var_sku_lc = strtolower( trim( (string) $variation->get_sku() ) );
				if ( $var_sku_lc && isset( $zoho_items_by_sku[ $var_sku_lc ] ) ) {
					$variation->update_meta_data( 'zi_item_id', $zoho_items_by_sku[ $var_sku_lc ] );
					$variation->save();
					// Capture group_id from the first matched item to store on the parent.
					if ( null === $matched_group_id && isset( $zoho_group_by_sku[ $var_sku_lc ] ) ) {
						$matched_group_id = $zoho_group_by_sku[ $var_sku_lc ];
					}
				}
			}
			if ( $matched_group_id ) {
				$product->update_meta_data( 'zi_item_id', $matched_group_id );
				$product->update_meta_data( 'zi_product_errmsg', 'Variation products are mapped with Zoho item group successfully' );
				$product->save();
				return;
			}
		}

		$pre_name = $product->get_title();
		$name     = sanitize_text_field( $pre_name );
		// remove any 4‑byte Unicode characters (covers all emojis).
		$name = preg_replace( '/[\x{10000}-\x{10FFFF}]/u', '', $name );

		$tax_rates  = WC_Tax::get_base_tax_rates( $product->get_tax_class() );
		$tax_id_key = '';
		foreach ( $tax_rates as $tax_key => $tax_value ) {
			$tax_id_key = $tax_key;
			break;
		}
		$tax_option     = get_option( 'cmbird_zoho_inventory_tax_rate_' . $tax_id_key );
		$tax_id         = explode( '##', $tax_option )[0];
		$zi_category_id = $this->cmbird_zi_get_prod_updated_category( $post_id );

		$zidata = '"group_name" : "' . $name . '", "tax_id" : "' . $tax_id . '","category_id" : "' . $zi_category_id . '",';

		// attributes.
		$attributes = $product->get_attributes();
		// fwrite($fd, PHP_EOL . 'ATTRIBUTES : ' . print_r($attributes, true));

		$attribute_name1 = '';
		$attribute_name2 = '';
		$attribute_name3 = '';
		foreach ( $attributes as $attribute ) {
			if ( ! empty( $attribute ) ) {
				$attrname1 = $attribute->get_name();
				$attrname  = sanitize_text_field( $attrname1 );
				if ( ! empty( $attrname ) && $attribute['variation'] ) {
					if ( empty( $attribute_name1 ) ) {
						$attribute_name1 = $attrname;
					} elseif ( empty( $attribute_name2 ) ) {
						$attribute_name2 = $attrname;
					} elseif ( empty( $attribute_name3 ) ) {
						$attribute_name3 = $attrname;
					}
				}
			}
		}
		if ( ! empty( $attribute_name1 ) ) {
			$zidata .= '"attribute_name1": "' . $attribute_name1 . '",';
		}
		if ( ! empty( $attribute_name2 ) ) {
			$zidata .= '"attribute_name2": "' . $attribute_name2 . '",';
		}
		if ( ! empty( $attribute_name3 ) ) {
			$zidata .= '"attribute_name3": "' . $attribute_name3 . '",';
		}

		$available_variations = $product->get_children();
		// If there is attributes variations then append that data to server.
		$items = array();
		if ( count( $available_variations ) > 0 ) {
			foreach ( $available_variations as $child_id ) {

				$product_variable = wc_get_product( $child_id );
				$items[]          = $this->cmbird_zi_variants_products( $product_variable, $child_id, $attribute_name1, $attribute_name2, $attribute_name3 );
			}
		}

		// get category id.
		// $zi_category_id = $this->cmbird_zi_get_prod_updated_category($post_id);
		// if ($zi_category_id) {.
		// $zidata .= '"category_id" : "' . $zi_category_id . '",';
		// }.

		$zidata .= '"items" :[' . implode( ',', $items ) . ']';
		// save the request body for debugging purposes.
		$zi_body_request = rtrim( $zidata, ',' );
		$product->update_meta_data( 'zi_product_body_request', '{' . $zi_body_request . '}' );
		$product->save();

		$data = array(
			'JSONString' => '{' . $zidata . '}',
		);

		// fwrite($fd, PHP_EOL . 'ZI Data JSON : ' . '{' . print_r($data, true) . '}');
		// fclose($fd);

		$zoho_inventory_oid = $this->config['ProductZI']['OID'];
		$zoho_inventory_url = $this->config['ProductZI']['APIURL'];

		if ( ! empty( $zoho_inventory_oid ) ) {
			$url = $zoho_inventory_url . 'inventory/v1/itemgroups?organization_id=' . $zoho_inventory_oid;

			$execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
			$json                     = $execute_curl_call_handle->execute_curl_call_post( $url, $data );

			$errmsg = $json->message;
			$product->update_meta_data( 'zi_product_errmsg', $errmsg );
			$product->save();
			$code = $json->code;
			if ( '0' === $code || 0 === $code ) {

				// This item will keep the copy of zoho item_id with respect to product.
				// name as key synced to zoho.
				$child_items = array();
				foreach ( $json->item_group as $key => $value ) {
					if ( 'group_id' === $key ) {
						$group_id = $value;
					}

					if ( 'items' === $key ) {
						foreach ( $value as $key2 => $val2 ) {
							// Only map child items by SKU — skip entries without SKU.
							if ( ! empty( $val2->sku ) ) {
								$child_items[ strtolower( trim( $val2->sku ) ) ] = $val2->item_id;
							}
						}
					}
				}
				if ( ! empty( $group_id ) ) {
					$product->update_meta_data( 'zi_item_id', $group_id );
					$product->save();
				}

				foreach ( $available_variations as $child_id ) {
					$product_variable = wc_get_product( $child_id );

					$var_sku = trim( (string) $product_variable->get_sku() );
					if ( $var_sku && isset( $child_items[ strtolower( $var_sku ) ] ) ) {
						$variation = wc_get_product( $child_id );
						if ( $variation ) {
							$variation->update_meta_data( 'zi_item_id', $child_items[ strtolower( $var_sku ) ] );
							$variation->save();
						}
					}
				}
			}
		}
		// End New Variable Product.
	}

	/**
	 * Function to sync variations of a product.
	 *
	 * @param object $product_variable - WooCommerce product object of a variation.
	 * @param int    $post_id          - Post ID of a variation product.
	 * @param string $attr1           - Attribute 1 name.
	 * @param string $attr2           - Attribute 2 name.
	 * @param string $attr3           - Attribute 3 name.
	 * @return string $item_id - Zoho item id of a variation product.
	 */
	protected function cmbird_zi_variants_products( $product_variable, $post_id, $attr1 = '', $attr2 = '', $attr3 = '' ) {
		// $fd = fopen(__DIR__.'/variations_products.txt','a+');
		// fwrite($fd,PHP_EOL.'-------------------------------');
		// fwrite($fd,PHP_EOL.'$attr1 : '.$attr1.' | $attr2 : '.$attr2.' | $attr3 : '.$attr3.' $post_id : '.$post_id);
		// Sync Attributes of Variable Products.

		$attributes = $product_variable->get_variation_attributes();
		// fwrite($fd,PHP_EOL.'$variation_attributes : '.print_r($attributes,true));
		$arrtibute_string = '';
		foreach ( array( $attr1, $attr2, $attr3 ) as $idx => $attr ) {
			if ( empty( $attr ) ) {
				continue;
			}
			// Use sanitize_title() so the key matches WooCommerce's internal storage for
			// both taxonomy attributes (pa_*) and custom/text attributes.
			$attr_key = 'attribute_' . sanitize_title( $attr );
			$raw_val  = $attributes[ $attr_key ] ?? '';
			// For taxonomy attributes get_variation_attributes() returns the term slug.
			// Resolve it to the human-readable name before sending to Zoho.
			if ( '' !== $raw_val && taxonomy_exists( $attr ) ) {
				$term = get_term_by( 'slug', $raw_val, $attr );
				if ( $term ) {
					$raw_val = $term->name;
				}
			}
			if ( '' !== $raw_val ) {
				$option_num        = $idx + 1;
				$arrtibute_string .= '"attribute_option_name' . $option_num . '": "' . str_replace( '"', '', $raw_val ) . '",';
			}
		}
		// fwrite($fd,PHP_EOL.'$arrtibute_string : '.$arrtibute_string);
		// fclose($fd);
		// Use product object meta accessor instead of direct postmeta.
		$zoho_item_id = $product_variable->get_meta( 'zi_item_id', true );
		$pname        = '';
		foreach ( $product_variable->get_variation_attributes() as $taxonomy => $terms_slug ) {

			$pname .= $terms_slug;
		}

		$vname = $product_variable->get_name();
		$name  = sanitize_text_field( $vname );
		// remove any 4‑byte Unicode characters (covers emojis).
		$name = preg_replace( '/[\x{10000}-\x{10FFFF}]/u', '', $name );
		$rate = $product_variable->get_regular_price();
		// $rateS = $product_variable->get_sale_price();
		if ( $product_variable->is_virtual( 'yes' ) ) {
			$product_type = 'service';
			$item_type    = 'sales';
		} else {
			$product_type = 'goods';
			$item_type    = 'inventory';
		}

		$sku            = $product_variable->get_sku();
		$stock_quantity = $product_variable->get_stock_quantity();
		$in_stock       = ( $stock_quantity > 0 ) ? $stock_quantity : 0;
		// Get Tax ID.
		$tax_rates  = WC_Tax::get_base_tax_rates( $product_variable->get_tax_class() );
		$tax_id_key = '';
		foreach ( $tax_rates as $tax_key => $tax_value ) {
			$tax_id_key = $tax_key;
			break;
		}
		$tax_option = get_option( 'cmbird_zoho_inventory_tax_rate_' . $tax_id_key );
		$tax_id     = explode( '##', $tax_option )[0];

		$zi_status = ( 'publish' === get_post_status( $post_id ) ) ? 'active' : 'inactive';
		// request data for adding/updating value to zoho.
		$zidata = '';
		if ( ! empty( $arrtibute_string ) ) {
			$zidata .= $arrtibute_string;
		}
		$zidata .= '"name" : "' . $name . '",';
		$zidata .= '"product_type" : "' . $product_type . '",';
		$zidata .= '"sku" : "' . $sku . '",';
		$zidata .= '"item_type" : "' . $item_type . '",';
		// $zidata .= '"unit" : "pcs",';
		$zidata .= '"status" : "' . $zi_status . '",';
		if ( empty( $zoho_item_id ) && $in_stock > 0 ) {
			$zidata .= '"initial_stock" : ' . $in_stock . ',';
			$zidata .= '"initial_stock_rate" : ' . $in_stock . ',';
		}
		$zidata .= '"rate" : "' . $rate . '",';
		$zidata .= '"tax_id" : "' . $tax_id . '",';
		// Get cost_price from product meta using the WC product object.
		$cost_price = $product_variable->get_meta( '_cogs_total_value', true );
		if ( ! empty( $cost_price ) && is_numeric( $cost_price ) ) {
			$zidata .= '"purchase_rate" : "' . $cost_price . '",';
		}

		$dimensions         = (object) array();
		$dimensions->length = $product_variable->get_length();
		$dimensions->width  = $product_variable->get_width();
		$dimensions->height = $product_variable->get_height();
		$dimensions->weight = $product_variable->get_weight();
		if ( ! empty( $dimensions ) ) {
			$zidata .= '"package_details" : ' . wp_json_encode( $dimensions );
		}

		// $fd = fopen(__DIR__ . '/variations.txt', 'a+');
		// fwrite($fd,PHP_EOL.'Get data for $post_id '.$post_id);
		if ( ctype_digit( $zoho_item_id ) && ! empty( $zoho_item_id ) ) {
			// fwrite($fd, PHP_EOL . 'Update Item');
			$update_error_msg = $this->cmbird_zi_product_put( $post_id, $zoho_item_id, $zidata );
			$zidataa          = '';
			// fwrite($fd, PHP_EOL . '{' . $zidata . '}');
			return $zidataa .= '{' . $zidata . '}';
		} else {
			// fwrite($fd, PHP_EOL . 'Create Item');
			// Check if the the given sku has product in zoho inventory.
			$zoho_inventory_oid       = $this->config['ProductZI']['OID'];
			$zoho_inventory_url       = $this->config['ProductZI']['APIURL'];
			$sku_check                = str_replace( ' ', '+', $sku );
			$url                      = $zoho_inventory_url . 'inventory/v1/items?search_text=' . $sku_check . '&filter_by=Status.Active&organization_id=' . $zoho_inventory_oid;
			$execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
			$get_request              = $execute_curl_call_handle->execute_curl_call_get( $url );
			$var_item_id              = '';
			$groupitem_id             = '';

			// Guard against transport/errors and ensure Zoho returned a successful response with items.
			if ( ! is_wp_error( $get_request ) && property_exists( $get_request, 'code' ) && (int) $get_request->code === 0 && property_exists( $get_request, 'items' ) && is_array( $get_request->items ) ) {
				foreach ( $get_request->items as $zoho_item ) {
					// match SKU exactly (trimmed) to avoid false positives from Zoho's "contains" search
					if ( isset( $zoho_item->sku ) && trim( (string) $zoho_item->sku ) === trim( (string) $sku ) ) {
						// prefer active items only.
						if ( ! isset( $zoho_item->status ) || 'active' === $zoho_item->status ) {
							$var_item_id  = $zoho_item->item_id ?? '';
							$groupitem_id = $zoho_item->group_id ?? '';
							break;
						}
					}
				}
			}

			$zidataa = '';
			if ( $var_item_id ) {
				// Persist mapping on the variation using WC_Product API.
				$product_variable->update_meta_data( 'zi_item_id', $var_item_id );
				$product_variable->save();
			}
			if ( $groupitem_id ) {
				$parent_product_id = wp_get_post_parent_id( $post_id );
				if ( $parent_product_id ) {
					$parent_product = wc_get_product( $parent_product_id );
					if ( $parent_product ) {
						$parent_product->update_meta_data( 'zi_item_id', $groupitem_id );
						$parent_product->save();
					}
				}
			}
			// fwrite($fd, PHP_EOL . '$var_item_id : ' . $var_item_id);
			// fclose($fd);
			return $zidataa .= '{' . $zidata . '}';
		}
	}

	/**
	 * Check if category already exists and return updated one
	 */
	protected function cmbird_zi_get_prod_updated_category( $product_id ) {
		// Check if product category already synced.
		$terms = get_the_terms( $product_id, 'product_cat' );
		if ( $terms ) {
			foreach ( $terms as $term ) {
				$product_cat_id = $term->term_id;
				$zoho_cat_id    = ( new \CommerceBird\Admin\Mappings\ZohoCategoryMap() )->zoho_id_for_term( (int) $product_cat_id );
				if ( $zoho_cat_id ) {
					break;
				}
			}
		}
		// Check if product has already mapped category.
		if ( empty( $zoho_cat_id ) ) {
			$zoho_cat_id = get_post_meta( $product_id, 'zi_category_id', true );
		}

		if ( $zoho_cat_id ) {
			return $zoho_cat_id;
		} else {
			return false;
		}
	}

	/**
	 * Function for adding Simple product from Zoho to woocommerce.
	 *
	 * @param $prod - Product object for adding new product in woocommerce.
	 * @param $user_id - Current Active user Id
	 * @param string $type - product is composite item or not (composite)
	 */
	public function cmbird_zi_product_to_woocommerce( $item, $item_stock = '', $type = '' ) {
		// $fd = fopen( __DIR__ . '/cmbird_zi_product_to_woocommerce.txt', 'a+' );
		try {
			if ( 'active' !== $item['status'] ) {
				return;
			}
			$product = new WC_Product();

			$allow_backorders      = get_option( 'woocommerce_allow_backorders' );
			$zi_disable_stock_sync = get_option( 'cmbird_zoho_disable_stock_sync_status' );

			// Set the product data.
			$product->set_status( 'publish' );
			$product->set_name( $item['name'] );
			$product->set_regular_price( $item['rate'] );
			$product->set_short_description( $item['description'] );
			$product->set_sku( $item['sku'] );

			// Set the stock management properties.
			if ( ! empty( $item_stock ) && ! $zi_disable_stock_sync ) {
				$product->set_manage_stock( true );
				$product->set_stock_quantity( $item_stock );

				if ( $item_stock > 0 ) {
					$product->set_stock_status( 'instock' );
				} elseif ( $item_stock < 0 && 'yes' === $allow_backorders ) {
					$product->set_stock_status( 'onbackorder' );
				} else {
					$product->set_stock_status( 'outofstock' );
				}
			}

			// Save the product.
			$product_id = $product->save();

			// Map composite items metadata to convert product as a bundle product.
			if ( 'composite' === $type ) {
				update_post_meta( $product_id, '_wc_pb_layout_style', 'default' );
				update_post_meta( $product_id, '_wc_pb_add_to_cart_form_location', 'default' );
				wp_set_object_terms( $product_id, 'bundle', 'product_type' );
			}

			return $product_id;
		} catch ( Exception $e ) {
			// Handle the exception, log it, or perform any necessary actions.
			return new WP_Error( 'Error creating WooCommerce product: ' . $e->getMessage() );
		}
	}
}
$cmbird_products_zi_export = new CMBIRD_Products_ZI_Export();
