<?php
/**
 * Product Sync Handler
 *
 * @package FKWCSQ\Sync
 */

namespace FKWCSQ\Includes\Sync;

use FKWCSQ\Includes\Traits\Instance;
use WC_Product;
use WC_Product_Simple;
use function wc_get_product;
use function get_post_meta;
use function update_post_meta;
use function is_wp_error;
use function do_action;
use function get_posts;
use function get_term_meta;
use function get_terms;
use function get_term;
use function update_term_meta;
use function get_attached_file;
use function file_exists;
use function wc_get_products;
use function download_url;
use function media_handle_sideload;
use function usleep;

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

/**
 * Handles product synchronization between WooCommerce and Square
 */
class Product {
	use Instance;

	/**
	 * Square API instance
	 *
	 * @var \FKWCSQ\Includes\API
	 */
	private $api;

	/**
	 * Constructor
	 */
	private function __construct() {
		$this->api = \FKWCSQ\Includes\API::get_instance();

		// Add auto-sync hook
		add_action( 'fkwcsq_auto_sync_product_to_square', array( $this, 'auto_sync_product_to_square' ) );
	}


	/**
	 * Auto-sync product to Square when checkbox is enabled
	 *
	 * @param int $product_id Product ID
	 */
	public function auto_sync_product_to_square( $product_id ) {
		// Check if product should sync with Square
		if ( ! \FKWCSQ\Square::should_sync_product_with_square( $product_id ) ) {
			return;
		}

		// Perform the sync
		$this->sync_product_to_square( $product_id );
	}

	/**
	 * Sync product from WooCommerce to Square
	 *
	 * @param int $product_id WooCommerce product ID
	 * @throws \Exception If the Square API call returns an error.
	 */
	public function sync_product_to_square( $product_id ) {
		try {
			// Check if product should sync with Square
			if ( ! \FKWCSQ\Square::should_sync_product_with_square( $product_id ) ) {
				return false;
			}

			$product = wc_get_product( $product_id );
			if ( ! $product ) {
				return false;
			}

			$image_ids = $this->sync_images_to_square( $product );

			$square_id           = get_post_meta( $product_id, '_square_id', true );
			$square_variation_id = get_post_meta( $product_id, '_square_variation_id', true );
			$product_data        = $this->prepare_product_data( $product );

			if ( ! empty( $image_ids ) ) {
				$product_data['image_ids'] = $image_ids;
			}

			if ( $square_id ) {
				$response = $this->api->update_product( $square_id, $product_data, $square_variation_id );
			} else {
				$response = $this->api->create_product( $product_data );
				if ( ! is_wp_error( $response ) && isset( $response['id'] ) ) {
					update_post_meta( $product_id, '_square_id', $response['id'] );
				}
			}

			if ( is_wp_error( $response ) ) {
				return false;
			}

			update_post_meta( $product_id, '_square_id', $response['id'] );
			do_action( 'fkwcsq_product_synced_to_square', $product_id, $response );
			return true;
		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Sync product from Square to WooCommerce
	 *
	 * @param array $square_data Square product data
	 * @return bool Success status
	 */
	public function sync_product_from_square( $square_data ) {
		try {
			$product_id   = $this->find_product_by_square_id( $square_data['id'] );
			$product_data = $this->prepare_woocommerce_data( $square_data );

			if ( $product_id ) {
				$product = wc_get_product( $product_id );
				if ( $product ) {
					foreach ( $product_data as $key => $value ) {
						$product->{"set_$key"}( $value );
					}
					$product->save();
					// Set sync with Square to 'yes' for existing products imported from Square
					update_post_meta( $product->get_id(), '_wc_square_synced', 'yes' );
				}
			} else {
				$product = new WC_Product_Simple();
				foreach ( $product_data as $key => $value ) {
					$product->{"set_$key"}( $value );
				}
				$product->save();
				update_post_meta( $product->get_id(), '_square_variation_id', $square_data['variation_id'] );
				update_post_meta( $product->get_id(), '_square_id', $square_data['id'] );
				// Set sync with Square to 'yes' by default for imported products
				update_post_meta( $product->get_id(), '_wc_square_synced', 'yes' );
			}

			// Handle product images
			if ( ! empty( $square_data['image_ids'] ) ) {
				$attachment_ids = array();
				foreach ( $square_data['image_ids'] as $image_id ) {
					$attachment_id = $this->import_image_from_square( $image_id );
					if ( $attachment_id ) {
						$attachment_ids[] = $attachment_id;
					}
				}

				if ( ! empty( $attachment_ids ) ) {
					// Set the first image as the product image
					$product->set_image_id( $attachment_ids[0] );

					// Set remaining images as gallery images
					if ( count( $attachment_ids ) > 1 ) {
						$product->set_gallery_image_ids( array_slice( $attachment_ids, 1 ) );
					}

					$product->save();
				}
			}

			if ( ! empty( $square_data['variation_id'] ) ) {
				$inventory_response = $this->api->retrieve_inventory_count( $square_data['variation_id'] );

				if ( ! is_wp_error( $inventory_response ) && ! empty( $inventory_response['counts'] ) ) {
					$current_location_id = (string) $this->api->get_location_id();

					$stock     = 0;
					$has_stock = false;

					$counts = $inventory_response['counts'];

					foreach ( $counts as $index => $count ) {
						if ( is_object( $count ) ) {
							$count = json_decode( json_encode( $count ), true );
						}

						if ( isset( $count['location_id'] ) && isset( $count['state'] ) && isset( $count['quantity'] ) ) {
							if ( (string) $count['location_id'] === $current_location_id && 'IN_STOCK' === $count['state'] ) {
								$stock     = (float) $count['quantity'];
								$has_stock = $stock > 0;
								break;
							}
						}
					}

					$product->set_manage_stock( true );
					$product->set_stock_quantity( $stock );
					$product->set_stock_status( $has_stock ? 'instock' : 'outofstock' );
					$product->save();
				}
			}

			do_action( 'fkwcsq_product_synced_from_square', $product->get_id(), $square_data );
			return true;
		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Delete product from Square
	 *
	 * @param int $product_id WooCommerce product ID
	 * @return bool Success status
	 * @throws \Exception If the Square API call returns an error.
	 */
	public function delete_product_from_square( $product_id ) {
		try {
			// Check if product has a Square ID
			$square_id = get_post_meta( $product_id, '_square_id', true );

			if ( ! $square_id ) {
				return false;
			}

			// Call the API to delete the product
			$response = $this->api->delete_product( $square_id );

			// Log the API response
			if ( is_wp_error( $response ) ) {
				return false;
			}

			// Clean up metadata
			delete_post_meta( $product_id, '_square_id' );

			// Fire action for other integrations
			do_action( 'fkwcsq_product_deleted_from_square', $product_id );
			return true;
		} catch ( \Exception $e ) {
			return false;
		}
	}
	/**
	 * Prepare product data for Square API
	 *
	 * @param WC_Product $product WooCommerce product
	 *
	 * @return array Square product data
	 */
	private function prepare_product_data( $product ) {
		$woo_category_ids = $product->get_category_ids();
		return array(
			'name'         => $product->get_name(),
			'description'  => $product->get_description(),
			'price'        => $product->get_price(),
			'id'           => $product->get_id(),
			'sku'          => $product->get_sku(),
			'category_id'  => $this->get_square_category_id( $woo_category_ids ),
			'category_ids' => $this->get_square_category_ids( $woo_category_ids ),
			'variations'   => $this->prepare_variations_data( $product ),
		);
	}

	/**
	 * Prepare WooCommerce product data from Square data
	 *
	 * @param array $square_data Square product data
	 *
	 * @return array WooCommerce product data
	 */
	private function prepare_woocommerce_data( $square_data ) {
		return array(
			'name'          => $square_data['name'],
			'description'   => $square_data['description'] ?? '',
			'regular_price' => $square_data['price'],
			'sku'           => $square_data['sku'] ?? '',
			'category_ids'  => $this->get_woocommerce_category_ids( $square_data['category_id'] ),
		);
	}

	/**
	 * Find WooCommerce product by Square ID
	 *
	 * @param string $square_id Square product ID
	 *
	 * @return int|false Product ID or false if not found
	 */
	private function find_product_by_square_id( $square_id ) {
		$args = array(
			'post_type'      => 'product',
			'post_status'    => 'publish',
			'meta_key'       => '_square_id', //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Necessary for Square integration lookup
			'meta_value'     => $square_id, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Necessary for Square integration lookup
			'fields'         => 'ids',
			'posts_per_page' => 1,
		);

		$products = get_posts( $args );

		return ! empty( $products ) ? $products[0] : false;
	}

	/**
	 * Get Square category ID from WooCommerce category IDs
	 *
	 * @param array $category_ids WooCommerce category IDs
	 *
	 * @return string|null Square category ID
	 */
	private function get_square_category_id( $category_ids ) {
		if ( empty( $category_ids ) ) {
			return null;
		}

		$square_category_id = get_term_meta( $category_ids[0], '_square_id', true );

		return $square_category_id ? $square_category_id : null;
	}
	/**
	 * Get Square category IDs from WooCommerce category IDs (multiple categories)
	 *
	 * @param array $category_ids WooCommerce category IDs
	 *
	 * @return array Square category IDs
	 */
	private function get_square_category_ids( $category_ids ) {
		if ( empty( $category_ids ) ) {
			return array();
		}

		$square_category_ids = array();

		foreach ( $category_ids as $woo_category_id ) {
			$square_category_id = get_term_meta( $woo_category_id, '_square_id', true );

			if ( $square_category_id ) {
				$square_category_ids[] = $square_category_id;
			}
		}

		return $square_category_ids;
	}
	/**
	 * Get WooCommerce category IDs from Square category ID
	 *
	 * @param string $square_category_id Square category ID
	 *
	 * @return array WooCommerce category IDs
	 */
	private function get_woocommerce_category_ids( $square_category_id ) {
		if ( ! $square_category_id ) {
			return array();
		}

		$args = array(
			'taxonomy'   => 'product_cat',
			'meta_key'   => '_square_id', //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Necessary for Square integration lookup
			'meta_value' => $square_category_id, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Necessary for Square integration lookup
			'fields'     => 'ids',
		);

		$categories = get_terms( $args );

		return ! is_wp_error( $categories ) ? $categories : array();
	}

	/**
	 * Prepare variations data for Square API
	 *
	 * @param WC_Product $product WooCommerce product
	 *
	 * @return array Square variations data
	 */
	private function prepare_variations_data( $product ) {
		if ( ! $product->is_type( 'variable' ) ) {
			return array();
		}

		$variations = array();
		foreach ( $product->get_children() as $variation_id ) {
			$variation = wc_get_product( $variation_id );
			if ( ! $variation ) {
				continue;
			}
			$image_ids    = $this->sync_images_to_square( $variation );
			$variations[] = array(
				'name'           => $variation->get_name(),
				'price'          => $variation->get_price(),
				'id'             => $variation->get_id(),
				'sku'            => $variation->get_sku(),
				'stock_quantity' => $variation->get_stock_quantity(),
				'image_id'       => ! empty( $image_ids ) ? $image_ids[0] : '',
			);
		}

		return $variations;
	}

	/**
	 * Sync product images to Square
	 *
	 * @param WC_Product $product WooCommerce product
	 *
	 * @return array Square image IDs
	 * @throws \Exception If the Square API call returns an error.
	 */
	private function sync_images_to_square( $product ) {
		try {
			$image_ids = array();
			$images    = array();

			// Get product images
			$product_image_id = $product->get_image_id();
			if ( $product_image_id ) {
				$images[] = $product_image_id;
			}

			// Get gallery images
			$gallery_image_ids = $product->get_gallery_image_ids();
			if ( ! empty( $gallery_image_ids ) ) {
				$images = array_merge( $images, $gallery_image_ids );
			}

			foreach ( $images as $image_id ) {
				// Check if image already exists in Square
				$square_image_id = get_post_meta( $image_id, '_square_id', true );

				if ( $square_image_id ) {
					$image_ids[] = $square_image_id;
					continue;
				}

				// Get image file path
				$image_path = get_attached_file( $image_id );
				if ( ! $image_path || ! file_exists( $image_path ) ) {
					continue;
				}

				// Upload image to Square
				$response = $this->api->upload_image( $image_path );

				if ( is_wp_error( $response ) ) {
					throw new \Exception( $response->get_error_message() );
				}

				if ( isset( $response['id'] ) ) {
					// Store Square image ID
					update_post_meta( $image_id, '_square_id', $response['id'] );
					$image_ids[] = $response['id'];
				}
			}

			return $image_ids;
		} catch ( \Exception $e ) {
			return array();
		}
	}

	/**
	 * Schedule batch sync of all products
	 *
	 * @return array Summary of sync results
	 */
	public function schedule_batch_sync() {
		try {
			$total_products = 0;
			$success_count  = 0;
			$failed_count   = 0;
			$success_ids    = array();
			$failed_ids     = array();
			$errors         = array();

			// Get all products with sync enabled
			$product_ids = wc_get_products(
				array(
					'status'     => 'publish',
					'limit'      => -1,
					'return'     => 'ids',
					'meta_query' => array( //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Necessary for Square integration batch sync
						array(
							'key'     => '_wc_square_synced',
							'value'   => 'yes',
							'compare' => '=',
						),
					),
				)
			);

			$total_products = is_array( $product_ids ) ? count( $product_ids ) : 0;

			if ( empty( $product_ids ) ) {
				return array(
					'total'       => 0,
					'success'     => 0,
					'failed'      => 0,
					'success_ids' => array(),
					'failed_ids'  => array(),
					'errors'      => array(),
				);
			}

			$batches = array_chunk( $product_ids, 20 );

			// Process batches immediately
			foreach ( $batches as $index => $batch ) {
				try {
					$batch_results  = $this->process_product_batch( $batch );
					$success_count += $batch_results['success'];
					$failed_count  += $batch_results['failed'];

					// Collect successful and failed IDs
					if ( ! empty( $batch_results['success_ids'] ) ) {
						$success_ids = array_merge( $success_ids, $batch_results['success_ids'] );
					}
					if ( ! empty( $batch_results['failed_ids'] ) ) {
						$failed_ids = array_merge( $failed_ids, $batch_results['failed_ids'] );
					}

					if ( ! empty( $batch_results['errors'] ) ) {
						$errors = array_merge( $errors, $batch_results['errors'] );
					}
				} catch ( \Exception $e ) {
					$failed_count += count( $batch );
					$failed_ids    = array_merge( $failed_ids, $batch );
					$errors[]      = 'Batch ' . ( $index + 1 ) . ': ' . $e->getMessage();
				}
			}

			return array(
				'total'       => $total_products,
				'success'     => $success_count,
				'failed'      => $failed_count,
				'success_ids' => $success_ids,
				'failed_ids'  => $failed_ids,
				'errors'      => $errors,
			);

		} catch ( \Exception $e ) {
			return array(
				'total'       => 0,
				'success'     => 0,
				'failed'      => 0,
				'success_ids' => array(),
				'failed_ids'  => array(),
				'errors'      => array( $e->getMessage() ),
			);
		}
	}

	/**
	 * Process a batch of products for syncing
	 *
	 * @param array $product_ids Array of product IDs to process
	 * @return array Array with success, failed counts and errors
	 * @throws \Exception If the Square API call returns an error.
	 */
	public function process_product_batch( $product_ids ) {
		try {
			if ( empty( $product_ids ) ) {
				return array(
					'success'     => 0,
					'failed'      => 0,
					'errors'      => array(),
					'success_ids' => array(),
					'failed_ids'  => array(),
				);
			}

			$success_count = 0;
			$failed_count  = 0;
			$errors        = array();
			$success_ids   = array();
			$failed_ids    = array();

			foreach ( $product_ids as $index => $product_id ) {

				try {
					$this->sync_product_to_square( $product_id );
					++$success_count;
					$success_ids[] = $product_id;
				} catch ( \Exception $e ) {
					++$failed_count;
					$failed_ids[] = $product_id;
					$errors[]     = sprintf( 'Product #%d (ID: %d) failed to sync: %s', $index + 1, $product_id, $e->getMessage() );
				}

				usleep( 500000 ); // 0.5 second delay
			}

			return array(
				'success'     => $success_count,
				'failed'      => $failed_count,
				'errors'      => $errors,
				'success_ids' => $success_ids,
				'failed_ids'  => $failed_ids,
			);

		} catch ( \Exception $e ) {
			// Re-throw to allow the parent method to catch and handle this error
			throw $e;
		}
	}

	/**
	 * Import all products from Square and sync to WooCommerce
	 *
	 * @return int|\WP_Error Number of products imported or WP_Error on failure
	 * @throws \Exception If the Square API call returns an error.
	 */
	public function import_all_from_square() {
		try {
			$success_count = 0;
			$failed_count  = 0;
			$errors        = array();

			// Get all products from Square
			$square_products = $this->api->get_all_square_products();
			if ( is_wp_error( $square_products ) ) {
				throw new \Exception( $square_products->get_error_message() );
			}

			foreach ( $square_products as $square_product ) {
				try {
					if ( method_exists( $square_product, 'getType' ) && $square_product->getType() === 'ITEM' ) {
						$variations = $square_product->getItemData()->getVariations();
						if ( empty( $variations ) ) {
							continue;
						}

						$primary_variation = $variations[0];
						$item_data         = $square_product->getItemData();
						$variation_data    = $primary_variation->getItemVariationData();

						$price_money = $variation_data->getPriceMoney();
						$price       = ( $price_money && $price_money->getAmount() !== null ) ? $price_money->getAmount() / 100 : 0;

						$data = array(
							'id'           => $square_product->getId(),
							'name'         => $item_data->getName() ?: '',
							'description'  => $item_data->getDescription() ?: '',
							'price'        => $price,
							'sku'          => $variation_data->getSku() ?: '',
							'category_id'  => $item_data->getCategoryId() ?: '',
							'image_ids'    => $item_data->getImageIds() ?: array(),
							'variation_id' => $primary_variation->getId(),
						);

						// Check if product already exists in WooCommerce
						$existing_product_id = $this->find_product_by_square_id( $data['id'] );

						if ( $existing_product_id ) {
							$product = wc_get_product( $existing_product_id );
							if ( $product ) {
								$product_data = $this->prepare_woocommerce_data( $data );
								foreach ( $product_data as $key => $value ) {
									$product->{"set_$key"}( $value );
								}
								$product->save();
								if ( ! empty( $data['variation_id'] ) ) {
									$inventory_response = $this->api->retrieve_inventory_count( $data['variation_id'] );
									if ( ! is_wp_error( $inventory_response ) && ! empty( $inventory_response['counts'] ) ) {
										$current_location_id = (string) $this->api->get_location_id();
										$counts              = $inventory_response['counts'];

										$in_stock_counts = array_filter(
											$counts,
											function ( $count_item ) use ( $current_location_id ) {
												$count_data = is_object( $count_item ) ? json_decode( json_encode( $count_item ), true ) : $count_item;

												return isset( $count_data['location_id'] ) && isset( $count_data['state'] ) && isset( $count_data['quantity'] ) && (string) $count_data['location_id'] === $current_location_id && 'IN_STOCK' === $count_data['state'];
											}
										);

										if ( ! empty( $in_stock_counts ) ) {
											$first_match = reset( $in_stock_counts );
											$count_data  = is_object( $first_match ) ? json_decode( json_encode( $first_match ), true ) : $first_match;
											$stock       = (float) $count_data['quantity'];
											$has_stock   = $stock > 0;
										} else {
											$stock     = 0;
											$has_stock = false;
										}

										$product->set_manage_stock( true );
										$product->set_stock_quantity( $stock );
										$product->set_stock_status( $has_stock ? 'instock' : 'outofstock' );
										$product->save();
									}

									update_post_meta( $product->get_id(), '_square_variation_id', $data['variation_id'] );
								}
								// Set sync with Square to 'yes' for existing products imported from Square
								update_post_meta( $product->get_id(), '_wc_square_synced', 'yes' );
							}
						} else {
							$this->sync_product_from_square( $data );
						}

						++$success_count;
					}
				} catch ( \Exception $e ) {
					++$failed_count;
					$errors[] = 'Product ' . $square_product->getId() . ': ' . $e->getMessage();
				}

				// Add a small delay to avoid API rate limits
				usleep( 200000 ); // 200ms pause between products
			}

			return $success_count;
		} catch ( \Exception $e ) {
			return new \WP_Error( 'import_error', $e->getMessage() );
		}
	}

	/**
	 * Import product image from Square
	 *
	 * @param string $square_image_id Square image ID
	 * @return int|false WordPress attachment ID or false on failure
	 */
	private function import_image_from_square( $square_image_id ) {
		try {
			// Ensure WordPress media functions are available
			if ( ! function_exists( 'media_handle_sideload' ) ) {
				require_once ABSPATH . 'wp-admin/includes/media.php';
				require_once ABSPATH . 'wp-admin/includes/file.php';
				require_once ABSPATH . 'wp-admin/includes/image.php';
			}

			// Ensure WordPress post functions are available
			if ( ! function_exists( 'get_posts' ) ) {
				require_once ABSPATH . 'wp-includes/post.php';
			}

			// Ensure WordPress meta functions are available
			if ( ! function_exists( 'update_post_meta' ) ) {
				require_once ABSPATH . 'wp-includes/post.php';
			}

			// Check if image already exists in WordPress
			$existing_attachment_id = $this->find_image_by_square_id( $square_image_id );
			if ( $existing_attachment_id ) {
				return $existing_attachment_id;
			}

			// Get image data from Square
			$image_data = $this->api->get_image( $square_image_id );
			if ( is_wp_error( $image_data ) ) {
				return false;
			}

			// Download image from Square
			$image_url = $image_data['url'];
			$temp_file = download_url( $image_url );

			if ( is_wp_error( $temp_file ) ) {
				return false;
			}

			// Prepare file data
			$file_array = array(
				'name'     => basename( $image_url ),
				'tmp_name' => $temp_file,
			);

			// Upload image to WordPress media library
			$attachment_id = media_handle_sideload( $file_array, 0 );
			if ( is_wp_error( $attachment_id ) ) {
				if ( file_exists( $temp_file ) ) {
					wp_delete_file( $temp_file );
				}
				return false;
			}

			update_post_meta( $attachment_id, '_square_image_id', $square_image_id );
			return $attachment_id;

		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Find WordPress attachment by Square image ID
	 *
	 * @param string $square_image_id Square image ID
	 * @return int|false WordPress attachment ID or false if not found
	 */
	private function find_image_by_square_id( $square_image_id ) {
		$args = array(
			'post_type'      => 'attachment',
			'post_status'    => 'inherit',
			'meta_key'       => '_square_image_id', //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Necessary for Square integration lookup
			'meta_value'     => $square_image_id, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Necessary for Square integration lookup
			'fields'         => 'ids',
			'posts_per_page' => 1,
		);

		$attachments = get_posts( $args );
		return ! empty( $attachments ) ? $attachments[0] : false;
	}
}
