<?php

namespace Pixeldev\SquareWooSync\Woo;

use Error;
use PO;

if (!defined('ABSPATH')) {
  exit; // Exit if accessed directly.
}

/**
 * Class to handle WooCommerce product creation.
 */
class CreateProduct
{

  /**
   * Entry point for creating or updating a WooCommerce product.
   *
   * @param array $wc_product_data Data for the WooCommerce product.
   * @param array $data_to_import Data indicating what should be imported.
   * @param bool $update_only Flag indicating whether to only update existing products.
   * @return int|false Product ID on success, or false on failure.
   */
  public function create_or_update_product($wc_product_data, $data_to_import, $update_only, $matching_product = null)
  {
    error_log(json_encode($wc_product_data));
    error_log(json_encode($data_to_import));

    try {
      $is_new_product = false; // Initialize the flag

      $can_process = $this->validate_product_data($wc_product_data);

      if ($can_process['status'] === 'failed') {
        return $can_process;
      }

      if (!$matching_product) {
        $product = $this->get_or_create_product_by_square_id($wc_product_data, $update_only, $is_new_product);
      } else {
        $product = $matching_product;
        $is_new_product = false; // If a matching product is passed, it's not new
      }

      if ($update_only && !$product) {
        return false;
      }

      $settings = get_option('square-woo-sync_settings', []);
      $attributesDisabled = $settings['squareAuto']['attributesDisabled'] ?? false;

      $data_to_import['attributesDisabled'] = $attributesDisabled;

      $this->update_common_product_properties($product, $wc_product_data, $data_to_import);


      if (isset($data_to_import['image']) && $data_to_import['image'] && !empty($wc_product_data['images'])) {
        $this->import_product_images($product, $wc_product_data, $data_to_import);
      }


      if ('variable' === $wc_product_data['type']) {
        $this->handle_variable_product($product, $wc_product_data, $data_to_import, $update_only);
      } else {
        $this->handle_simple_product($product, $wc_product_data, $data_to_import);
      }

      if (isset($data_to_import['categories']) && $data_to_import['categories']) {
        if (!empty($wc_product_data['categories'])) {
          // Assign categories if provided
          $this->assign_product_category($product, $wc_product_data);
        } else {
          // Remove all categories if none are provided in the import
          wp_set_object_terms($product->get_id(), [], 'product_cat');
        }
      }


      if ($is_new_product) {
        // Set status for new products based on settings or default to 'publish'
        $status = isset($settings['importStatus']) && $settings['importStatus']
          ? $settings['importStatus']
          : 'publish';
        $product->set_status($status);
      } else {
        // Get the current status of the product
        $current_status = $product->get_status();
        // If the current status is 'draft' and import status is set to 'publish', update the status
        if ($current_status === 'draft' && isset($settings['importStatus']) && $settings['importStatus'] === 'publish') {
          $product->set_status('publish');
        }
      }

      if (!empty($wc_product_data['modifiers']) && isset($settings['modifierSyncing']) && $settings['modifierSyncing'] === true) {
        // Replace existing modifiers with whatever came from Square.
        $this->update_product_modifiers($product, $wc_product_data);
      }

      $product->save();

      // // Set default attributes after publishing
      // if ('variable' === $wc_product_data['type']) {
      //   $this->set_default_attributes($product);
      // }


      return $product->get_id();
    } catch (\Exception $e) {
      error_log('Error creating/updating product: ' . $e->getMessage());
      return ['status' => 'failed', 'product_id' => null, 'square_id' => $wc_product_data['square_product_id'], 'message' => esc_html($e->getMessage())];
    }
  }

  /**
   * Validates essential product data.
   *
   * @param array $wc_product_data Data for the WooCommerce product.
   * @throws \Exception If any required data is missing or invalid.
   */
  private function validate_product_data($wc_product_data)
  {
    if (empty($wc_product_data['name'])) {
      return ['status' => 'failed', 'product_id' => null, 'square_id' => $wc_product_data['square_product_id'], 'message' => 'Product name is missing.'];
    }


    if ($wc_product_data['type'] === 'variable') {
      if (empty($wc_product_data['variations']) || !is_array($wc_product_data['variations'])) {
        return ['status' => 'failed', 'product_id' => null, 'square_id' => $wc_product_data['square_product_id'], 'message' => 'Variations data is missing for variable product.'];
      }
    }

    return ['status' => 'success'];
    // Add more validation rules as needed
  }

  /**
   * Set default attributes for a variable product.
   *
   * @param \WC_Product_Variable $product WooCommerce variable product object.
   */
  private function set_default_attributes($product)
  {
    $product_children = $product->get_children();
    if (!empty($product_children)) {
      $first_variation_id = $product_children[0];
      $first_variation = wc_get_product($first_variation_id);
      $default_attributes = $first_variation->get_attributes();

      $product->set_default_attributes($default_attributes);
      $product->save();
    }
  }

  /**
   * Retrieves or creates a product based on Square product ID.
   *
   * @param array $wc_product_data Data for the WooCommerce product.
   * @param bool $update_only Indicates whether to update existing products only.
   * @return \WC_Product|false WooCommerce product object or false if not found and update_only is true.
   */
  private function get_or_create_product_by_square_id($wc_product_data, $update_only, &$is_new_product)
  {
    global $wpdb;
    $meta_key = 'square_product_id';
    $meta_value = $wc_product_data['square_product_id'];

    $query = $wpdb->prepare(
      "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s LIMIT 1",
      $meta_key,
      $meta_value
    );

    $product_id = $wpdb->get_var($query);

    // Check if the product exists. If $update_only is true and no product is found, return false
    if ($update_only && !$product_id) {
      return false;
    }

    // Create a new product if it doesn't exist
    if (!$product_id) {
      $is_new_product = true; // Mark the product as new
      if ($wc_product_data['type'] === 'simple') {
        $product = new \WC_Product_Simple();
      } else {
        $product = new \WC_Product_Variable();
      }
      $product->save();
      $product_id = $product->get_id();

      // Assign the square_product_id meta to the new product
      update_post_meta($product_id, $meta_key, $meta_value);
    } else {
      $is_new_product = false; // The product already exists
      $product = wc_get_product($product_id);
    }

    return $product;
  }


  /**
   * Retrieves or creates a product based on Square product ID.
   *
   * @param array $wc_product_data Data for the WooCommerce product.
   * @param bool $update_only Indicates whether to update existing products only.
   * @return array|\WC_Product[]|false Array of WooCommerce product objects or false if not found and update_only is true.
   */
  public function get_product_by_square_id($square_product_id)
  {
    global $wpdb;
    $meta_key = 'square_product_id';
    $meta_value = $square_product_id;

    // Prepare query to get all post IDs with the same Square product ID
    $query = $wpdb->prepare(
      "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s",
      $meta_key,
      $meta_value
    );

    // Get all product IDs that match the Square product ID
    $product_ids = $wpdb->get_col($query);

    if ($product_ids) {
      // Return an array of WooCommerce products
      $products = array_map('wc_get_product', $product_ids);
      return $products;
    }

    // Return false if no products are found
    return false;
  }


  /**
   * Retrieves WooCommerce products by their SKU.
   *
   * @param string $sku The SKU to look up.
   * @return array|\WC_Product[]|false Array of WooCommerce product objects or false if not found.
   */
  public function get_product_by_sku($sku)
  {
    global $wpdb;
    $meta_key   = '_sku';
    $meta_value = $sku;

    // Prepare query to get all post IDs with the matching SKU
    $query = $wpdb->prepare(
      "SELECT post_id 
       FROM {$wpdb->postmeta} 
       WHERE meta_key = %s 
         AND meta_value = %s",
      $meta_key,
      $meta_value
    );

    // Get all product IDs that match the SKU
    $product_ids = $wpdb->get_col($query);

    if (!empty($product_ids)) {
      // Return an array of WooCommerce product objects
      $products = array_map('wc_get_product', $product_ids);
      return $products;
    }

    // Return false if no products are found
    return false;
  }


  /**
   * Updates common properties of a WooCommerce product.
   *
   * @param \WC_Product $product WooCommerce product object.
   * @param array $wc_product_data Data for the WooCommerce product.
   * @param array $data_to_import Data indicating what should be imported.
   */
  private function update_common_product_properties(&$product, $wc_product_data, $data_to_import)
  {
    // Set common product properties
    if (isset($data_to_import['title']) && $data_to_import['title']) {
      $product->set_name($wc_product_data['name']);
    }

    $product->update_meta_data('square_product_id', $wc_product_data['square_product_id']);

    if (!empty($wc_product_data['locations']) && is_array($wc_product_data['locations'])) {
      $locations_list = implode(',', $wc_product_data['locations']);
      $product->update_meta_data('square_locations', $locations_list);
    }

    if (isset($wc_product_data['variation_id'])) {
      $product->update_meta_data('square_variation_id', $wc_product_data['variation_id']);
    }

    if ($wc_product_data['type'] === 'simple') {
      $product->update_meta_data('square_variation_id', $wc_product_data['variations'][0]['variation_square_id']);
    }

    if (isset($data_to_import['description']) && $data_to_import['description'] && isset($wc_product_data['description'])) {
      $product->set_description($wc_product_data['description']);
    }

    // Preserve existing variation attributes
    $preserved_attributes = $product->get_attributes();
    $product->set_attributes($preserved_attributes);
    $product->save();
  }


  /**
   * Handles variable products.
   *
   * @param \WC_Product $product WooCommerce product object.
   * @param array $wc_product_data Data for the WooCommerce product.
   * @param array $data_to_import Data indicating what should be imported.
   * @param bool $update_only Flag to indicate if only updates should be performed.
   */
  private function handle_variable_product(&$product, $wc_product_data, $data_to_import, $update_only)
  {
    $product_id = $product->get_id();

    // Retrieve the images map
    $images_map = isset($wc_product_data['images']) ? $wc_product_data['images'] : [];

    // Check if the product is simple and switch it to variable
    if ($product->is_type('simple')) {
      delete_post_meta($product_id, 'square_variation_id');
      $product_classname = \WC_Product_Factory::get_product_classname($product_id, 'variable');
      $new_product = new $product_classname($product_id);
      $new_product->save();
      $product = wc_get_product($new_product->get_id());
    }

    if (!isset($data_to_import['attributesDisabled']) || (isset($data_to_import['attributesDisabled'])) && $data_to_import['attributesDisabled'] === false) {
      // Collect and ensure unique attribute options from variations
      $square_attributes = [];
      foreach ($wc_product_data['variations'] as $variation) {
        foreach ($variation['attributes'] as $attribute) {
          $attribute_name = strtolower($attribute['name']); // Normalize attribute name casing
          $attribute_option = strtolower($attribute['option']); // Normalize attribute option casing
          $square_attributes[$attribute_name][] = $attribute_option;
        }
      }

      foreach ($square_attributes as &$options) {
        $options = array_unique($options);
      }

      // Retrieve existing attributes and their terms from the product
      $existing_attributes = $product->get_attributes();
      $new_attributes = [];

      foreach ($square_attributes as $attribute_name => $terms) {
        // Retrieve existing terms from the product's attributes
        $attribute_slug = wc_sanitize_taxonomy_name($attribute_name);
        $existing_terms = [];
        $normalized_existing_terms = [];

        // Check for local attribute
        if (isset($existing_attributes[$attribute_name])) {
          $existing_terms = $existing_attributes[$attribute_name]->get_options();
          $normalized_existing_terms = array_map('strtolower', $existing_terms); // Normalize existing terms for comparison
        } else {
          // Check for global attribute
          $taxonomy = 'pa_' . $attribute_slug;
          if (isset($existing_attributes[$taxonomy])) {
            $existing_terms = $existing_attributes[$taxonomy]->get_options();
            $normalized_existing_terms = array_map('strtolower', $existing_terms); // Normalize existing terms for comparison
          }
        }

        // Normalize terms for comparison
        $normalized_terms = array_map('strtolower', $terms);

        // Merge existing terms with new terms (normalized)
        $merged_normalized_terms = array_unique(array_merge($normalized_existing_terms, $normalized_terms));

        // Merge original existing terms with new terms (preserving original casing)
        $merged_terms = array_unique(array_merge($existing_terms, $terms));

        // Get or create the attribute, passing the product object to handle local attributes
        $attribute = $this->get_or_create_attribute($attribute_name, $merged_terms, $product);

        if ($attribute) {
          $new_attributes[$attribute->get_name()] = $attribute;
        }
      }

      // Combine existing and new attributes
      $product_attributes = array_merge($existing_attributes, $new_attributes);

      // Set all attributes to the variable product
      $product->set_attributes($product_attributes);
    }


    $product->save();



    // Retrieve parent product ID and SKU
    $parent_product_id = $product->get_id();
    $parent_sku = $product->get_sku();


    // Process each variation
    foreach ($wc_product_data['variations'] as $variation_data) {
      $existing_variation_id = null;

      // Query for existing variations of this product with the same square_product_id
      $existing_variations = get_posts([
        'post_type' => 'product_variation',
        'post_status' => ['publish', 'draft'], // Use an array for multiple statuses
        'posts_per_page' => -1,
        'post_parent' => $parent_product_id,
        'meta_query' => [
          [
            'key' => 'square_product_id',
            'value' => $variation_data['variation_square_id'],
          ],
        ],
        'fields' => 'ids',
      ]);

      if (!empty($existing_variations)) {
        $existing_variation_id = $existing_variations[0]; // Assuming there's only one match
      }

      if ($update_only && !isset($existing_variation_id)) {
        continue;
      }

      // Use existing variation ID or create new variation
      $variation = !empty($existing_variations) ? wc_get_product($existing_variations[0]) : new \WC_Product_Variation();
      $variation->set_parent_id($parent_product_id);

      // Preserve existing variation attributes
      $preserved_variation_attributes = $variation->get_attributes();


      if (isset($data_to_import['sku']) && $data_to_import['sku'] && isset($variation_data['sku'])) {
        $new_sku = $variation_data['sku'];
        // Check parent product SKU
        if ($new_sku !== $parent_sku && !wc_get_product_id_by_sku($variation_data['sku'])) {
          $variation->set_sku($new_sku);
        }
      }

      if (isset($variation_data['upc'])) {
        $variation->update_meta_data('_global_unique_id', $variation_data['upc']);
      }

      if (isset($data_to_import['stock']) && $data_to_import['stock'] && isset($variation_data['stock'])) {
        $variation->set_manage_stock(true);
        $variation->set_stock_status('instock');
        $variation->set_stock_quantity($variation_data['stock']);
      }


      if (isset($variation_data['track_inventory']) && $variation_data['track_inventory'] === false) {
        $variation->set_manage_stock(false);
        $variation->set_stock_status('instock');
      }

      if (isset($variation_data['location_overrides']) && count($variation_data['location_overrides']) > 0) {
        $settings = get_option('square-woo-sync_settings', []);
        $location = $settings['location'];

        foreach ($variation_data['location_overrides'] as $squareLoc) {
          if ($squareLoc['location_id'] === $location) {
            if (!empty($squareLoc['track_inventory'])) {
            }

            if (isset($squareLoc['sold_out']) && $squareLoc['sold_out'] === true) {
              $variation->set_stock_status('outofstock');
            } else {
              $variation->set_stock_status('instock');
            }
          }
        }
      }


      if (isset($data_to_import['price']) && $data_to_import['price'] && isset($variation_data['price'])) {
        $variation->set_regular_price($variation_data['price']);
      }

      if (!isset($data_to_import['attributesDisabled']) || (isset($data_to_import['attributesDisabled'])) && $data_to_import['attributesDisabled'] === false) {
        // Set attributes for the variation
        $variation_attributes = [];
        foreach ($variation_data['attributes'] as $attribute) {
          $attribute_name = strtolower($attribute['name']); // Normalize attribute name casing
          $attribute_option = $attribute['option']; // Preserve the original casing for the option

          $attribute_slug = wc_sanitize_taxonomy_name($attribute_name);

          // First, check if it's a local attribute
          if (isset($existing_attributes[$attribute_name])) {
            $local_terms = $existing_attributes[$attribute_name]->get_options();
            $normalized_local_terms = array_map('strtolower', $local_terms); // Lowercase local terms for comparison
            if (in_array(strtolower($attribute_option), $normalized_local_terms)) {
              // Use the term with the original casing
              $key = array_search(strtolower($attribute_option), $normalized_local_terms);
              $variation_attributes[$attribute_name] = $local_terms[$key];
            } else {
              // If the local term doesn't exist, add it using the original casing
              $local_terms[] = $attribute_option;
              $existing_attributes[$attribute_name]->set_options($local_terms);
              $variation_attributes[$attribute_name] = $attribute_option;
            }
          } else {
            // Check if it's a global attribute
            $taxonomy = 'pa_' . $attribute_slug;
            if (taxonomy_exists($taxonomy)) {
              $term = get_term_by('slug', strtolower($attribute_option), $taxonomy);

              if ($term) {
                $variation_attributes[$taxonomy] = $term->slug;
              } else {
                $variation_attributes[$taxonomy] = $attribute_option;
              }
            } else {
              $variation_attributes[$attribute_slug] = $attribute_option;
            }
          }
        }
        $variation->set_attributes($variation_attributes);
      } else {
        $variation->set_attributes($preserved_variation_attributes);
      }

      if (isset($variation_data['image_ids']) && is_array($variation_data['image_ids']) && !empty($variation_data['image_ids']) && isset($data_to_import['image']) && $data_to_import['image']) {
        // Iterate through all image IDs to find the first match
        foreach ($variation_data['image_ids'] as $image_id) {
          if (isset($images_map[$image_id])) {
            $image_url = $images_map[$image_id];
            $attachment_id = self::get_attachment_id_by_unique_image_key($image_id, $product->get_id(), $image_url);

            if ($attachment_id) {
              $variation->set_image_id($attachment_id);
              break; // Stop checking once we've found and set the image
            }
          }
        }
      }

      // Update meta data and save
      $variation->update_meta_data('square_product_id', $variation_data['variation_square_id']);
      $variation->save();
    }

    // Save parent product after all variations are processed
    $product->save();
  }


  /**
   * Handles simple products.
   *
   * @param \WC_Product_Simple $product WooCommerce simple product object.
   * @param array $wc_product_data Data for the WooCommerce product.
   * @param array $data_to_import Data indicating what should be imported.
   */
  private function handle_simple_product(&$product, $wc_product_data, $data_to_import)
  {
    $variation_id = $product->get_meta('square_variation_id');
    error_log(json_encode($wc_product_data));

    if ($variation_id) {
      // Find the matching variation in wc_product_data
      foreach ($wc_product_data['variations'] as $variation) {

        if ($variation['variation_square_id'] === $variation_id) {
          // Update simple product based on matching variation data
          if (isset($variation['price']) && $data_to_import['price']) {
            $product->set_regular_price($variation['price']);
          }


          if (isset($variation['sku']) && !wc_get_product_id_by_sku($variation['sku']) && $data_to_import['sku']) {
            $product->set_sku($variation['sku']);
          }

          if (isset($variation['upc'])) {
            $product->update_meta_data('_global_unique_id', $variation['upc']);
          }



          if (isset($data_to_import['stock']) && $data_to_import['stock'] && isset($variation['stock'])) {
            $product->set_manage_stock(true);
            $product->set_stock_status('instock');
            $product->set_stock_quantity($variation['stock']);
          }


          if (isset($variation['track_inventory']) && $variation['track_inventory'] === false) {
            $product->set_manage_stock(false);
            $product->set_stock_status('instock');
          }


          if (isset($variation['location_overrides']) && count($variation['location_overrides']) > 0) {
            $settings = get_option('square-woo-sync_settings', []);
            $location = $settings['location'];

            foreach ($variation['location_overrides'] as $squareLoc) {
              if ($squareLoc['location_id'] === $location) {
                if (isset($squareLoc['sold_out']) && $squareLoc['sold_out'] === true) {
                  $product->set_stock_status('outofstock');
                } else {
                  $product->set_stock_status('instock');
                }
              }
            }
          }


          if (!isset($data_to_import['attributesDisabled']) || (isset($data_to_import['attributesDisabled'])) && $data_to_import['attributesDisabled'] === false) {

            if (isset($variation['attributes'])) {
              $attributes = [];
              foreach ($variation['attributes'] as $attribute_data) {
                $attribute_name = $attribute_data['name'];
                $terms = [$attribute_data['option']];

                // Retrieve existing terms from the product's attributes
                $attribute_slug = wc_sanitize_taxonomy_name($attribute_name);
                $existing_attributes = $product->get_attributes();
                $existing_terms = [];

                // Check for local attribute
                if (isset($existing_attributes[$attribute_name])) {
                  $existing_terms = $existing_attributes[$attribute_name]->get_options();
                } else {
                  // Check for global attribute
                  $taxonomy = 'pa_' . $attribute_slug;
                  if (isset($existing_attributes[$taxonomy])) {
                    $existing_terms = $existing_attributes[$taxonomy]->get_options();
                  }
                }

                // Merge existing terms with new terms
                $merged_terms = array_unique(array_merge($existing_terms, $terms));

                // Get or create the attribute
                $attribute = $this->get_or_create_attribute($attribute_name, $merged_terms, $product);
                if ($attribute) {
                  $attributes[$attribute->get_name()] = $attribute;
                }
              }

              // Set all attributes to the simple product
              $product->set_attributes($attributes);
            }
          }

          // Save the product
          $product->save();

          // Exit loop once the matching variation is found and processed
          break;
        }
      }
    } else {
      // Fallback to processing the first variation for the simple product
      if (isset($data_to_import['price']) && $data_to_import['price'] && isset($wc_product_data['variations'][0]['price'])) {
        $product->set_regular_price($wc_product_data['variations'][0]['price']);
      }

      if (isset($data_to_import['sku']) && $data_to_import['sku'] && isset($wc_product_data['variations'][0]['sku']) && !wc_get_product_id_by_sku($wc_product_data['variations'][0]['sku'])) {
        $product->set_sku($wc_product_data['variations'][0]['sku']);
      }

      if (isset($wc_product_data['variations'][0]['upc'])) {
        $product->update_meta_data('_global_unique_id', $wc_product_data['variations'][0]['upc']);
      }


      if (isset($data_to_import['stock']) && $data_to_import['stock'] && isset($wc_product_data['variations'][0]['stock'])) {
        $product->set_manage_stock(true);
        $product->set_stock_status('instock');
        $product->set_stock_quantity($wc_product_data['variations'][0]['stock']);
      }

      if (isset($wc_product_data['variations'][0]['track_inventory']) && $wc_product_data['variations'][0]['track_inventory'] === false) {
        $product->set_manage_stock(false);
        $product->set_stock_status('instock');
      }

      if (isset($wc_product_data['variations'][0]['location_overrides']) && count($wc_product_data['variations'][0]['location_overrides']) > 0) {
        $settings = get_option('square-woo-sync_settings', []);
        $location = $settings['location'];

        foreach ($wc_product_data['variations'][0]['location_overrides'] as $squareLoc) {
          if ($squareLoc['location_id'] === $location) {
            if (isset($squareLoc['sold_out']) && $squareLoc['sold_out'] === true) {
              $product->set_stock_status('outofstock');
            } else {
              $product->set_stock_status('instock');
            }
          }
        }
      }

      if (!isset($data_to_import['attributesDisabled']) || (isset($data_to_import['attributesDisabled'])) && $data_to_import['attributesDisabled'] === false) {

        if (isset($wc_product_data['variations'][0]['attributes'])) {
          $all_attribute_options = $wc_product_data['variations'][0]['attributes'];
          $attributes = [];

          foreach ($all_attribute_options as $attribute_data) {
            $attribute_name = $attribute_data['name'];
            $terms = [$attribute_data['option']];

            // Retrieve existing terms from the product's attributes
            $attribute_slug = wc_sanitize_taxonomy_name($attribute_name);
            $existing_attributes = $product->get_attributes();
            $existing_terms = [];

            // Check for local attribute
            if (isset($existing_attributes[$attribute_name])) {
              $existing_terms = $existing_attributes[$attribute_name]->get_options();
            } else {
              // Check for global attribute
              $taxonomy = 'pa_' . $attribute_slug;
              if (isset($existing_attributes[$taxonomy])) {
                $existing_terms = $existing_attributes[$taxonomy]->get_options();
              }
            }

            // Merge existing terms with new terms
            $merged_terms = array_unique(array_merge($existing_terms, $terms));

            // Get or create the attribute
            $attribute = $this->get_or_create_attribute($attribute_name, $merged_terms, $product);
            if ($attribute) {
              $attributes[$attribute->get_name()] = $attribute;
            }
          }

          // Set all attributes to the simple product
          $product->set_attributes($attributes);
        }
      }

      $product->save();
    }
  }


  /**
   * Assigns a product category based on data in $wc_product_data['categories'].
   *
   * @param \WC_Product $product WooCommerce product object.
   * @param array       $wc_product_data Data for the WooCommerce product.
   */
  private function assign_product_category($product, $wc_product_data)
  {
    if (!empty($wc_product_data['categories']) && is_array($wc_product_data['categories'])) {
      $category_ids = [];
      $created_categories = [];

      // We'll split them into two sets:
      // 1) Those with no (or empty) parent_id => top-level
      // 2) Those that do have a parent_id => subcategory
      $categories_without_parents = [];
      $categories_with_parents = [];

      foreach ($wc_product_data['categories'] as $cat_data) {
        // Treat "false", "", null, etc. as "no parent"
        if (empty($cat_data['parent_id'])) {
          $categories_without_parents[] = $cat_data;
        } else {
          $categories_with_parents[] = $cat_data;
        }
      }

      // 1) Create top-level categories first
      foreach ($categories_without_parents as $cat_data) {
        $wp_cat_id = $this->get_or_create_category($cat_data['name']);
        if ($wp_cat_id !== false) {
          // Map this JSON "id" to the new WP category ID
          $created_categories[$cat_data['id']] = $wp_cat_id;
          // Also collect it for eventual assignment to the product
          $category_ids[] = $wp_cat_id;
        }
      }

      // 2) Now create subcategories
      foreach ($categories_with_parents as $cat_data) {
        $json_parent_id = $cat_data['parent_id'];

        // If we have a known parent WP ID, use it; otherwise, treat as top-level or do another fallback
        $parent_wp_id = isset($created_categories[$json_parent_id])
          ? $created_categories[$json_parent_id]
          : false;

        // Create/get this subcategory
        $wp_cat_id = $this->get_or_create_category($cat_data['name'], $parent_wp_id);
        if ($wp_cat_id !== false) {
          $created_categories[$cat_data['id']] = $wp_cat_id;
          $category_ids[] = $wp_cat_id;
        }
      }

      // Finally, assign all gathered WP IDs to the product
      if (!empty($category_ids)) {
        wp_set_object_terms($product->get_id(), array_unique($category_ids), 'product_cat');
        $product->save();
      }
    } else {
      // If 'categories' is set but empty, remove all categories from the product
      wp_set_object_terms($product->get_id(), [], 'product_cat');
    }
  }


  /**
   * Creates or retrieves a WooCommerce category.
   *
   * @param string    $category_name The name of the category.
   * @param int|false $parent_id     The ID of the parent category, or false if none.
   * @return int|false The category ID on success, or false on failure.
   */
  private function get_or_create_category(string $category_name, $parent_id = 0)
  {
    // Try to find an existing term by exact name
    $term = get_term_by('name', $category_name, 'product_cat');

    if ($term && ! is_wp_error($term)) {
      // Found it—return the term_id
      return (int) $term->term_id;
    }

    // Not found, so create it
    $args = [];
    if ($parent_id) {
      $args['parent'] = (int) $parent_id;
    }

    $new_term = wp_insert_term($category_name, 'product_cat', $args);

    if (! is_wp_error($new_term) && isset($new_term['term_id'])) {
      return (int) $new_term['term_id'];
    }

    // Log any error and bail
    error_log(sprintf(
      /* translators: %s: WP_Error message */
      esc_html__('Error creating category: %s', 'squarewoosync'),
      $new_term instanceof \WP_Error ? $new_term->get_error_message() : __('unknown error', 'squarewoosync')
    ));

    return false;
  }





  /**
   * Retrieves or creates a WooCommerce attribute.
   *
   * @param string $attribute_name The name of the attribute.
   * @param array $terms Array of term slugs or term IDs to associate with the attribute.
   * @param \WC_Product $product The product object for local attributes.
   * @return WC_Product_Attribute|bool The attribute object if successful, or false on failure.
   */
  private function get_or_create_attribute($attribute_name, $terms, $product)
  {
    // Normalize attribute name casing
    $attribute_name = strtolower($attribute_name);

    // Sanitize attribute name for taxonomy
    $attribute_taxonomy = wc_sanitize_taxonomy_name($attribute_name);
    $taxonomy = 'pa_' . $attribute_taxonomy;

    // Convert slugs to term IDs and keep existing IDs
    $terms_with_ids = [];
    foreach ($terms as $term) {
      if (is_numeric($term)) {
        $terms_with_ids[] = $term;
      } else {
        $term_obj = get_term_by('slug', $term, $taxonomy);
        if (!$term_obj) {
          $term_data = wp_insert_term($term, $taxonomy);
          if (!is_wp_error($term_data)) {
            $terms_with_ids[] = $term_data['term_id'];
          }
        } else {
          $terms_with_ids[] = $term_obj->term_id;
        }
      }
    }

    // Check if local attribute exists on the product
    $existing_attributes = $product->get_attributes();

    if (isset($existing_attributes[$attribute_name])) {
      $local_attribute = $existing_attributes[$attribute_name];
      $existing_terms = $local_attribute->get_options();
      $merged_terms_with_ids = array_unique(array_merge($existing_terms, $terms_with_ids));
      $local_attribute->set_options($merged_terms_with_ids);
      return $local_attribute;
    }

    // Check if the taxonomy exists globally
    if (taxonomy_exists($taxonomy)) {
      // Get the ID of the existing global attribute
      $attribute_id = wc_attribute_taxonomy_id_by_name($taxonomy);

      // Check if attribute ID is valid
      if (!$attribute_id) {
        error_log("Invalid global attribute ID for taxonomy: $taxonomy");
        return false;
      }

      // Fetch existing terms from the product for this attribute
      $existing_terms = [];
      if (isset($existing_attributes[$taxonomy])) {
        $existing_terms = $existing_attributes[$taxonomy]->get_options();
      }


      $merged_terms_with_ids = array_unique(array_merge($existing_terms, $terms_with_ids));


      $attribute = new \WC_Product_Attribute();
      $attribute->set_id($attribute_id);
      $attribute->set_name($taxonomy);
      $attribute->set_options($merged_terms_with_ids); // Set term IDs as options
      $attribute->set_position(0);
      $attribute->set_visible(true);
      $attribute->set_variation(true);

      return $attribute;
    } else {
      // Create the global attribute if it doesn't exist
      $attribute_data = [
        'name'             => ucfirst($attribute_name),
        'slug'             => $attribute_taxonomy, // No 'pa_' prefix
        'type'             => 'select',
        'order_by'         => 'menu_order',
        'has_archives'     => 1, // 1 means it will be a global attribute
      ];

      // Check if the attribute data is correct
      if (empty($attribute_data['name'])) {
        error_log("Attribute name is missing in the attribute data");
        return false;
      }

      $attribute_id = wc_create_attribute($attribute_data);

      if (is_wp_error($attribute_id)) {
        error_log("Error creating global attribute: " . $attribute_id->get_error_message());
        return false;
      }

      // Register the taxonomy for the new attribute
      register_taxonomy(
        $taxonomy,
        'product',
        [
          'label' => ucfirst($attribute_name),
          'public' => true,
          'hierarchical' => false,
          'show_ui' => true,
          'query_var' => true,
          'rewrite' => ['slug' => $taxonomy],
        ]
      );

      // Add the terms to the newly created taxonomy
      foreach ($terms as $term) {
        if (!is_numeric($term) && !term_exists($term, $taxonomy)) {
          wp_insert_term($term, $taxonomy);
        }
      }

      // Convert slugs to term IDs again after creating the terms
      $terms_with_ids = [];
      foreach ($terms as $term) {
        if (is_numeric($term)) {
          $terms_with_ids[] = $term;
        } else {
          $term_obj = get_term_by('slug', $term, $taxonomy);
          if ($term_obj) {
            $terms_with_ids[] = $term_obj->term_id;
          }
        }
      }

      $attribute = new \WC_Product_Attribute();
      $attribute->set_id($attribute_id);
      $attribute->set_name($taxonomy);
      $attribute->set_options($terms_with_ids); // Set term IDs as options
      $attribute->set_position(0);
      $attribute->set_visible(true);
      $attribute->set_variation(true);

      return $attribute;
    }
  }


  /**
   * Imports product images.
   *
   * @param \WC_Product $product WooCommerce product object.
   * @param array $wc_product_data Data for the WooCommerce product.
   * @param array $data_to_import Data indicating what should be imported.
   */

  private function import_product_images(\WC_Product $product, array $wc_product_data, array $data_to_import)
  {
    // 1) nothing to do if there are no images
    if (empty($wc_product_data['images']) || ! is_array($wc_product_data['images'])) {
      return;
    }

    // 2) pull out Square IDs and URLs in insertion order
    $square_ids  = array_keys($wc_product_data['images']);
    $image_urls  = array_values($wc_product_data['images']);
    $imported_ids = [];

    // 3) sideload each image in exact Square order
    foreach ($square_ids as $i => $square_image_id) {
      $url = $image_urls[$i];
      $attach_id = self::sideload_image($url, $square_image_id, $product->get_id());
      if ($attach_id) {
        $imported_ids[] = $attach_id;
      }
    }

    if (empty($imported_ids)) {
      return;
    }

    // 4) clear out any existing images
    $product->set_image_id(0);
    $product->set_gallery_image_ids([]);
    $product->save();

    // 5) assign the first imported image as the featured image,
    //    the rest become the gallery
    $featured = array_shift($imported_ids);
    $product->set_image_id($featured);

    if (! empty($imported_ids)) {
      $product->set_gallery_image_ids($imported_ids);
    }

    $product->save();
  }


  private static function sideload_image($file, $square_image_id, $product_id)
  {
    if (!function_exists('download_url')) {
      require_once(ABSPATH . 'wp-admin/includes/file.php');
    }

    // Generate the unique image key
    $normalized_url = self::normalize_url($file);
    $unique_image_key = md5($square_image_id . '|' . $product_id . '|' . $normalized_url);

    // Check for existing image by unique key and product ID
    $existing_attachment = self::get_attachment_by_unique_key($unique_image_key, $product_id);
    if ($existing_attachment) {
      return $existing_attachment->ID;
    }

    // Download the image
    $tmp = download_url($file);

    if (is_wp_error($tmp)) {
      error_log('Temporary download error: ' . $tmp->get_error_message());
      return null; // Return null on error
    }

    // Generate a unique filename
    $original_filename = basename(parse_url($file, PHP_URL_PATH));
    $extension = pathinfo($original_filename, PATHINFO_EXTENSION);
    $unique_filename = $square_image_id . '_' . time() . '.' . $extension;

    // Get the uploads directory
    $uploads = wp_upload_dir();

    // Move the temporary file to the uploads directory
    $destination = $uploads['path'] . '/' . $unique_filename;

    if (!rename($tmp, $destination)) {
      error_log('Failed to move file to uploads directory.');
      @unlink($tmp); // Clean up temporary file
      return null; // Return null on error
    }

    $createProduct = new CreateProduct();
    $mimeType = $createProduct->get_mime_type($destination);

    // Build the attachment array
    $attachment = [
      'guid'           => $uploads['url'] . '/' . $unique_filename,
      'post_mime_type' => $mimeType,
      'post_title'     => sanitize_file_name($original_filename),
      'post_content'   => '',
      'post_status'    => 'inherit',
      'post_parent'    => $product_id, // Associate with the product
    ];

    // Insert the attachment into the database
    $attach_id = wp_insert_attachment($attachment, $destination, $product_id);

    if (is_wp_error($attach_id)) {
      error_log('Error inserting attachment: ' . $attach_id->get_error_message());
      @unlink($destination);
      return null; // Return null on error
    }

    // Include required libraries for generating attachment metadata
    require_once(ABSPATH . 'wp-admin/includes/image.php');

    // Generate metadata for the attachment and update the database record
    $attach_data = wp_generate_attachment_metadata($attach_id, $destination);
    wp_update_attachment_metadata($attach_id, $attach_data);

    // Save the unique image key as post meta
    update_post_meta($attach_id, 'unique_image_key', $unique_image_key);

    // Return the attachment ID
    return $attach_id;
  }

  /**
   * Retrieves the attachment ID based on the Square image ID, parent ID, and image URL.
   *
   * @param string $square_image_id The Square image ID.
   * @param int    $parent_id       The parent product or variation ID.
   * @param string $image_url       The URL of the image.
   * @return int|false             The attachment ID if found, false otherwise.
   */
  private static function get_attachment_id_by_unique_image_key($square_image_id, $parent_id, $image_url)
  {
    // Normalize the URL as it was during unique_image_key generation
    $normalized_url = self::normalize_url($image_url);
    $unique_image_key = md5($square_image_id . '|' . $parent_id . '|' . $normalized_url);

    $args = [
      'post_type'      => 'attachment',
      'post_status'    => 'inherit',
      'meta_query'     => [
        [
          'key'   => 'unique_image_key',
          'value' => $unique_image_key,
        ],
      ],
      'fields'         => 'ids',
      'posts_per_page' => 1,
    ];

    $query = new \WP_Query($args);

    if (!empty($query->posts)) {
      return (int) $query->posts[0];
    }

    return false;
  }

  /**
   * Get the MIME type of a file.
   *
   * @param string $file Path to the file.
   * @return string MIME type of the file.
   */
  private function get_mime_type($file)
  {
    // Check if `finfo_file` is available
    if (function_exists('finfo_file')) {
      $finfo = finfo_open(FILEINFO_MIME_TYPE);
      $mimeType = finfo_file($finfo, $file);
      finfo_close($finfo);
      return $mimeType;
    }

    // Fallback: Use `mime_content_type` if available
    if (function_exists('mime_content_type')) {
      return mime_content_type($file);
    }

    // Fallback: Guess MIME type based on file extension
    $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
    $mime_types = [
      'jpg' => 'image/jpeg',
      'jpeg' => 'image/jpeg',
      'png' => 'image/png',
      'gif' => 'image/gif',
      'bmp' => 'image/bmp',
      'webp' => 'image/webp',
      'pdf' => 'application/pdf',
      'doc' => 'application/msword',
      'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'xls' => 'application/vnd.ms-excel',
      'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      'txt' => 'text/plain',
      'csv' => 'text/csv',
      'zip' => 'application/zip',
      'tar' => 'application/x-tar',
      'gz' => 'application/gzip',
      'rar' => 'application/vnd.rar',
      '7z' => 'application/x-7z-compressed',
      'mp3' => 'audio/mpeg',
      'wav' => 'audio/wav',
      'ogg' => 'audio/ogg',
      'mp4' => 'video/mp4',
      'mov' => 'video/quicktime',
      'avi' => 'video/x-msvideo',
      'mkv' => 'video/x-matroska',
      'html' => 'text/html',
      'css' => 'text/css',
      'js' => 'application/javascript',
      'json' => 'application/json',
      'xml' => 'application/xml',
      'svg' => 'image/svg+xml',
    ];

    return $mime_types[$extension] ?? 'application/octet-stream'; // Default MIME type if not found
  }

  private static function get_attachment_by_unique_key($unique_image_key, $product_id)
  {
    $args = [
      'post_type'      => 'attachment',
      'post_status'    => 'inherit',
      'post_parent'    => $product_id,
      'meta_query'     => [
        [
          'key'     => 'unique_image_key',
          'value'   => $unique_image_key,
          'compare' => '=',
        ],
      ],
      'posts_per_page' => 1, // Fetch only one result
      'fields'         => 'ids',
    ];

    $query = new \WP_Query($args);

    if (!empty($query->posts) && count($query->posts) === 1) {
      return get_post($query->posts[0]); // Return the attachment post
    }

    // If multiple results or no result, return false
    return false;
  }

  private static function normalize_url($url)
  {
    return trim($url); // Use the URL as-is, including query parameters
  }

  /**
   * Update (replace) product modifiers based on data in wc_product_data['modifiers'].
   *
   * This DOES update the "square_mod_id" / "square_mod_list_id" fields
   * so that your ProductModifiers class can display/edit them.
   *
   * Also sets the stock to 0 ONLY if that specific modifier is sold out
   * at the current location. (No global override for all modifiers.)
   */
  private function update_product_modifiers(\WC_Product $product, array $wc_product_data)
  {
    // Clear existing modifiers
    update_post_meta($product->get_id(), '_pws_modifier_sets', []);

    if (empty($wc_product_data['modifiers']) || !is_array($wc_product_data['modifiers'])) {
      return;
    }

    $settings         = get_option('square-woo-sync_settings', []);
    $current_location = $settings['location'] ?? '';

    $new_sets = [];

    // Loop each modifier list
    foreach ($wc_product_data['modifiers'] as $mod_list) {
      $list_name   = $mod_list['name'] ?? '';
      $list_id     = $mod_list['modifier_list_id'] ?? '';
      $selection_type = strtoupper($mod_list['selection_type'] ?? 'SINGLE');
      $single_choice  = ($selection_type === 'MULTIPLE') ? false : true;

      // Possibly the entire list might have location_overrides
      // We'll store them in a var so we can apply them to *all* modifiers in this list if you prefer that logic.
      // or handle them individually if each mod has location overrides. 
      $list_location_overrides = $mod_list['location_overrides'] ?? [];

      $mod_options = [];
      if (!empty($mod_list['modifiers']) && is_array($mod_list['modifiers'])) {
        foreach ($mod_list['modifiers'] as $single_modifier) {
          $mod_name      = $single_modifier['name']  ?? '';
          $mod_price     = isset($single_modifier['price']) ? (float) $single_modifier['price'] : 0.0;
          $square_mod_id = $single_modifier['id']    ?? '';

          // Check if the single_modifier also has location_overrides
          $modifier_overrides = $single_modifier['location_overrides'] ?? [];

          // Start with no stock limit
          $modifier_stock = '';

          // 1) If the single modifier has location_overrides, check them first
          if (!empty($modifier_overrides)) {
            foreach ($modifier_overrides as $loc_override) {
              if (!empty($loc_override['location_id']) && $loc_override['location_id'] === $current_location) {
                if (!empty($loc_override['sold_out']) && $loc_override['sold_out'] === true) {
                  $modifier_stock = 0; // out of stock
                }
              }
            }
          }
          // 2) Otherwise, if the list has location_overrides that apply to all modifiers in that list
          elseif (!empty($list_location_overrides)) {
            foreach ($list_location_overrides as $loc_override) {
              if (!empty($loc_override['location_id']) && $loc_override['location_id'] === $current_location) {
                if (!empty($loc_override['sold_out']) && $loc_override['sold_out'] === true) {
                  $modifier_stock = 0;
                }
              }
            }
          }

          $mod_options[] = [
            'name'               => $mod_name,
            'price'              => $mod_price,
            'stock'              => $modifier_stock,
            'square_mod_id'      => $square_mod_id,
            'square_mod_list_id' => $list_id,
          ];
        }
      }

      $new_sets[] = [
        'set_name'      => $list_name,
        'single_choice' => $single_choice,
        'square_mod_list_id' => $list_id, // Store the list ID at the set level
        'options'       => $mod_options,
      ];
    }

    update_post_meta($product->get_id(), '_pws_modifier_sets', $new_sets);
  }
}
