<?php

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

/**
 * Class CMBIRD_Pricelist_ZI
 *
 * Handles importing Zoho price lists into WooCommerce and B2B plugins.
 */
class CMBIRD_Pricelist_ZI {
	use CommerceBird\Admin\Traits\LogWriter;

	/**
	 * Configuration array containing Zoho Inventory API details.
	 *
	 * @var array
	 */
	private array $config;

	/**
	 * Class constructor.
	 *
	 * Retrieves Zoho Inventory connection configuration options for OID and API URL.
	 */
	public function __construct() {
		$this->config = array(
			'ProductZI' => array(
				'OID'    => get_option( 'cmbird_zoho_inventory_oid' ),
				'APIURL' => get_option( 'cmbird_zoho_inventory_url' ),
			),
		);
	}

	/**
	 * Get all Zoho price list
	 * Example response:
	 * {
	 *      "currency_code": Code based on currency,
	 *      "currency_id": The currenct id of the currency,
	 *      "decimal_place": Decimal place for pricebook.,
	 *      "description": Description about the pricebook,
	 *      "is_default": To check the default pricebook.Allowed values: true,false,
	 *      "is_increase": Mark up or Mark down to discounts.Allowed values: true,false,
	 *      "name": Name of the pricebook,
	 *      "percentage": About percentage of discounts,
	 *      "pricebook_id": Unique ID generated by server for the price book,
	 *      "pricebook_items": [
	 *          {
	 *              "item_id": Unique ID generated by server for Item,
	 *              "pricebook_rate": Rate of the price book for the Items,
	 *              "pricebook_item_id": Unique ID generated by the server for each pricebook line item
	 *          }
	 *      ],
	 *      "pricebook_type": Type of the pricebook.Allowed values: per_item,fixed_percentage,
	 *      "rounding_type": Type of the rounding.Allowed values: no_rounding,round_to_dollor,round_to_dollar_minus_01,round_to_half_dollar,round_to_half_dollar_minus_01,
	 *      "sales_or_purchase_type": Whether its sales or purchase type.Allowed values: sales,purchases,
	 *      "status": Status of the price book
	 * }
	 *
	 * @return array of price list
	 */
	public function zi_get_all_pricelist(): array {
		$in_cache = get_transient( 'zoho_pricelist' );
		if ( $in_cache ) {
			return $in_cache;
		}

		$url                      = $this->config['ProductZI']['APIURL'] . 'inventory/v1/pricebooks?organization_id=' . $this->config['ProductZI']['OID'];
		$execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
		$json                     = $execute_curl_call_handle->execute_curl_call_get( $url );

		if ( isset( $json->pricebooks ) ) {
			set_transient( 'zoho_pricelist', $json->pricebooks, MINUTE_IN_SECONDS );

			return $json->pricebooks;
		}

		return array();
	}

	/**
	 * Save pricelist function to update prices and discounts for user roles.
	 *
	 * @param array $post The post-data containing user role and price information.
	 * Example:
	 * {
	 *      "zoho_inventory_pricelist": Zoho inventory pricelist id
	 *      "wp_user_role": WordPress user role
	 * }
	 */
	public function save_price_list( array $post ): bool {
		if ( class_exists( 'Addify_B2B_Plugin' ) ) {
			$success = $this->addify_b2b( $post['zoho_inventory_pricelist'], $post['wp_user_role'] );
		} elseif ( CMBIRD_Common_Functions::is_wcb2b_active() ) {
			$success = $this->wc_b2b( $post['wcb2b'] );
		}
		return $success;
	}

	/**
	 * Fetch one page of pricebook data from the Zoho API without caching.
	 *
	 * @param int $pricebook_id Pricebook ID.
	 * @param int $page         1-based page number.
	 *
	 * @return array{pricebook: array, has_more_page: bool}
	 */
	private function fetch_pricebook_page( int $pricebook_id, int $page ): array {
		$url  = $this->config['ProductZI']['APIURL']
			. 'inventory/v1/pricebooks/' . $pricebook_id
			. '?organization_id=' . $this->config['ProductZI']['OID']
			. '&page=' . $page;
		$api  = new CMBIRD_API_Handler_Zoho();
		$json = $api->execute_curl_call_get( $url );

		if ( ! is_object( $json ) || ! property_exists( $json, 'pricebook' ) ) {
			return array(
				'pricebook'     => array(),
				'has_more_page' => false,
			);
		}

		$pricebook = json_decode( wp_json_encode( $json->pricebook ), true );
		$has_more  = ! empty( $json->page_context->has_more_page );

		return array(
			'pricebook'     => $pricebook,
			'has_more_page' => $has_more,
		);
	}

	/**
	 * Get page 1 of a Zoho pricebook (cached for 24 h).
	 *
	 * The returned array includes a '_has_more_page' boolean so callers can
	 * decide whether to queue subsequent pages via Action Scheduler.
	 *
	 * @param int $pricebook_id pricebook id.
	 *
	 * @return array Pricebook data or empty array on failure.
	 */
	private function get_zoho_pricebook( int $pricebook_id ): array {
		$in_cache = get_transient( 'zoho_pricelist_' . $pricebook_id );
		if ( $in_cache ) {
			return $in_cache;
		}

		$result    = $this->fetch_pricebook_page( $pricebook_id, 1 );
		$pricebook = $result['pricebook'];
		if ( empty( $pricebook ) ) {
			return array();
		}

		$pricebook['_has_more_page'] = $result['has_more_page'];
		set_transient( 'zoho_pricelist_' . $pricebook_id, $pricebook, DAY_IN_SECONDS );
		update_option( 'cmbird_zoho_pricelist_id', $pricebook_id );

		return $pricebook;
	}

	/**
	 * Get published product ids, based on zi_item_id meta-key
	 *
	 * @return array
	 */
	private function get_zoho_products(): array {
		$in_cache = get_transient( 'zoho_products' );
		if ( $in_cache ) {
			return $in_cache;
		}
		global $wpdb;

		$ids = $wpdb->get_results(
			"SELECT p.ID AS id, pm.meta_value AS zi_item_id
           				 FROM {$wpdb->postmeta} pm
           				 INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID
           				 WHERE pm.meta_key = 'zi_item_id'
           				 AND p.post_type in ('product', 'product_variation')
           				 AND p.post_status = 'publish'
           				 AND pm.meta_value != ''",
			ARRAY_A
		);

		$plist = wp_list_pluck( $ids, 'id', 'zi_item_id' );
		ksort( $plist );
		set_transient( 'zoho_products', $plist, DAY_IN_SECONDS );

		return $plist;
	}

	/**
	 * Insert or update a single role-based price entry on a product.
	 *
	 * @param int    $product_id WP post ID.
	 * @param string $role       User role slug.
	 * @param array  $metavalue  Discount configuration to store.
	 */
	private function upsert_role_base_price( int $product_id, string $role, array $metavalue ): void {
		$postmeta_array = get_post_meta( $product_id, '_role_base_price', true );
		if ( ! is_array( $postmeta_array ) ) {
			$postmeta_array = array();
		}
		$updated = false;

		if ( ! empty( $postmeta_array ) ) {
			foreach ( $postmeta_array as &$postmeta ) {
				if ( isset( $postmeta['user_role'] ) && $postmeta['user_role'] === $role ) {
					$postmeta = $metavalue;
					$updated  = true;
					break;
				}
			}
			unset( $postmeta );
			if ( ! $updated ) {
				$postmeta_array[] = $metavalue;
			}
		} else {
			$postmeta_array[] = $metavalue;
		}
		update_post_meta( $product_id, '_role_base_price', $postmeta_array );
	}

	/**
	 * Apply one page of per-item pricebook entries to Addify B2B product meta.
	 *
	 * @param array  $pricebook_items Items from one API page.
	 * @param string $role            WordPress user role.
	 * @param array  $zi_item_ids     Map of Zoho item ID → WP product ID.
	 */
	private function apply_addify_items_page( array $pricebook_items, string $role, array $zi_item_ids ): void {
		foreach ( $pricebook_items as $itemlist ) {
			if ( ! isset( $zi_item_ids[ $itemlist['item_id'] ] ) ) {
				continue;
			}
			$product_id = (int) $zi_item_ids[ $itemlist['item_id'] ];

			if ( is_array( $itemlist['price_brackets'] ?? null ) && ! empty( $itemlist['price_brackets'] ) ) {
				$bracket         = $itemlist['price_brackets'][0];
				$formatted_price = array(
					'start_quantity' => str_replace( '.', wc_get_price_decimal_separator(), $bracket['start_quantity'] ?? 0 ),
					'end_quantity'   => str_replace( '.', wc_get_price_decimal_separator(), $bracket['end_quantity'] ?? 0 ),
					'pricebook_rate' => str_replace( '.', wc_get_price_decimal_separator(), $bracket['pricebook_rate'] ?? 0 ),
				);
			} else {
				$formatted_price = str_replace( '.', wc_get_price_decimal_separator(), $itemlist['pricebook_rate'] ?? 0 );
			}

			$metavalue = array(
				'discount_type'  => 'fixed_price',
				'user_role'      => $role,
				'discount_value' => is_array( $formatted_price ) ? $formatted_price['pricebook_rate'] : $formatted_price,
				'min_qty'        => is_array( $formatted_price ) ? $formatted_price['start_quantity'] : '',
				'max_qty'        => is_array( $formatted_price ) ? $formatted_price['end_quantity'] : '',
			);
			$this->upsert_role_base_price( $product_id, $role, $metavalue );
		}
	}

	/**
	 * Support For Addify B2B
	 *
	 * Processes page 1 synchronously. For per-item pricebooks with more pages,
	 * subsequent pages are queued via Action Scheduler (cmbird_pricelist_addify_page).
	 *
	 * @param int    $pricebook_id pricebook id.
	 * @param string $role user role.
	 */
	private function addify_b2b( int $pricebook_id, string $role ): bool {
		$price_book = $this->get_zoho_pricebook( $pricebook_id );
		if ( empty( $price_book ) ) {
			return false;
		}

		$zi_item_ids    = $this->get_zoho_products();
		$pricebook_type = $price_book['pricebook_type'] ?? '';

		if ( 'fixed_percentage' === $pricebook_type ) {
			$percentage = isset( $price_book['percentage'] ) ? $price_book['percentage'] : 0;
			$orderby    = ! empty( $price_book['is_increase'] ) ? 'percentage_increase' : 'percentage_decrease';
			foreach ( array_values( $zi_item_ids ) as $product_id ) {
				$metavalue = array(
					'discount_type'  => $orderby,
					'user_role'      => $role,
					'discount_value' => str_replace( '.', wc_get_price_decimal_separator(), $percentage ),
					'min_qty'        => '',
					'max_qty'        => '',
				);
				$this->upsert_role_base_price( (int) $product_id, $role, $metavalue );
			}
			return true;
		}

		// per_item: process page 1 (already fetched by get_zoho_pricebook), queue the rest.
		$this->apply_addify_items_page( $price_book['pricebook_items'] ?? array(), $role, $zi_item_ids );
		if ( ! empty( $price_book['_has_more_page'] ) ) {
			as_schedule_single_action( time(), 'cmbird_pricelist_addify_page', array( $pricebook_id, $role, 2 ), 'commercebird' );
		}
		return true;
	}

	/**
	 * Action Scheduler handler: process one page of Addify B2B per-item entries.
	 *
	 * @param int    $pricebook_id Zoho pricebook ID.
	 * @param string $role         WordPress user role.
	 * @param int    $page         Page number to fetch and process.
	 */
	public function process_addify_page( int $pricebook_id, string $role, int $page ): void {
		$result = $this->fetch_pricebook_page( $pricebook_id, $page );
		if ( empty( $result['pricebook'] ) ) {
			return;
		}
		$zi_item_ids = $this->get_zoho_products();
		$this->apply_addify_items_page( $result['pricebook']['pricebook_items'] ?? array(), $role, $zi_item_ids );
		if ( $result['has_more_page'] ) {
			as_schedule_single_action( time(), 'cmbird_pricelist_addify_page', array( $pricebook_id, $role, $page + 1 ), 'commercebird' );
		}
	}

	/**
	 * Apply one page of per-item pricebook entries to WC B2B group product meta.
	 *
	 * @param array $pricebook_items Items from one API page.
	 * @param int   $group_id        WooCommerce B2B group post ID.
	 * @param array $zi_item_ids     Map of Zoho item ID → WP product ID.
	 */
	private function apply_wc_b2b_items_page( array $pricebook_items, int $group_id, array $zi_item_ids ): void {
		foreach ( $pricebook_items as $pricebook_item ) {
			if ( ! isset( $zi_item_ids[ $pricebook_item['item_id'] ] ) ) {
				continue;
			}
			$product_id     = $zi_item_ids[ $pricebook_item['item_id'] ];
			$start_quantity = null;
			$end_quantity   = null;

			if ( is_array( $pricebook_item['price_brackets'] ?? null ) && count( $pricebook_item['price_brackets'] ) > 0 ) {
				$price          = $pricebook_item['price_brackets'][0]['pricebook_rate'];
				$start_quantity = $pricebook_item['price_brackets'][0]['start_quantity'];
				$end_quantity   = $pricebook_item['price_brackets'][0]['end_quantity'];
			} else {
				$price = $pricebook_item['pricebook_rate'];
			}

			// Merge into existing per-group meta so saving group A does not erase group B's prices on the same product.
			$existing_prices      = get_post_meta( $product_id, 'wcb2b_product_group_prices', true );
			$existing_min         = get_post_meta( $product_id, 'wcb2b_product_group_min', true );
			$existing_max         = get_post_meta( $product_id, 'wcb2b_product_group_max', true );
			$product_group_prices = is_array( $existing_prices ) ? $existing_prices : array();
			$product_group_min    = is_array( $existing_min ) ? $existing_min : array();
			$product_group_max    = is_array( $existing_max ) ? $existing_max : array();

			if ( ! isset( $product_group_prices[ $group_id ] ) || ! is_array( $product_group_prices[ $group_id ] ) ) {
				$product_group_prices[ $group_id ] = array();
			}

			$product_group_prices[ $group_id ]['regular_price'] = $price;
			update_post_meta( $product_id, 'wcb2b_product_group_prices', $product_group_prices );

			if ( null !== $start_quantity ) {
				$product_group_min[ $group_id ] = $start_quantity;
				update_post_meta( $product_id, 'wcb2b_product_group_min', $product_group_min );
			}
			if ( null !== $end_quantity ) {
				$product_group_max[ $group_id ] = $end_quantity;
				update_post_meta( $product_id, 'wcb2b_product_group_max', $product_group_max );
			}
		}
	}

	/**
	 * Update price book for each group and its products.
	 *
	 * Processes page 1 synchronously. For per-item pricebooks with more pages,
	 * subsequent pages are queued via Action Scheduler (cmbird_pricelist_wc_b2b_page).
	 *
	 * @param string $group_settings Group settings in JSON format.
	 *
	 * @return bool True if successful, false otherwise.
	 */
	private function wc_b2b( $group_settings ): bool {
		$group_settings = json_decode( $group_settings, true );
		if ( empty( $group_settings ) ) {
			return false;
		}
		// Force-refresh the product map so newly imported items are visible to this save.
		delete_transient( 'zoho_products' );
		foreach ( $group_settings as $group_setting ) {
			$group_id     = (int) $group_setting['key'];
			$pricebook_id = (int) $group_setting['value'];
			// Bypass the 24h pricebook cache so changes made in Zoho take effect on re-import.
			delete_transient( 'zoho_pricelist_' . $pricebook_id );
			$price_book = $this->get_zoho_pricebook( $pricebook_id );
			if ( empty( $price_book ) ) {
				continue;
			}
			$zi_item_ids = $this->get_zoho_products();
			switch ( $price_book['pricebook_type'] ) {
				case 'fixed_percentage':
					$percentage = isset( $price_book['percentage'] ) ? $price_book['percentage'] : 0;
					update_post_meta( $group_id, 'wcb2b_group_discount', $percentage );
					update_post_meta( $group_id, 'pricebook_id', $pricebook_id );
					update_post_meta( $group_id, 'pricebook_name', $price_book['name'] );
					break;
				case 'per_item':
					// Tag the group itself before iterating items so it stays attached even if no products match yet.
					update_post_meta( $group_id, 'pricebook_id', $pricebook_id );
					update_post_meta( $group_id, 'pricebook_name', $price_book['name'] );
					// Process page 1 items (already fetched by get_zoho_pricebook), queue the rest.
					$this->apply_wc_b2b_items_page( $price_book['pricebook_items'] ?? array(), $group_id, $zi_item_ids );
					if ( ! empty( $price_book['_has_more_page'] ) ) {
						as_schedule_single_action( time(), 'cmbird_pricelist_wc_b2b_page', array( $pricebook_id, $group_id, 2 ), 'commercebird' );
					}
					break;
				default:
					break;
			}
		}

		return true;
	}

	/**
	 * Action Scheduler handler: process one page of per-item WC B2B pricebook entries.
	 *
	 * @param int $pricebook_id Zoho pricebook ID.
	 * @param int $group_id     WooCommerce B2B group post ID.
	 * @param int $page         Page number to fetch and process.
	 */
	public function process_wc_b2b_page( int $pricebook_id, int $group_id, int $page ): void {
		$result = $this->fetch_pricebook_page( $pricebook_id, $page );
		if ( empty( $result['pricebook'] ) ) {
			return;
		}
		$zi_item_ids = $this->get_zoho_products();
		$this->apply_wc_b2b_items_page( $result['pricebook']['pricebook_items'] ?? array(), $group_id, $zi_item_ids );
		if ( $result['has_more_page'] ) {
			as_schedule_single_action( time(), 'cmbird_pricelist_wc_b2b_page', array( $pricebook_id, $group_id, $page + 1 ), 'commercebird' );
		}
	}

	/**
	 * Retrieves and caches the WooCommerce B2B groups.
	 *
	 * @return array An associative array of group IDs as keys and their labels as values.
	 */
	public function wc_b2b_groups() {
		// Stored as a wp_option (not a transient) so external object caches cannot evict it
		// out from under the admin UI; invalidated explicitly on save_post of wcb2b_group.
		$in_cache = get_option( 'cmbird_wcb2b_groups' );
		if ( ! empty( $in_cache ) ) {
			return $in_cache;
		}
		global $wpdb;

		$results = $wpdb->get_results(
			"SELECT ID AS id, post_title AS label
			        FROM {$wpdb->posts} p
			        WHERE p.post_type = 'wcb2b_group'
			        AND p.post_status = 'publish'",
			ARRAY_A // Return results as an associative array.
		);

		if ( empty( $results ) ) {
			return array();
		}
		$groups = wp_list_pluck( $results, 'label', 'id' );
		update_option( 'cmbird_wcb2b_groups', $groups, true );

		return $groups;
	}
}
