<?php
/**
 * Handles API requests for Stock Sync With Google Sheet For WooCommerce.
 *
 * @package StockSyncWithGoogleSheetForWooCommerce
 * @since 1.0.0
 */

// Namespace.
namespace StockSyncWithGoogleSheetForWooCommerce;

use Error;

// Exit if accessed directly.
defined('ABSPATH') || exit;

if ( ! class_exists('\StockSyncWithGoogleSheetForWooCommerce\API') ) {

	/**
	 * Handles API requests for Stock Sync With Google Sheet For WooCommerce.
	 *
	 * @package StockSyncWithGoogleSheetForWooCommerce
	 * @since 1.0.0
	 */
	class API extends Base {

		/**
		 * Contains an instance of this class, if available.
		 *
		 * @var API
		 */
		public static $instance = null;

		/**
		 * API init
		 */
		public static function init() {
			if ( ! self::$instance ) {
				self::$instance = new self();
			}

			self::$instance->register_routes();
		}

		/**
		 * Register API  routes
		 */
		public function register_routes() {
			add_action('rest_api_init', [ $this, 'add_api_routes' ]);
		}


		/**
		 * Add API routes
		 */
		public function add_api_routes() {

		$routes = [
			'action' => [
				'methods'             => [ 'GET', 'POST', 'OPTIONS' ],
				'callback'            => [ $this, 'action_api_callback' ],
				'permission_callback' => [ $this, 'permission_callback' ],
			],

			'update' => [
				'methods'             => [ 'GET', 'POST', 'OPTIONS' ],
				'callback'            => [ $this, 'update_api_callback' ],
				'permission_callback' => [ $this, 'permission_callback' ],
			],
			'sale_update' => [
				'methods'             => [ 'GET', 'POST', 'OPTIONS' ],
				'callback'            => [ $this, 'sale_update_api_callback' ],
				'permission_callback' => [ $this, 'permission_callback' ],
			]
		];

			foreach ( $routes as $route => $args ) {
				register_rest_route('ssgsw/v1', $route, $args);

			}
		}


	/**
	 * Check permission for API.
	 *
	 * @return bool
	 */
	public function permission_callback( $request = null ) {
		// Always allow OPTIONS requests for CORS preflight
		if ( $request && $request->get_method() === 'OPTIONS' ) {
			return true;
		}
		
		if ( defined('SSGSW_DEBUG') && SSGSW_DEBUG ) {
			return true;
		}

		$bearer2 = isset($_SERVER['HTTP_AUTHORIZATION']) ? sanitize_text_field( wp_unslash($_SERVER['HTTP_AUTHORIZATION'] ) ) : '';
		
		$bearer3 = isset($_SERVER['HTTP_SSGSWKEY']) ? sanitize_text_field( wp_unslash($_SERVER['HTTP_SSGSWKEY'] ) ) : '';
	
		$bearer = '';
		if ( empty($bearer3) ) {
			$bearer = $bearer2;
		}
		if ( empty($bearer2) ) {
			$bearer = $bearer3;
		}
		if ( empty($bearer) ) {
			return false;
		}

		$bearer      = str_replace('Bearer ', '', $bearer);
		$saved_token = get_option('ssgsw_token');

		if ( empty($saved_token) ) {
			return false;
		}

		if ( $bearer !== $saved_token ) {
			return false;
		}

		return true;
	}

	/**
	 * Responses for API
	 *
	 * @param bool  $success Success status.
	 * @param mixed $data Data to return.
	 * @return \WP_REST_Response
	 */
	public function response( $success = true, $data = null ) {
		$response = [
			'success' => $success,
		];

		if ( $data ) {
			if ( is_object($success) || is_array($success) ) {
				$response['data'] = $data;
			} else {
				$response['message'] = $data;
			}
		}

		$rest_response = new \WP_REST_Response($response);
		
		/**
		 * Ensure CORS headers are present for multi-store requests.
		 *
		 * NOTE:
		 * - WordPress REST API does not automatically add CORS headers.
		 * - AppsScript from Google Sheets needs Origin header in response
		 *   for cross-origin requests to work consistently.
		 * - This ensures Origin header is always present even on subsequent requests.
		 */
		$origin = isset($_SERVER['HTTP_ORIGIN']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_ORIGIN'])) : '*';
		$rest_response->header('Access-Control-Allow-Origin', $origin);
		$rest_response->header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
		$rest_response->header('Access-Control-Allow-Credentials', 'true');
		$rest_response->header('Access-Control-Allow-Headers', 'Content-Type, Authorization, SSGSWKEY, Origin');
		
		return $rest_response;
	}

		/**
		 * Action API callback. Which will call the action method.
		 *
		 * @param \WP_REST_Request $request Request.
		 * @return mixed Response.
		 */
	public function action_api_callback( $request ) {
		// Handle OPTIONS preflight requests for CORS
		if ( $request->get_method() === 'OPTIONS' ) {
			return $this->response(true, 'OK');
		}
		
		$params = $request->get_params();
		$action = $params['action'] ?? null;

			if ( ! $action ) {
				return $this->response( false, __('No action specified', 'stock-sync-with-google-sheet-for-woocommerce') );
			}

			$action = strtolower($action);

			if ( ! method_exists($this, 'action_' . $action) ) {
				return $this->response(
					false,
					__('Action not found', 'stock-sync-with-google-sheet-for-woocommerce')
				);
			}

			try {
				return $this->{'action_' . $action}($request);
			} catch ( \Exception $e ) {
				return $this->response(false, $e->getMessage());
			}
		}

		/**
		 * Callback for action sync.
		 *
		 * @param \WP_REST_Request $request Request.
		 * @return mixed Response.
		 */
		public function action_sync( $request ) {
			ssgsw_start_sync();
			$params  = $request->get_params();
			$message = $params['message'] ?? __('Products synced successfully', 'stock-sync-with-google-sheet-for-woocommerce');
			$ssgsw_items = isset( $params['ssgsw_items'] ) ? $params['ssgsw_items'] : false;
			$ssgsw_offset = isset( $params['ssgsw_offset'] ) ? $params['ssgsw_offset'] : 0;
			$ssgsw_batch_size = isset( $params['ssgsw_batch_size'] ) ? $params['ssgsw_batch_size'] : 0;
			$ssgsw_index = isset( $params['ssgsw_index'] ) ? $params['ssgsw_index'] : 1;
			$token_id = isset( $params['token_id'] ) ? $params['token_id'] : 0;
			$vendor_request = isset( $params['vendor_request'] ) ? $params['vendor_request'] : 0;
			$multi_store = isset( $params['multi_store'] ) ? $params['multi_store'] : false;
			$siteUrl = isset( $params['siteUrl'] ) ? $params['siteUrl'] : false;
			$siteToken = isset( $params['siteToken'] ) ? $params['siteToken'] : false;
		
			try {
				if ($multi_store){
					if ($ssgsw_items) {
						update_option('ssgsw_license_sync', false);
						$get_all_site = ssgsw_get_all_site_count_product($vendor_request);
						if ($get_all_site) {
							return $this->response( true, $get_all_site);
						} else {
							return $this->response(
								false,
								__('Something went wrong', 'stock-sync-with-google-sheet-for-woocommerce')
							);
						}
					} else {
						$product = new Product();
						$home_url = home_url();
						if ($home_url == $siteUrl) {
							$update = $product->sync_batch_all($ssgsw_offset, $ssgsw_batch_size, $ssgsw_index);
						} else {
							if (function_exists('ssgsw_batch_api_request')) {
								$sheets_info = ssgsw_get_sheet_data_from_transient();
			
								$columns = new Column();
								$columns_info = $columns->get_columns_no_edit_remove();
								$sheet_header = isset($sheets_info[0]) ? $sheets_info[0] : [];
								$key_with_headers = ssgsw_format_headers_with_sheet_header($sheet_header, $columns_info);
								$keys = isset($key_with_headers[0]) ? $key_with_headers[0] : [];
								
								$domain_url = apply_filters('ssgsw_domain_url', false);
					
								$get_batch = ssgsw_batch_api_request(
									$ssgsw_offset,
									$ssgsw_batch_size,
									$ssgsw_index,
									$siteUrl,
									$siteToken,
									$keys,
									$domain_url,
									[],
									$sheets_info
								);
							
								$update = $product->sync_batch_all_api($ssgsw_offset, $ssgsw_batch_size, $ssgsw_index, $get_batch);
							}
						}
						if ( is_array($update) && $update['success'] ) {
							ssgsw_end_sync();
							$vendor_product_sync = ssgsw_get_product_sync_user_id($token_id);
							if ($vendor_product_sync && $vendor_request) {
								ssgsw_update_product_sync_user_id();
							}
							return $this->response(true, $update['count'] );
						} else {
							ssgsw_end_sync();
							return $this->response(false, __('Something went wrong', 'stock-sync-with-google-sheet-for-woocommerce'));
						}
					}
				}
				if ( isset($params['ssgsw_items']) ) {
					if ( $ssgsw_items ) {
						update_option('ssgsw_license_sync', false);
						$response = ssgsw_count_all_product();
						if ( $response ) {
							ssgsw_end_sync();
							return $this->response(
								true,
								$response
							);
						} else {
							ssgsw_end_sync();
							return $this->response(
								false,
								__('Something went wrong', 'stock-sync-with-google-sheet-for-woocommerce')
							);
						}
					} else {
						$product = new Product();
						$update = $product->sync_batch_all($ssgsw_offset, $ssgsw_batch_size, $ssgsw_index);
						
						if ( is_array($update) && $update['success'] ) {
							ssgsw_end_sync();
							$vendor_product_sync = ssgsw_get_product_sync_user_id($token_id);
							if ($vendor_product_sync && $vendor_request) {
								ssgsw_update_product_sync_user_id();
							}
							
							return $this->response(true, $update['count'] );
						} else {
							ssgsw_end_sync();
							return $this->response(false, __('Something went wrong', 'stock-sync-with-google-sheet-for-woocommerce'));
						}
					}
				} else {
					$product = new Product();
					$response = $product->sync_all();
					if ( $response ) {
						ssgsw_end_sync();
						return $this->response(
							true,
							$message
						);
					} else {
						ssgsw_end_sync();
						return $this->response(
							false,
							__('Something went wrong', 'stock-sync-with-google-sheet-for-woocommerce')
						);
					}
				}
			} catch ( \Exception $e ) {
				ssgsw_end_sync();
				return $this->response(false, $e->getMessage());
			}
		}
		/**
		 * Handles the API callback for updating sales data for a specific product.
		 *
		 * This endpoint is used to trigger a product data synchronization or update 
		 * process (likely related to sales/inventory) based on the provided product ID.
		 * It also handles CORS preflight requests (OPTIONS method).
		 *
		 * @param \WP_REST_Request $request The full request object.
		 * * @return \WP_REST_Response A response object indicating success or failure.
		 * Success response includes 'update successfully!'.
		 * Failure response includes a descriptive message 
		 * or the exception message.
		 */
		public function sale_update_api_callback($request) {
			// Handle OPTIONS preflight requests for CORS
			if ( $request->get_method() === 'OPTIONS' ) {
				return $this->response(true, 'OK');
			}
			global $ssgsw_request_params;
			$body     = $request->get_params();
    		$ssgsw_request_params = $body;
			$product = new Product();
			$product_id = $body['product_id'] ?? null;
			$function_id = $body['function_id'] ?? false;
			$sync_formula = $body['sync_formula'] ?? false;
			try {
				
				if ($sync_formula) {
					$this->formating_formula_event();	
					return $this->response( true, 'update successfully!' );
				} else {
					if($product_id) {
						if ($function_id) {
							$product->batch_update_delete_and_append2($product_id);
						} else {
							$product->batch_update_delete_and_append($product_id);
						}
						return $this->response( true, 'update successfully!' );
					}
				}
				
				return $this->response( false, 'update failed!' );
				
			} catch ( \Exception $e ) {
				return $this->response(false, $e->getMessage());
			}
		}
		/**
		 * Processes sheet data, extracts URLs from Google Sheet IMAGE formulas, 
		 * structures the data, and schedules batch processing using WordPress Cron.
		 *
		 * This function performs the following steps:
		 * 1. Initializes a Sheet object to fetch both calculated values and raw formulas.
		 * 2. Handles error/empty checks for fetched data.
		 * 3. Extracts header rows ('keys') from both datasets.
		 * 4. Iterates over rows using array_map to combine unformatted values and formulas.
		 * 5. Specifically checks for columns named 'Image' and replaces the '=IMAGE("URL")' formula 
		 * with the extracted URL using regular expressions (preg_match).
		 * 6. Structures the processed data into an associative array (master_data).
		 * 7. Chunks the master data into groups of 2500 items.
		 * 8. Schedules a single WordPress Cron event ('ssgsw_process_data_chunk_formula')
		 * for each chunk with an increasing delay (5s, 20s, 35s, etc.) for sequential processing.
		 *
		 * @return bool|void Returns false if data fetching or processing results in empty arrays, otherwise void.
		 */
		public function formating_formula_event() {
			$new_sheet = new Sheet();
			$formulas = $new_sheet->get_formula_value();
			if ( empty($formulas) ) {
				$formulas = $new_sheet->get_formula_value();
			}
            
            $unformatted_values = $new_sheet->get_sheet_values();
			if ( empty($unformatted_values) ) {
				$unformatted_values = $new_sheet->get_sheet_values();
			}
            

            $image_names = ['Image'];
            $keys = [];
            if (isset($formulas[0])) {
                $keys = array_shift($unformatted_values);
                $key2 = array_shift($formulas);
            }
            if (empty($keys)) {
                return false;
            }
            if(is_array($unformatted_values) && empty($unformatted_values)) {
                return false;
            }
            if(is_array($formulas) && empty($formulas)) {
                return false;
            }
            // Use array_map() to iterate over both arrays at the same time.
            $master_data = array_map(function( $unformatted_row, $formula_row) use ($keys, $image_names) {
                // Loop through each image column index found.
                $formatted_row = [];
                foreach ($keys as $new_key => $value) {
                    $key = ssgsw_format_sheet_key_function($value);
                    if (in_array($value, $image_names)) {
                        if (isset($formula_row[$new_key]) && strpos($formula_row[$new_key], '=IMAGE(') === 0) {
                            $image_formula = $formula_row[$new_key];
                            $image_url = '';
                            preg_match('/=IMAGE\("(.*?)"\)/', $image_formula, $matches);
                            $image_url = isset($matches[1]) ? $matches[1] : '';
                            // Replace the value in the unformatted data with the extracted URL.
                            $formatted_row[$key] = $image_url;
                        } else {
                            $formatted_row[$key] = '';
                        }
                    } else {
						if (isset($formula_row[$new_key]) && strpos($formula_row[$new_key], '=') === 0) {
                    		$formatted_row[$key] = isset($unformatted_row[$new_key]) ? $unformatted_row[$new_key] : '';
						} else {
							if($key === 'ID') {
								$formatted_row[$key] = isset($unformatted_row[$new_key]) ? $unformatted_row[$new_key] : '';
							}
						}
                    }
                }
                return $formatted_row;
            }, $unformatted_values, $formulas);

		
            if(!is_array($master_data)) {
                return false;
            }
            if(empty($master_data)) {
                return false;
            }
            $chunk_size = 2500;
            $total_data_count = count($master_data);
            // print_r($total_data_count);
            $total_chunks = ceil($total_data_count / $chunk_size);
            $delay_time = 5;
            $base_time = time();
            for($i = 0; $i < $total_chunks; $i++) {
                 $chunk = array_slice($master_data, $i * $chunk_size, $chunk_size);
                 
                 // Store chunk in transient to avoid hitting cron argument size limits
                 $transient_key = 'ssgsw_chunk_' . $base_time . '_' . $i . '_' . uniqid();
                 set_transient($transient_key, $chunk, HOUR_IN_SECONDS);

                 wp_schedule_single_event(
                    $base_time + $delay_time,
                    'ssgsw_process_data_chunk_formula',
                    [
                        [
                            'transient_key' => $transient_key
                        ]
                    ]
                );
                $delay_time += 15;
            }
		}
			
		/**
		 * Callback for action update.
		 *
		 * @param \WP_REST_Request $request Request.
		 * @return mixed Response.
		 */
		public function update_api_callback( $request ) {
			// Handle OPTIONS preflight requests for CORS
			if ( $request->get_method() === 'OPTIONS' ) {
				return $this->response(true, 'OK');
			}
			
			ssgsw_start_sync();
			
			$body     = $request->get_params();

			
			$products = $body['products'] ?? null;

			$message = $body['message'] ?? __('Product Create successfully', 'stock-sync-with-google-sheet-for-woocommerce');
			$sync_all_active = isset( $body['sync_all'] ) && 'sync_all' === $body['sync_all'] ? true : false;
			$product_length = isset( $body['arrayLength'] ) ? $body['arrayLength'] : 1;
			$formula = isset( $body['formula'] ) ? $body['formula'] : false;
			$first_index = isset( $body['firstIndex'] ) ? $body['firstIndex'] : 0;
			$last_index = isset( $body['lastIndex'] ) ? $body['lastIndex'] : 0;
			$token_id = isset( $body['token_id'] ) ? $body['token_id'] : 0;
			$vendor_request = isset( $body['vendor_request'] ) ? $body['vendor_request'] : 0;
			$user_token = get_user_by( 'id', $token_id );
		
			if ($user_token) {
				$vendor_product_sync = ssgsw_get_product_sync_user_id($token_id);
				if ($vendor_product_sync && $vendor_request) {
					return $this->response( false, "New Update: Product data needs refreshing. Use 'Sync on Google Sheet' in settings, or 'Fetch from WooCommerce' from your sheet menu." );
				}
			}

			if ( ! $vendor_request ) {
				$sync_mode = ssgsw_sync_validation_mode( 'sheets-to-woocommerce' );
				if ( ! $sync_mode ) {
					$current_mode = ssgsw_get_option( 'selected_sync_mode', 'live-sync' );
					if ( 'live-sync' === $current_mode ) {
						return $this->response(
							false,
							"Sync is set to Real-Time (WooCommerce → Sheets ). Change it anytime from Settings > Sync Settings"
						);
					} else {
						$sync_direction = ssgsw_get_option('peroidic_sync_direction', 'woocommerce-to-sheets');
						if ('woocommerce-to-sheets' === $sync_direction) {
							return $this->response(
								false,
								"Sync is set to Scheduled (WooCommerce → Sheets ). Change it anytime from Settings > Sync Settings.",
							);
						} else {
							return $this->response(
								false,
								"Sync is set to Scheduled (Sheets → WooCommerce ). Change it anytime from Settings > Sync Settings.",
							);
						}
					}
				}
			}


			if ( $products && ( is_array($products) || is_object($products) ) && ! empty($products) ) {

				try {
					$product  = new Product();

					if ( isset($body['newapp']) ) {
						$response = $product->chunk_product_update($products, $first_index, $last_index);
					} else {
						$response = $product->bulk_update($products, $sync_all_active, $product_length);
					}
					ssgsw_end_sync();
					if(is_array($response) && !empty($response)) {
						$total_processed = count($products);
						$error_messages = [];
						$sale_error = isset($response['sale_error']) ? (int) $response['sale_error'] : 0;
						$status_error = isset($response['status_error']) ? (int) $response['status_error'] : 0;
						$attribute_error = isset($response['attribute_error']) ? (int) $response['attribute_error'] : 0; // NEW: Capture Attribute Error

						$total_errors = $sale_error + $status_error + $attribute_error; // UPDATED: Include attribute_error
						$successful_count = max(0, $total_processed - $total_errors); 

						// --- Short Message Construction ---

						// 1. Success Count
						if ( $successful_count > 0 ) {
							$error_messages[] = sprintf(
								/* translators: %s: number of products */
								__('✅ Updated: %s', 'stock-sync-with-google-sheet-for-woocommerce'),
								number_format_i18n($successful_count)
							);
						}

						// 2. Sale Price Error Count (Updated to be more specific)
						if ( $sale_error > 0 ) {
							$error_messages[] = sprintf(
								/* translators: %s: number of products */
								__('🛑 Invalid Sale Price: %s', 'stock-sync-with-google-sheet-for-woocommerce'),
								number_format_i18n($sale_error)
							);
						}

						// 3. Status Error Count (Updated to be more specific)
						if ( $status_error > 0 ) {
							$error_messages[] = sprintf(
								/* translators: %s: number of products */
								__('⚠️ Variation Status Locked: %s', 'stock-sync-with-google-sheet-for-woocommerce'),
								number_format_i18n($status_error)
							);
						}

						// 4. Attribute Error Count (NEW)
						if ( $attribute_error > 0 ) {
							$error_messages[] = sprintf(
								/* translators: %s: number of products */
								__('⚠️ Attribute Error: %s', 'stock-sync-with-google-sheet-for-woocommerce'),
								number_format_i18n($attribute_error)
							);
						}

						// 5. Total Count
						$error_messages[] = sprintf(
							/* translators: %s: number of products */
							__('📊 Total: %s', 'stock-sync-with-google-sheet-for-woocommerce'),
							number_format_i18n($total_processed)
						);

						// Shob message ekshathe pipe (' | ') diye jog kora holo.
						$final_message = implode( ' | ', $error_messages );

						// Final message return kora holo
						return $this->response( true, $final_message );
					} else {
						if ( $response ) {
							return $this->response( true, $message );
						} else {
							$new_message = __("You couldn't create a new product because the Add new products from Google Sheet feature is not enabled in your settings",'stock-sync-with-google-sheet-for-woocommerce');//phpcs:ignore
							return $this->response( true, $new_message );
						}
					}
					
				} catch ( \Exception $e ) {
					$product = new Product();
					$response = $product->sync_all();
					ssgsw_end_sync();
					return $this->response(false, $e->getMessage());
				}
			}
			ssgsw_end_sync();
			return $this->response( false, __('No products specified', 'stock-sync-with-google-sheet-for-woocommerce') );
		}
	}
	/**
	 * API init
	 */
	API::init();
}
