<?php

namespace CommerceBird\Admin\Actions\Sync;

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

use CommerceBird\Admin\Actions\Ajax\ExactOnlineAjax;
use CommerceBird\Admin\Connectors\Connector;

/**
 * Handles synchronization of products, customers, and orders between WooCommerce and Exact Online.
 */
class ExactOnlineSync {

	/**
	 * Whether to enable stock synchronization.
	 *
	 * @var bool Whether to enable stock synchronization.
	 * @since 2.6.5
	 */
	public static bool $enable_eo_stock;

	/**
	 * Construct to load options from wp_options
	 */
	public function __construct() {
		// Load any required options or settings here if needed.
		self::$enable_eo_stock = get_option( 'cmbird_enable_eo_stock', false );
	}

	/**
	 * Sync data from Exact Online.
	 *
	 * @param string $type product|customer.
	 * @param array  $data to sync from Exact Online.
	 * @param bool   $import import or update.
	 * @return mixed
	 */
	public function sync( string $type, array $data, bool $import = false ) {
		if ( empty( $type ) ) {
			return false;
		}
		if ( $import ) {
			$this->import( $type, $data );
		} else {
			$this->update( $type, $data );
		}
	}
	/**
	 * Import data from Exact Online.
	 *
	 * For product data will be like,
	 * {
	 * "Code":string,
	 * "Description": string,
	 * "ID": string,
	 * "IsSalesItem": bool,
	 * "PictureName": null|string,
	 * "PictureUrl": string,
	 * "StandardSalesPrice": float,
	 * "Stock": int
	 * },
	 *
	 * for Order data will be like,
	 * {
	 * "MainContact": null|string,
	 * "Email": null|string,
	 * "ID": string,
	 * "Name": string,
	 * "AddressLine1": null|string,
	 * "AddressLine2": null|string,
	 * "City": string,
	 * "Country": string,
	 * "Phone": null|string,
	 * "Postcode": string
	 * }
	 *
	 * @param string $type of provided data.
	 * @param array  $data of import.
	 * @return mixed
	 */
	public static function import( string $type, array $data ) {
		// add logging.
		// $fd = fopen( __DIR__ . '/import.txt', 'a+' );
		$endpoint = '';
		$payload  = array();

		switch ( $type ) {
			case 'product':
				$endpoint      = '/wc/v3/products/batch';
				$filtered_data = array_filter(
					$data,
					function ( $item ) {
						// Exclude item if product already exists via SKU.
						return ! wc_get_product_id_by_sku( $item['Code'] );
					}
				);
				$payload       = array(
					'create' => array_map(
						function ( $item ) {
								// Skip image import if PictureName is 'placeholder_item'.
								$images = array();
							if ( isset( $item['PictureName'] ) && 'placeholder_item' !== $item['PictureName'] ) {
									$image_id = self::get_existing_image_id( $item['PictureName'] );
									// If image exists, add it to the images array, else upload the image and add it to the images array.
								if ( $image_id ) {
									$images[] = array( 'id' => $image_id );
								} else {
									$image_id = self::upload_image( $item['PictureUrl'], $item['PictureName'] );
									// If image upload is successful, add it to the images array.
									if ( $image_id ) {
										$images[] = array( 'id' => $image_id );
									}
								}
							}
								// Check if category exists with $item['ItemGroupDescription'], else create it and get the ID.
							if ( isset( $item['ItemGroupDescription'] ) ) {
								$term = get_term_by( 'name', $item['ItemGroupDescription'], 'product_cat' );
								$term_id = $term->term_id;
								if ( empty( $term_id ) ) {
									$term = wp_insert_term(
										$item['ItemGroupDescription'],
										'product_cat',
										array(
											'parent' => 0,
										)
									);
									$term_id = $term['term_id'];
								}
							} else {
								$term_id = 0;
							}
								// if stock exists then set manage_stock to true && if stock is enabled in settings.
								$manage_stock = ( isset( $item['Stock'] ) && self::$enable_eo_stock ) ? true : false;
								$item_stock = isset( $item['Stock'] ) ? $item['Stock'] : null;
								return array(
									'name'           => $item['Description'],
									'sku'            => $item['Code'],
									'status'         => 'publish',
									'type'           => 'simple',
									'regular_price'  => (string) $item['StandardSalesPrice'],
									'images'         => $images,
									'stock_quantity' => $item_stock,
									'manage_stock'   => $manage_stock,
									'categories'     => array(
										array(
											'id' => $term_id,
										),
									),
									'meta_data'      => array(
										array(
											'key'   => 'eo_item_id',
											'value' => $item['ID'],
										),
										array(
											'key'   => '_cogs_total_value',
											'value' => $item['CostPriceStandard'],
										),
										array(
											'key'   => 'eo_unit',
											'value' => $item['Unit'],
										),
									),
								);
						},
						$filtered_data
					),
				);
				break;

			case 'customer':
				$endpoint      = '/wc/v3/customers/batch';
				$filtered_data = array_filter(
					$data,
					function ( $item ) {
						// Exclude item if customer already exists via email.
						return ! get_user_by( 'email', $item['Email'] );
					}
				);
				$payload       = array(
					'create' => array_map(
						function ( $item ) {
							if ( empty( $item['Email'] ) ) {
									return null;
							}
							if ( ! empty( $item['VATNumber'] ) ) {
								$first_name = '';
								$last_name = '';
								$company = $item['Name'];
							} else {
								$names = explode( ' ', $item['Name'] );
								$first_name = array_shift( $names ); // Take the first word as first name.
								$last_name = implode( ' ', $names ); // Join the rest as last name.
							}
							// generate username based on email.
							$username = explode( '@', $item['Email'] )[0];
							// add random number to username if it already exists.
							$existing_user = get_user_by( 'login', $username );
							if ( $existing_user ) {
								$username .= '_' . wp_rand( 1000, 9999 );
							}
							$address = array(
								'first_name' => $first_name,
								'last_name'  => $last_name,
								'company'    => $company ?? '',
								'address_1'  => $item['AddressLine1'] ?? '',
								'address_2'  => $item['AddressLine2'] ?? '',
								'city'       => $item['City'] ?? '',
								'country'    => $item['Country'] ?? '',
								'postcode'   => $item['Postcode'] ?? '',
								'phone'      => $item['Phone'] ?? '',
								'email'      => $item['Email'],
							);
							return array(
								'email'      => $item['Email'],
								'first_name' => $first_name,
								'last_name'  => $last_name,
								'username'   => $username,
								'billing'    => $address,
								'shipping'   => $address,
								'meta_data'  => array(
									array(
										'key'   => 'eo_account_id',
										'value' => $item['ID'],
									),
									array(
										'key'   => 'eo_contact_id',
										'value' => $item['MainContact'] ?? '',
									),
								),
							);
						},
						array_filter( $filtered_data, fn( $item ) => ! empty( $item['Email'] ) )
					),
				);
				break;

			default:
				return false;
		}

		if ( empty( $payload['create'] ) ) {
			return false;
		}
		// log the payload.
		// fwrite( $fd, print_r( $payload, true ) );
		add_filter( 'woocommerce_rest_check_permissions', '__return_true' );
		$request = new \WP_REST_Request( 'POST', $endpoint );
		$request->set_body_params( $payload );
		$response = rest_do_request( $request );
		remove_filter( 'woocommerce_rest_check_permissions', '__return_true' );
		// log the response.
		// fwrite( $fd, print_r( $response, true ) );
		// fclose( stream: $fd );
		return $response;
	}

	/**
	 * Update data based on Exact Online.
	 *
	 * @param string $type of provided data.
	 * @param array  $data to update in Exact Online.
	 * @return mixed
	 */
	public function update( string $type, array $data ) {
		// $fd = fopen( __DIR__ . '/update.txt', 'w+' );
		$endpoint = '';
		$payload  = array();
		// log data.
		// fwrite( $fd, print_r( $data, true ) );

		switch ( $type ) {
			case 'product':
				// Track all SKUs from Exact Online for this batch.
				$exact_skus           = array_column( $data, 'Code' );
				$existing_synced_skus = get_option( 'cmbird_eo_synced_skus', array() );
				if ( ! is_array( $existing_synced_skus ) ) {
					$existing_synced_skus = array();
				}
				$existing_synced_skus = array_merge( $existing_synced_skus, $exact_skus );
				update_option( 'cmbird_eo_synced_skus', array_values( array_unique( $existing_synced_skus ) ), false );

				$filtered_data = array_filter(
					$data,
					function ( $item ) {
						// Exclude item if product does not exists via SKU.
						return wc_get_product_id_by_sku( $item['Code'] );
					}
				);
				$endpoint      = '/wc/v3/products/batch';
				$payload       = array(
					'update' => array_map(
						function ( $item ) {
							// Check if product exists via SKU, else get the product ID by title.
							$product_id = wc_get_product_id_by_sku( $item['Code'] ) ? wc_get_product_id_by_sku( $item['Code'] ) : self::get_product_id_by_title( $item['Description'] );
							// Mark product as synced with current timestamp.
							update_post_meta( $product_id, '_eo_last_synced', time() );
							// Featured image.
							if ( isset( $item['PictureName'] ) && 'placeholder_item' !== $item['PictureName'] ) {
									$image_id = self::get_existing_image_id( $item['PictureName'] );
									// If image exists, add it to the images array, else upload the image and add it to the images array.
								if ( $image_id ) {
									set_post_thumbnail( $product_id, $image_id );
									update_post_meta( $image_id, '_wp_attachment_image_alt', $item['Description'] );
									update_post_meta( $product_id, '_thumbnail_id', $image_id );
									wp_update_image_subsizes( $image_id );
								} else {
									$image_id = self::upload_image( $item['PictureUrl'], $item['PictureName'] );
									// If image upload is successful, add it to the images array.
									if ( $image_id ) {
										set_post_thumbnail( $product_id, $image_id );
										update_post_meta( $image_id, '_wp_attachment_image_alt', $item['Description'] );
										update_post_meta( $product_id, '_thumbnail_id', $image_id );
										wp_update_image_subsizes( $image_id );
									}
								}
							}
							// update product category.
							if ( isset( $item['ItemGroupDescription'] ) ) {
								// Check if term exists by name.
								$term = get_term_by( 'name', $item['ItemGroupDescription'], 'product_cat' );
								$term_id = $term->term_id;
								if ( empty( $term_id ) ) {
									$term = wp_insert_term(
										$item['ItemGroupDescription'],
										'product_cat',
										array(
											'parent' => 0,
										)
									);
									$term_id = $term['term_id'];
								}
								// update product category directly.
								wp_set_object_terms( $product_id, $term_id, 'product_cat' );
							}
							$stock_status = '';
							$backorders_allowed = get_post_meta( $product_id, '_backorders', true ) === 'yes';
							if ( isset( $item['Stock'] ) ) {
								if ( self::$enable_eo_stock ) {
									if ( $item['Stock'] <= 0 ) {
										$stock_status = $backorders_allowed ? 'outofstock' : 'instock';
									} else {
										$stock_status = 'instock';
									}
								}
							}
							return array(
								'id'             => $product_id,
								'regular_price'  => (string) $item['StandardSalesPrice'],
								'stock_quantity' => ( isset( $item['Stock'] ) && self::$enable_eo_stock ) ? (int) $item['Stock'] : null,
								'manage_stock'   => ( isset( $item['Stock'] ) && self::$enable_eo_stock ) ? true : false,
								'stock_status'   => $stock_status,
								'name'           => $item['Description'],
								'slug'           => sanitize_title( $item['Description'] ),
								'meta_data'      => array(
									array(
										'key'   => 'eo_item_id',
										'value' => $item['ID'],
									),
									array(
										'key'   => '_cogs_total_value',
										'value' => $item['CostPriceStandard'],
									),
									array(
										'key'   => 'eo_unit',
										'value' => $item['Unit'],
									),
								),

							);
						},
						$filtered_data
					),
				);
				break;

			case 'customer':
				$endpoint = '/wc/v3/customers/batch';
				$payload  = array(
					'update' => array_map(
						function ( $item ) {
							$user = get_user_by( 'email', $item['Email'] );
							return $user ? array(
								'id'        => $user->ID,
								'meta_data' => array(
									array(
										'key'   => 'eo_account_id',
										'value' => $item['ID'],
									),
									array(
										'key'   => 'eo_contact_id',
										'value' => $item['MainContact'] ?? '',
									),
								),
								// if $item['VATNumber'] is not empty then update the billing company name.
								'billing'   => ! empty( $item['VATNumber'] ) ? array(
									'company' => $item['Name'],
								) : null,
							) : null;
						},
						array_filter( $data, fn( $item ) => get_user_by( 'email', $item['Email'] ) )
					),
				);
				break;

			case 'orders':
				$endpoint = '/wc/v3/orders/batch';
				$payload  = array(
					'update' => array_map(
						function ( $item ) {
							return array(
								'id'        => $item['Description'],
								'meta_data' => array(
									array(
										'key'   => 'eo_order_id',
										'value' => $item['OrderID'],
									),
									array(
										'key'   => 'eo_order_number',
										'value' => $item['OrderNumber'],
									),
								),
							);
						},
						$data
					),
				);
				break;

			default:
				return false;
		}
		// fwrite( $fd, print_r( $payload, true ) );
		if ( empty( $payload['update'] ) ) {
			return false;
		}
		add_filter( 'woocommerce_rest_check_permissions', '__return_true' );
		$request = new \WP_REST_Request( 'POST', $endpoint );
		$request->set_body_params( $payload );
		$response = rest_do_request( $request );
		remove_filter( 'woocommerce_rest_check_permissions', '__return_true' );
		// fwrite( $fd, 'response: ' . print_r( $response, true ) );
		// fclose( $fd );
		return $response;
	}

	private static function get_product_id_by_title( string $product_title ) {
		// Set up the query arguments.
		$args = array(
			'post_type'      => 'product',
			'posts_per_page' => 1,
			'fields'         => 'ids',
			's'              => $product_title, // Search by product title.
		);

		// Run the query.
		$query = new \WP_Query( $args );

		// Get the product ID from the query results.
		$product_id = $query->post_count > 0 ? $query->posts[0] : 0;

		// Reset post data.
		wp_reset_postdata();

		return $product_id;
	}

	public static function get_payment_status_via_cron() {
		// execute get_payment_status of ExactOnlineAjax class.
		$ajax = new ExactOnlineAjax();
		$ajax->get_payment_status();
	}

	/**
	 * Process the payment status of the order via Exact Online.
	 *
	 * @param array $
	 * @return void
	 */
	public static function cmbird_payment_status() {
		$args     = func_get_args();
		$order_id = $args[0];
		if ( empty( $order_id ) ) {
			return;
		}
		$order             = wc_get_order( $order_id );
		$object            = array();
		$order_id          = $order->get_id();
		$object['OrderID'] = $order_id;
		$customer_id       = $order->get_customer_id();
		// get the eo_account_id from the user meta.
		$object['AccountID'] = get_user_meta( $customer_id, 'eo_account_id', true );
		$response            = ( new Connector() )->payment_status( $object );
		// check response contains "Payment_Status" key.
		if ( ! isset( $response['Payment_Status'] ) ) {
			return;
		}
		// if response is Paid then update the order status to completed.
		if ( 'Paid' === $response['Payment_Status'] ) {
			// set order as paid.
			if ( $order->get_status() === 'completed' ) {
				return;
			}
			$order->payment_complete();
			$order->update_status( 'completed', __( 'Payment processed in Exact Online', 'commercebird' ) );
			$order->save();
		} elseif ( 'Unpaid' === $response['Payment_Status'] ) {
			if ( $order->get_status() === 'on-hold' ) {
				return;
			}
			$order->update_status( 'on-hold', __( 'Payment not processed in Exact Online', 'commercebird' ) );
			$order->save();
		}
	}

	/**
	 * Check if the image exists in the media library.
	 *
	 * @param string $picture_name
	 * @return int|false Attachment ID if exists, false otherwise
	 */
	private static function get_existing_image_id( $picture_name ) {
		global $wpdb;

		// Strip extension from picture_name.
		$picture_title = pathinfo( $picture_name, PATHINFO_FILENAME );

		// Prepare a LIKE match to catch sanitized versions.
		$like = '%' . $wpdb->esc_like( $picture_title ) . '%';

		$attachment_id = $wpdb->get_var(
			$wpdb->prepare(
				"
            SELECT ID FROM {$wpdb->posts}
            WHERE post_type = 'attachment'
            AND post_title LIKE %s
            ORDER BY ID DESC
            LIMIT 1
            ",
				$like
			)
		);

		return $attachment_id ? (int) $attachment_id : false;
	}

	/**
	 * Upload the product image from Exact Online.
	 *
	 * @param string $picture_url URL of the image to upload.
	 * @param string $picture_name Name of the image file (used for naming the attachment).
	 * @return int|false Attachment ID of the uploaded image if successful, otherwise false
	 */
	private static function upload_image( $picture_url, $picture_name ) {
		require_once ABSPATH . 'wp-admin/includes/media.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/image.php';

		// Fetch image content.
		$response = wp_safe_remote_get( $picture_url, array( 'timeout' => 10 ) );

		if ( is_wp_error( $response ) ) {
			// error_log( 'Error fetching image: ' . $response->get_error_message() );
			return false;
		}

		$image_data = wp_remote_retrieve_body( $response );
		if ( empty( $image_data ) ) {
			return false;
		}

		// Generate filename.
		$upload_dir = wp_upload_dir();
		$filename   = sanitize_file_name( $picture_name );
		$file_path  = $upload_dir['path'] . '/' . $filename;

		// Save the file.
		file_put_contents( $file_path, $image_data );

		// Check if file was saved correctly.
		if ( ! file_exists( $file_path ) ) {
			return false;
		}

		// Prepare file array for WordPress.
		$file = array(
			'name'     => $filename,
			'type'     => mime_content_type( $file_path ),
			'tmp_name' => $file_path,
			'size'     => filesize( $file_path ),
		);

		// Upload to WordPress Media Library.
		$attachment_id = media_handle_sideload( $file, 0 );

		// Check for errors.
		if ( is_wp_error( $attachment_id ) ) {
			// error_log( 'Error attaching image: ' . $attachment_id->get_error_message() );
			return false;
		}

		return $attachment_id;
	}

	/**
	 * Sync orders via cron job.
	 * This function will get all orders from the last 30 days that have no eo_order_id or eo_invoice_id as meta key
	 * and send them to the send_orders function in CommerceBird class.
	 */
	public static function sync_orders_via_cron() {
		$logger  = wc_get_logger();
		$context = array( 'source' => 'cmbird_exact_online_orders' );
		// get all orders from the last 30 days that have no eo_order_id or eo_invoice_id as meta key.
		$start_date = gmdate( 'Y-m-d H:i:s', strtotime( '-15 days' ) );
		$end_date   = time();
		$orders     = wc_get_orders(
			array(
				'status'       => array_diff( array_keys( wc_get_order_statuses() ), array( 'wc-failed', 'wc-checkout-draft', 'wc-pending', 'wc-on-hold', 'wc-cancelled' ) ),
				'limit'        => -1,
				'date_created' => $start_date . '...' . $end_date,
				'return'       => 'ids',
				'meta_query'   => array(
					array(
						'key'     => 'eo_order_id',
						'compare' => 'NOT EXISTS',
					),
				),
			)
		);
		if ( empty( $orders ) ) {
			return;
		}
		// send the orders to the send_orders function in CommerceBird class.
		$response = ( new Connector() )->send_orders( array( 'orderIds' => $orders ) );
		if ( is_string( $response ) || 200 !== $response['code'] ) {
			// log the error.
			$logger->error(
				'Error syncing Exact Online orders',
				$context + array(
					'response' => $response,
					'orders'   => $orders,
				)
			);
			return;
		}
	}
}
