<?php
/**
 * API class.
 *
 * @package    Diller_Loyalty
 * @subpackage Diller_Loyalty/includes
 * @author     Diller AS <dev@diller.no>
 */
final class Diller_Loyalty_Api {

	public function authenticate($api_key, $store_pin){
		$api = new Diller_Loyalty_API_Request( '/storeIDAuth', array( "x_api_key" => $api_key, "store_pin" =>  $store_pin ), 'POST');
		return $api->request();
	}

	public function get_store_details(){
		$api = new Diller_Loyalty_API_Request( '/getStoreDetails', array(), 'POST', 'PosV3');
		$response = $api->request();

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

		
		$defaults = array(
			"buttons" => array("color" => "#ffff", "color_hover" => "#000"),
			"membership_progress_bar" => array("color" => "#ffff")
		);

		$response["result"]["store_styles"] = wp_parse_args( array(
			"membership_progress_bar" => array(
				"background_color" => $response["result"]["point_bar_color"] ?? DILLER_LOYALTY_BRAND_COLOR
			),
			"buttons" => array(
				"background_color" => $response["result"]["wp_submit_button_color"] ?? DILLER_LOYALTY_BRAND_COLOR,
				"background_color_hover" => !empty($response["result"]["wp_submit_button_color_hover"])
					? $response["result"]["wp_submit_button_color_hover"]
					: Diller_Loyalty_Helpers::css_lighten_color($response["result"]["wp_submit_button_color"] ?? DILLER_LOYALTY_BRAND_COLOR, 0.2)	 // If no hover color provided, apply the default: lighten main color by 20%
			)
		), $defaults);

		
		$response["result"]["stamps_enabled"] = false;

		
		// Exclude unnecessary data from being stored in WP, to avoid confusion and misuse
		$exclusions = array(
			"external_id",
			"woocommerce_store_url",
			"language",
			"store_language",
			"wordpress_language",
			"partner_id",
			"organisation_no",
			"store_domain",
			"sms_sender_id",
			"store_pin",
			"x_api_key",
			"point_bar_color",
			"wp_submit_button_color",
			"wp_submit_button_color_hover",
			"point_bar_level_width",
			"paymentplan",
			"store_status",
			"paymentplan_cap_amount",
			"paymentsystem"
		);
		foreach ($exclusions as $field){
			if(isset($response["result"][$field])) {
				unset( $response["result"][$field] );
			}
		}

		if(isset($response["result"]["department_details"]["store_id"])) {
			unset( $response["result"]["department_details"]["store_id"] );
		}
		if(isset($response["result"]["department_details"]["mailchimp_tag"])) {
			unset( $response["result"]["department_details"]["mailchimp_tag"] );
		}
		if(isset($response["result"]["department_details"]["mailchimp_tag"])) {
			unset( $response["result"]["department_details"]["mailchimp_merge_id"] );
		}

		return $response["result"];
	}

	/**
	 * Update store details, with the new values passed in
	 *
	 * @param array $values
	 *
	 * @return mixed|WP_Error
	 */
	public function update_store_details($values = array()){
		$request_data = array_merge($values, array(
			// Add default values here
		));

		$api = new Diller_Loyalty_API_Request( '/updateStore', $request_data, 'POST', 'PosV2');
		$response = $api->request();

		return $response;
	}


	
	/**
	 * Retuns the full details for a giving follower, including membership details and segments
	 *
	 * @param Diller_Loyalty_Follower $follower
	 *
	 * @return Diller_Loyalty_Follower|mixed|WP_Error
	 */
	public function get_follower_details(Diller_Loyalty_Follower $follower) {

		//Note 2 self: Difference between PosV2 and PosV1 here is that Posv2 return an object with the "membershipDetails"
		$api    = new Diller_Loyalty_API_Request( "/getFollower", array( "country_code" => $follower->get_phone_country_code(), "phone" => $follower->get_phone_number()), 'POST', 'PosV2');
		$result = $api->request();

		if(is_wp_error($result)){
			return $result;
		}

		$follower_data = $result["result"];

		// Follower details
		$follower->set_first_name($follower_data["first_name"])
		         ->set_last_name($follower_data["last_name"])
		         ->set_email($follower_data["email"])
		         ->set_address($follower_data["address"] ?? '')
		         ->set_gender($follower_data["gender"] ?? 3)
		         ->set_postal_city($follower_data["city"] ?? '')
		         ->set_postal_code($follower_data["zip_code"] ?? '')
		         ->set_country($follower_data["country"] ?? '')
		         ->set_birth_date($follower_data["date_of_birth"] ?? '')
		         ->set_membership_consent_accepted(strtotime($follower_data["GDPR_date"]) > 0)
		         ->set_membership_consent_accepted_date($follower_data["GDPR_date"])
		         ->set_purchase_history_consent_accepted($follower_data["is_purchase_history"])
		         ->set_marketing_email_consent_accepted($follower_data["receive_email"])
		         ->set_marketing_sms_consent_accepted($follower_data["receive_sms"])
		         ->set_diller_id($follower_data["user_follower_id"])
		         ->set_remaining_points($follower_data['remaining_points'] ?? 0);

		// Country is returned in full name format, so we need to parse it to ISO Alpha-2 format
		$country = Diller_Loyalty_Helpers::parse_country($follower_data["country"]);
		$follower->set_country($country);

		// Store departments
		//Expecting: ["department_id": 477, "department_values": "Arendal" }, { "department_id": 479, "department_values": "Tromsø" }]
		$follower->set_department_ids(array_column($follower_data['DepartmentDetails'] ?? array(), 'department_id'));

		// Membership details
		$membership_details = $follower_data['membershipDetails'];
		if(is_array($membership_details) && sizeof($membership_details) > 0){
			// Api returns false if no points are expiring
			$point_expiry_details = is_bool($membership_details['point_expiry_details']) ? [] : $membership_details['point_expiry_details'];
			$follower->set_points($membership_details['points'] ?? 0)
			         ->set_total_earned_points($membership_details['total_earn_point'] ?? 0)
			         ->set_points_expire_details($point_expiry_details)
			         ->set_membership_level_created_date($follower_data['level_created_date'])
			         ->set_membership_level_expire_details($membership_details['member_level_expiry_details'])
			         ->set_current_membership_level($membership_details['current_membership_level_title'])
			         ->set_next_membership_level($membership_details['next_membership_level_title'])
			         ->set_next_membership_level_required_points($membership_details['next_membership_level_require_points']);
					//->set_next_membership_level_points($memberships['next_membership_level_points'])
					
		}

		// Segments
		$follower_segments_data = $follower_data['SegmentDetails'] ?? array();
		if(is_array($follower_segments_data) && sizeof($follower_segments_data) > 0) {
			$follower_segments = array();
			foreach ( $follower_segments_data as $seg_key => $segment ) {
				$segment_values   = $segment["follower_segment_values"];

				// Skip empty values
				if(is_string($segment_values) && ($segment_values == '0000-00-00 00:00:00' || empty($segment_values ?? ''))){
					continue;
				}

				$follower_segments[] = array(
					"segment_id" => $segment["segment_id"],
					"segment_type" => $segment["segment_type"],
					"segment_value" => $segment_values,
				);
			}

			$follower->set_segments($follower_segments);
		}
		return $follower;
	}

	
	/**
	 *
	 * Returns WP_Error is no follower is found.
	 *
	 * @param string $activation_token
	 *
	 * @return WP_Error|Diller_Loyalty_Follower
	 */
	public function get_follower_by_activation_token(string $activation_token) {
		$api = new Diller_Loyalty_API_Request( '/getFollower', array( "activation_token" => $activation_token), 'POST');
		$result = $api->request();

		if(is_wp_error($result)){
			return $result;
		}

		$follower_data = $result["result"];

		// Follower main properties
		$follower = (new Diller_Loyalty_Follower())
			->set_full_phone_number($follower_data["country_code"], $follower_data["phone"])
			->set_first_name($follower_data["first_name"])
			->set_last_name($follower_data["last_name"])
			->set_email($follower_data["email"])
			->set_address($follower_data["address"] ?? '')
			->set_gender($follower_data["gender"] ?? 3)
			->set_postal_city($follower_data["city"] ?? '')
			->set_postal_code($follower_data["zip_code"] ?? '')
			->set_country($follower_data["country"] ?? '')
			->set_birth_date($follower_data["date_of_birth"] ?? '')
			->set_membership_consent_accepted(strtotime($follower_data["GDPR_date"]) > 0)
			->set_membership_consent_accepted_date(strtotime($follower_data["GDPR_date"]))
			->set_purchase_history_consent_accepted($follower_data["is_purchase_history"])
			->set_marketing_email_consent_accepted($follower_data["receive_email"])
			->set_marketing_sms_consent_accepted($follower_data["receive_sms"])
			->set_diller_id($follower_data["user_follower_id"])
			->set_remaining_points($follower_data['remaining_points'] ?? 0);

		// Store departments
		//Expecting: ["department_id": 477, "department_values": "Arendal" }, { "department_id": 479, "department_values": "Tromsø" }]
		$follower->set_department_ids(array_column($follower_data['DepartmentDetails'] ?? array(), 'department_id'));

		return $follower;
	}

	/**
	 * Fetches public available registered for this $full_phone_number.
	 * Returns data like name, address, zip code and city.
	 * Only available for NO, SE, DK and FI mobile numbers.
	 *
	 * @since 2.0.0
	 * @since 2.4.0 The response structure was simplified.
	 *
	 * @param string $full_phone_number
	 *
	 * @return array
	 */
	public function get_follower_details_by_phone(string $full_phone_number) {
		$api    = new Diller_Loyalty_API_Request( "/getFollowerDetailsByPhone/$full_phone_number", array(), 'GET');
		$result = $api->request();

		$contact_details = array();
		if(!is_wp_error($result) && (int)$result["result"]["count"] >= 1) {
			$contact = $result["result"]["contacts"][0];
			if(!empty($contact)){
				$contact_details["first_name"]  = trim($contact["firstName"] ?? '');
				$contact_details["last_name"]   = trim($contact["lastName"] ?? '');
				if(!empty($contact["geography"]) && !empty($contact["geography"]["address"])) {
					$contact_details["address"] = trim($contact["geography"]["address"]["street"] ?? '');
					$contact_details["postal_code"] = trim($contact["geography"]["address"]["postCode"] ?? '');
					$contact_details["postal_city"] = trim($contact["geography"]["address"]["postArea"] ?? '');
				}
			}
		}
		return $contact_details;
	}

	/**
	 * Checks whether a given phone number is already registered for this store or not
	 *
	 * @param string $phone_country_code
	 * @param string $phone_number
	 *
	 * @return bool
	 */
	public function check_phone_number_is_available(string $phone_country_code, string $phone_number){
		$api    = new Diller_Loyalty_API_Request( "/getPhoneAvailability", array(
			"country_code" => $phone_country_code,
			"phone" => $phone_number
		), 'POST', "PosV3");

		$result = $api->request();
		return !is_wp_error($result) && $result["success"] !== false;
	}

	/**
	 * Checks whether a given email address is already registered for this store or not
	 *
	 * @param string $email
	 *
	 * @return bool
	 */
	public function check_email_is_available(string $email){
		$api    = new Diller_Loyalty_API_Request( "/checkFollowerEmail", array(
			"country_code" => "", // Required by API v1 even if empty
			"phone" => "", // Required by API v1 even if empty
			"email" => $email
		));
		$result = $api->request();
		return !is_wp_error($result) && $result["success"] !== false;
	}


	/**
	 * Returns the membership details for a given follower
	 *
	 * @param Diller_Loyalty_Follower $follower
	 *
	 * @return Diller_Loyalty_Follower
	 */
	public function get_membership_details_for(Diller_Loyalty_Follower $follower) {
		$api    = new Diller_Loyalty_API_Request( '/getFollower', array( "country_code" => $follower->get_phone_country_code(), "phone" => $follower->get_phone_number()), 'POST', 'PosV2');
		$result = $api->request();

		if(is_wp_error($result)){
			return $result;
		}

		$memberships = $result["result"]['membershipDetails'];
		if(is_array($memberships)){
			// Api returns false if no points are expiring
			$point_expiry_details = is_bool($memberships['point_expiry_details']) ? [] : $memberships['point_expiry_details'];
			$follower->set_points($memberships['points'] ?? 0)
	                 ->set_total_earned_points($memberships['total_earn_point'] ?? 0)
	                 ->set_points_expire_details($point_expiry_details)
	                 ->set_membership_level_created_date($result["result"]['level_created_date'])
	                 ->set_membership_level_expire_details($memberships['member_level_expiry_details'])
	                 ->set_current_membership_level($memberships['current_membership_level_title'])
	                 ->set_next_membership_level($memberships['next_membership_level_title'])
	                 ->set_next_membership_level_required_points($memberships['next_membership_level_require_points']);
					//->set_next_membership_level_points($memberships['next_membership_level_points'])
					
		}
		return $follower;
	}

	/**
	 * Fetches the Follower data object by its phone number. This is a lighter version of the function <code>get_follower_details()</code> as it only returns personal data and not segments, departments data
	 * Use this function for cases like, checking if follower exists or not, if consent was accepted, etc..
	 * Returns WP_Error is no follower is found.
	 *
	 * @param string $country_code
	 * @param string $phone_number
	 *
	 * @return WP_Error|Diller_Loyalty_Follower
	 */
	public function get_follower(string $country_code, string $phone_number) {
		$api = new Diller_Loyalty_API_Request( '/getFollower', array( "country_code" => $country_code, "phone" => $phone_number), 'POST');
		$result = $api->request();

		if(is_wp_error($result)){
			return $result;
		}

		$follower_data = $result["result"];

		// Follower main properties
		$follower = (new Diller_Loyalty_Follower())
				 ->set_full_phone_number($country_code, $phone_number)
				 ->set_first_name($follower_data["first_name"])
		         ->set_last_name($follower_data["last_name"])
		         ->set_email($follower_data["email"])
		         ->set_address($follower_data["address"] ?? '')
		         ->set_gender($follower_data["gender"] ?? 3)
		         ->set_postal_city($follower_data["city"] ?? '')
		         ->set_postal_code($follower_data["zip_code"] ?? '')
		         ->set_country($follower_data["country"] ?? '')
		         ->set_birth_date($follower_data["date_of_birth"] ?? '')
		         ->set_membership_consent_accepted(strtotime($follower_data["GDPR_date"]) > 0)
		         ->set_membership_consent_accepted_date(strtotime($follower_data["GDPR_date"]))
			     ->set_purchase_history_consent_accepted($follower_data["is_purchase_history"])
		         ->set_marketing_email_consent_accepted($follower_data["receive_email"])
		         ->set_marketing_sms_consent_accepted($follower_data["receive_sms"])
		         ->set_diller_id($follower_data["user_follower_id"])
		         ->set_remaining_points($follower_data['remaining_points'] ?? 0);

		// Store departments
		//Expecting: ["department_id": 477, "department_values": "Arendal" }, { "department_id": 479, "department_values": "Tromsø" }]
		$follower->set_department_ids(array_column($follower_data['DepartmentDetails'] ?? array(), 'department_id'));

		return $follower;
	}

	/**
	 *
	 *
	 * @param Diller_Loyalty_Follower $follower
	 *
	 * @return bool
	 */
	public function invite_friend(Diller_Loyalty_Follower $follower, $friends_first_name, $friends_last_name, $friends_email) {
		$api = new Diller_Loyalty_API_Request( '/referAFriend', array(
			"country_code" => $follower->get_phone_country_code(),
			"phone" => $follower->get_phone_number(),
			"first_name" => $friends_first_name,
			"last_name" => $friends_last_name,
			"email" => $friends_email
		), 'POST');

		$result = $api->request();
		return !is_wp_error($result);
	}


	/**
	 * Returns a list of invited friends as well as it's status
	 *
	 * @param Diller_Loyalty_Follower $follower
	 *
	 * @return array
	 */
	public function get_invited_friends_list(Diller_Loyalty_Follower $follower) {
		$api = new Diller_Loyalty_API_Request( '/getFollower', array("country_code" => $follower->get_phone_country_code(), "phone" => $follower->get_phone_number() ), 'POST', 'PosV2');
		$result = $api->request();

		if(is_wp_error($result)){
			return $result;
		}

		$data = array();
		$invitations = $result["result"]['referFriendDetails'];
		if(is_array($invitations) && sizeof($invitations) > 0){
			foreach ($invitations as $key => $invitation):

				$data[] = (new Diller_Loyalty_Follower())
						 ->set_first_name($invitation['first_name'])
				         ->set_last_name($invitation['last_name'])
				         ->set_email($invitation['email'])
				         ->set_status($invitation['status']);

			endforeach;
		}
		return $data;
	}


	/**
	 * Returns the details for the person that was referred to join the Loyalty Program
	 *
	 * @param Diller_Loyalty_Follower $referral
	 *
	 * @return mixed
	 */
	public function get_invited_friend_details($referral_id) {
		$api = new Diller_Loyalty_API_Request( '/getReferFriendDetails', array( "refer_id" => $referral_id ), 'POST');
		$result = $api->request();

		if(!is_wp_error($result)){
			return (new Diller_Loyalty_Follower())
				->set_first_name($result["result"]['first_name'])
				->set_last_name($result["result"]['last_name'])
				->set_email($result["result"]['email'])
				->set_diller_id($result["result"]['user_follower_id']);
		}
		return $result;
	}


	/**
	 * Returns all the stamps for a given follower
	 *
	 * @param Diller_Loyalty_Follower $follower
	 *
	 * @return mixed
	 */
	public function get_stamps_for(Diller_Loyalty_Follower $follower) {

		$api = new Diller_Loyalty_API_Request( '/GetFollowerStamps', array(
			"country_code" => $follower->get_phone_country_code(),
			"phone" => $follower->get_phone_number(),
			"type"  => "all"
		), 'POST' );

		$result = $api->request();
		if(!is_wp_error($result)){
			$stamps = array();
			foreach($result["result"] as $key => $data){
				$stamp = new Diller_Loyalty_Stamp($data["Id"], $data["Title"]);
				$stamps[] = $stamp->set_usages($data["Usages"])
				      ->set_valid_until($data["ValidTo"])
				      ->set_discount($data["Discount"])
				      ->set_discount_type($data["DiscountType"])
				      ->set_description($data["Description"])
				      ->set_points_required($data["PointsRequired"])
				      ->set_icon($data["MediaContent"])
				      ->set_last_stamp_text($data["LastStampText"])
				      ->set_product_ids($data["product_id"])
				      ->set_product_category_ids($data["product_category_id"])
				      ->set_product_names($data["product_name"])
				      ->set_auto_start_stamp($data["autoStampStart"])
				      ->set_is_applicable($data["IsApplicable"])
				      ->set_total_redemptions($data["TotalRedemptions"])
				      ->set_remaining_redemptions($data["RemainingRedemptions"]);
			}
			return $stamps;
		}

		return $result;
	}

	/**
	 * Returns the details for a specific stamps
	 *
	 * @param Diller_Loyalty_Follower $follower
	 * @param int $stamp_id
	 *
	 * @return mixed
	 */
	public function get_stamp_details_for(Diller_Loyalty_Follower $follower, string $stamp_id) {

		$api = new Diller_Loyalty_API_Request( '/getStampDetails', array(
			"country_code" => $follower->get_phone_country_code(),
			"phone" => $follower->get_phone_number(),
			"stamp_id" => $stamp_id
		), 'POST' );

		$result = $api->request();
		if(!is_wp_error($result)){
			$data = $result["result"];
			$stamp = new Diller_Loyalty_Stamp($data["Id"], $data["Title"]);
			return $stamp->set_usages($data["Usages"])
		                ->set_valid_until($data["ValidTo"])
						->set_discount($data["Discount"])
						->set_discount_type($data["DiscountType"])
						->set_description($data["Description"])
						->set_points_required($data["PointsRequired"] ?? 0)
						->set_icon($data["MediaContent"])
						->set_last_stamp_text($data["LastStampText"])
						->set_product_ids($data["product_id"])
						->set_product_category_ids($data["product_category_id"])
						->set_product_names($data["product_name"])
						->set_auto_start_stamp($data["autoStampStart"])
						->set_is_applicable($data["IsApplicable"])
						->set_total_redemptions($data["TotalRedemptions"])
						->set_remaining_redemptions($data["RemainingRedemptions"]);
		}

		return $result;
	}


	/**
	 * Returns the details for a specific coupon
	 *
	 * @param Diller_Loyalty_Follower $follower
	 * @param int $coupon_id
	 *
	 * @return mixed
	 */
	public function get_coupon_details_for(Diller_Loyalty_Follower $follower, string $coupon_id) {

		$api = new Diller_Loyalty_API_Request( '/getCouponDetails', array(
			"country_code" => $follower->get_phone_country_code(),
			"phone" => $follower->get_phone_number(),
			"coupon_id" => $coupon_id
		), 'POST' );

		$result = $api->request();
		if(!is_wp_error($result)){
			$data = $result["result"];
			$coupon = new Diller_Loyalty_Coupon($data["Id"], $data["Title"]);
			return $coupon->set_usages($data["Usages"])
			             ->set_valid_until($data["ValidTo"])
			             ->set_discount($data["Discount"])
			             ->set_discount_type($data["DiscountType"])
			             ->set_description($data["Description"])
			             ->set_points_required($data["PointsRequired"] ?? 0)
			             ->set_icon($data["MediaContent"])
			             ->set_product_ids($data["product_id"])
			             ->set_product_category_ids($data["product_category_id"])
			             ->set_product_names($data["product_name"])
			             ->set_is_applicable($data["IsApplicable"])
			             ->set_total_redemptions($data["TotalRedemptions"])
			             ->set_remaining_redemptions($data["RemainingRedemptions"])
			             ->set_promo_code($data["PromoCode"])
			             ->set_membership_level_title($data["MembershipLevelTitle"] ?? "")
			             ->set_woocommerce_id($data["WooCommerceId"] ?? 0)
			             ->set_is_campaign($data["IsCampaign"])
			             ->set_coupon_type($data["CouponType"]);
		}

		return $result;
	}

	public function get_earned_points_for_order(Diller_Loyalty_Follower $follower, string $order_id) {
		$api = new Diller_Loyalty_API_Request( '/getOrderHistoryData', array(
			"country_code" => $follower->get_phone_country_code(),
			"phone" => $follower->get_phone_number(),
			"order_id" => $order_id
		), 'POST' );

		$result = $api->request();
		return !is_wp_error($result) ? $result["result"]["purchase_points"] : $result;
	}

	/**
	 * Cancels the order transaction.
	 *
	 * @param Diller_Loyalty_Follower $follower
	 * @param string $transaction_id
	 *
	 * @return mixed|WP_Error
	 */
	public function cancel_order_transaction_for(Diller_Loyalty_Follower $follower, string $transaction_id) {

		$api = new Diller_Loyalty_API_Request( '/cancelTransaction', array(
			"country_code" => $follower->get_phone_country_code(),
			"phone" => $follower->get_phone_number(),
			"transaction_id" => $transaction_id
		), 'POST' );

		return $api->request();
	}

	/**
	 * Returns all the coupons for a given follower
	 *
	 * @param Diller_Loyalty_Follower $follower
	 *
	 * @return mixed
	 */
	public function get_coupons_for(Diller_Loyalty_Follower $follower) {

		$api = new Diller_Loyalty_API_Request( '/GetFollowerCoupons', array(
			"country_code" => $follower->get_phone_country_code(),
			"phone" => $follower->get_phone_number(),
			"type"  => "active"
		), 'POST', "PosV2" );

		$result = $api->request();
		if(!is_wp_error($result)){
			$coupons = array();
			foreach($result["result"] as $key => $data){
				$coupon = new Diller_Loyalty_Coupon($data["Id"], $data["Title"]);
				$coupons[] = $coupon->set_usages($data["Usages"])
				                   ->set_valid_until($data["ValidTo"])
				                   ->set_discount($data["Discount"])
				                   ->set_discount_type($data["DiscountType"])
				                   ->set_description($data["Description"])
				                   ->set_points_required($data["PointsRequired"] ?? 0)
				                   ->set_icon($data["MediaContent"])
				                   ->set_is_applicable($data["IsApplicable"])
				                   ->set_total_redemptions($data["TotalRedemptions"])
				                   ->set_remaining_redemptions($data["RemainingRedemptions"])
				                   ->set_promo_code($data["PromoCode"])
				                   ->set_membership_level_title($data["MembershipLevelTitle"] ?? "")
				                   ->set_woocommerce_id($data["WooCommerceId"] ?? 0)
				                   ->set_is_campaign($data["IsCampaign"])
				                   ->set_coupon_type($data["CouponType"]);
			}
			return $coupons;
		}

		// API return a 400 if there are no coupons available for this follower. It should have been just an empty array() and return a 200 - OK
		// Therefore we catch it here until it's fixed server side.
		return array();
	}

	/**
	 * Checks whether a given coupon code is valid or not for the follower.
	 * Returns true if valid otherwise returns WP_Error object with a message of why isn't valid.
	 *
	 * @param Diller_Loyalty_Follower $follower
	 * @param string $coupon_code
	 * @param array $product_ids
	 *
	 * @return bool|WP_Error
	 */
	public function validate_coupon_for(Diller_Loyalty_Follower $follower, string $coupon_code, array $product_ids = array()) {
		
		$api = new Diller_Loyalty_API_Request( '/validateCouponPromoCode', array(
			"country_code" => $follower->get_phone_country_code(),
			"phone" => $follower->get_phone_number(),
			"promo_code"  => $coupon_code,
			"product_ids"  => $product_ids
		), 'POST');

		$result = $api->request();

		return !is_wp_error($result) ? true : $result;
	}

	/**
	 * Adds a phone number to Diller API. Follower is created only with phone number and will get sent a link to conclude the registration and accept the GDPR.
	 * This function is used for a POS/Diller enrollment type of scenario.
	 * To find the right values for $department_ids parameter, call the function <code>DillerLoyalty()->get_store()->get_store_departments()</code>
	 *
	 * @param $country_code Phone Country Code
	 * @param $phone_number Phone Number without spaces
	 * @param $department_id Optional. Store department ID. See <code>DillerLoyalty()->get_store()->get_store_departments()</code>
	 *
	 * @return bool|WP_Error
	 */
	public function add_new_follower($country_code, $phone_number, $department_id) {
		$request_data = array(
			"country_code" => $country_code,
			"phone" => $phone_number,
			"department_id" => $department_id,
			'subscribe_type' => 'WOOCOMMERCE',
			'subscribe_by' => 'POS'
		);

		$api = new Diller_Loyalty_API_Request( '/addFollower', $request_data, 'POST');
		$result = $api->request();

		return !is_wp_error($result) ? true : $result;
	}

	/**
	 * Creates a new User/Follower in Diller and fires the action <code>diller_api_follower_registered</code> after the new Follower was successfully created in Diller.
	 *
	 * @return Diller_Loyalty_Follower|mixed|WP_Error
	 */
	public function create_new_follower(Diller_Loyalty_Follower $follower, bool $send_welcome_msg = false, bool $send_password_msg = false) {

		$follower->set_password( wp_generate_password( 12, true, false ) );

		$department_ids = $follower->get_department_ids();
		if(empty($department_ids) && !empty(DillerLoyalty()->get_store()->get_default_store_department())){
			$department_ids[] = DillerLoyalty()->get_store()->get_default_store_department();
		}

		//Map Follower object to the API field
		$request_data = array(
			"country_code"         => $follower->get_phone_country_code(),
			"phone"                => $follower->get_phone_number(),
			"first_name"           => $follower->get_first_name(),
			"last_name"            => $follower->get_last_name(),
			"email"                => $follower->get_email(),
			"address"              => $follower->get_address(),
			"country"              => $follower->get_country(),
			"gender"               => Diller_Loyalty_Helpers::value_from_gender_name( $follower->get_gender() ),
			"city"                 => $follower->get_postal_city(),
			"postcode"             => $follower->get_postal_code(),
			"zip_code"             => $follower->get_postal_code(),
			"date_of_birth"        => $follower->get_birth_date(),
			"privacy_policy"       => $follower->get_membership_consent_accepted(), // Diller API Expects Yes/No value
			"is_purchase_history"  => $follower->get_purchase_history_consent_accepted(), // Diller API Expects Yes/No value
			"receive_sms"          => $follower->get_marketing_sms_consent_accepted(), // Diller API Expects Yes/No value
			"receive_email"        => $follower->get_marketing_email_consent_accepted(), // Diller API Expects Yes/No value
			"password"             => $follower->get_password(),
			"refer_id"             => $follower->get_diller_referral_id(),
			"is_sendsms"           => 0, // Diller API Expects 1/0 value. This field will trigger sending the link to confirm the registration. Eg. https://diller.app/webshop/subscription/U2lRWlVqMExpd3BVSFNJRVlmdGtWZz09
			"is_welcome_sms"       => $send_welcome_msg ? 1 : 0, // Diller API Expects 1/0 value.
			"department_id"        => $department_ids, // Diller API Expects array of ints // Mandatory, otherwise departments get reset. Diller API Expects array of ints
			"has_external_account" => 'Yes', // This flag tells Diller api, to redirect the user to this store my account rather than the retailer panel
		);

		// This field will trigger sending the welcome text + password for logging into Diller
		if($send_password_msg){
			$request_data["is_password_sms"] = 1; // Diller API Expects 1/0 value.
		}

		// Diller API Expects array of objects, with multi-values concatenated (if applicable). Eg:
		// [
		//      { "segment_id": 318, "segment_type": 1, "segment_value" : "Lorem ipsum" },
		//      { "segment_id": 319, "segment_type": 4, "segment_value" : "Value1,Value2,Value3" } // for arrays
		// ]
		$request_data["segment_details"] =  $follower->get_segments();
		Diller_Loyalty_Helpers::join_multi_value_array_field($request_data["segment_details"], "segment_value");

		$api    = new Diller_Loyalty_API_Request( '/addRegistration', $request_data, 'POST');
		$result = $api->request();

		if(is_wp_error($result)){
			//Add extra data for logging the error
			$result->add_data($request_data, "request_data");
			return $result;
		}

		$follower->set_diller_id($result["response"]["user_id"]);

		/**
		 * Fires after the new Follower was successfully created in Diller.
		 *
		 * @since 2.0
		 *
		 * @param Diller_Loyalty_Follower $follower Follower object.
		 */
		do_action( 'diller_api_follower_registered', $follower);

		return $follower;
	}

	public function reset_phone_number($values = array()) {
		$request_data = array(
			"country_code" => $values["previous_phone_country_code"],
			"phone" => $values["previous_phone_number"],
			"change_country_code" => $values["phone_country_code"],
			"change_phone" => $values["phone_number"]
		);

		$api    = new Diller_Loyalty_API_Request( '/resetPhoneNumber', $request_data, 'POST');
		$result = $api->request();

		return !is_wp_error($result);
	}

	public function verify_phone_number_change($values = array()) {
		$request_data = array(
			"country_code" => $values["previous_phone_country_code"],
			"phone" => $values["previous_phone_number"],
			"otp" => $values["phone_verification_code"],
			"change_country_code" => $values["phone_country_code"],
			"change_phone" => $values["phone_number"]
		);

		$api    = new Diller_Loyalty_API_Request( '/VerifyChangePhoneNumber', $request_data, 'POST');
		$result = $api->request();

		return !is_wp_error($result);
	}

	/**
	 * Sends an SMS with an OTP code for the Follower to be able to login into My Account.
	 * @param $dial_code
	 * @param $phone
	 *
	 * @return bool
	 */
	public function send_sms_login_code($dial_code, $phone) {
		$api    = new Diller_Loyalty_API_Request( '/sendOTP', array( "country_code" => $dial_code, "phone" => $phone ), 'POST');
		$result = $api->request();

		return !is_wp_error($result);
	}

	/**
	 * Validates the OTP code for the Follower to be able to login into My Account.
	 *
	 * @param $dial_code
	 * @param $phone
	 * @param $otp_code
	 *
	 * @return bool
	 */
	public function validate_sms_login_code($dial_code, $phone, $otp_code) {
		$api    = new Diller_Loyalty_API_Request( '/validateOTP', array( "country_code" => $dial_code, "phone" => $phone,  "otp_code" =>  $otp_code), 'POST');
		$result = $api->request();

		return !is_wp_error($result);
	}

	/**
	 * Updates an existing Follower in Diller and fires the action <code>diller_api_follower_updated</code> if Follower was successfully updated in Diller.
	 *
	 * @param Diller_Loyalty_Follower $follower
	 *
	 * @return Diller_Loyalty_Follower|mixed|WP_Error
	 */
	public function update_follower(Diller_Loyalty_Follower $follower, bool $send_welcome_msg = false, bool $send_password_msg = false) {

		//Map Follower object to the API field
		$request_data = array(
			"country_code"         => $follower->get_phone_country_code(),
			"phone"                => $follower->get_phone_number(),
			"first_name"           => $follower->get_first_name(),
			"last_name"            => $follower->get_last_name(),
			"email"                => $follower->get_email(),
			"gender"               => Diller_Loyalty_Helpers::value_from_gender_name( $follower->get_gender() ),
			"address"              => $follower->get_address(),
			"country"              => $follower->get_country(),
			"city"                 => $follower->get_postal_city(),
			"postcode"             => $follower->get_postal_code(),
			"zip_code"             => $follower->get_postal_code(),
			"date_of_birth"        => $follower->get_birth_date(),
			"privacy_policy"       => $follower->get_membership_consent_accepted(), // Diller API Expects Yes/No value
			"is_purchase_history"  => $follower->get_purchase_history_consent_accepted(), // Diller API Expects Yes/No value
			"receive_sms"          => $follower->get_marketing_sms_consent_accepted(), // Diller API Expects Yes/No value
			"receive_email"        => $follower->get_marketing_email_consent_accepted(), // Diller API Expects Yes/No value
			"is_sendsms"           => 0, // Diller API Expects 1/0 value
			"is_welcome_sms"       => $send_welcome_msg ? 1 : 0, // Diller API Expects 1/0 value.
			"is_password_sms"      => 0, // Diller API Expects 1/0 value
			"department_id"        => $follower->get_department_ids(), // Mandatory, otherwise departments get reset. Diller API Expects array of ints
			"has_external_account" => 'Yes', // This flag tells Diller api, to redirect the user to this store my account rather than the retailer panel
			"activation_token"     => $follower->get_activation_token()
		);

		// Send profile_updated_at only if the member has updated their profile
		if(!empty($follower->get_profile_updated_at())){
			$request_data["profile_updated_at"] = $follower->get_profile_updated_at();
		}

		
		// then we need to send in param q_hidden_val so that Diller API accepts GDPR
		if($follower->get_force_membership_consent_acceptance()){
			$request_data["q_hidden_val"] = true;
		}

		// This field will trigger sending the welcome text + password for logging into Diller
		if($send_password_msg){
			$follower->set_password( wp_generate_password( 12, true, false ) );
			$request_data["password"] = $follower->get_password();
			$request_data["is_password_sms"] = 1; // Diller API Expects 1/0 value.
		}

		// Diller API Expects array of objects, with multivalues concatenated (if applicable). Eg:
		// [
		//      { "segment_id": 318, "segment_type": 1, "segment_value" : "Lorem ipsum" },
		//      { "segment_id": 319, "segment_type": 4, "segment_value" : "Value1,Value2,Value3" } // for arrays
		// ]
		$request_data["segment_details"] =  $follower->get_segments();
		Diller_Loyalty_Helpers::join_multi_value_array_field($request_data["segment_details"], "segment_value");

		$api    = new Diller_Loyalty_API_Request( '/updateFollower', $request_data, 'POST', "PosV2");
		$result = $api->request();

		if(is_wp_error($result)){
			//Add extra data for logging the error
			$result->add_data($request_data, "request_data");

			DillerLoyalty()->get_logger()->error(sprintf("API: Error while calling `%s`", __FUNCTION__), $follower, $result);

			return $result;
		}

		/**
		 * Fires after the Follower was successfully updated in Diller.
		 *
		 * @since 2.0
		 *
		 * @param Diller_Loyalty_Follower $follower Follower object.
		 */
		do_action( 'diller_api_follower_updated', $follower);

		return $follower;
	}

	/**
	 * Sends the wordpress password to a given follower.
	 * It assumes, password was previously assigned by calling <code>$follower->set_password('xxxx')<code>.
	 * This method calls the API endpoint `updateFollower` under the hood.
	 *
	 * @param Diller_Loyalty_Follower $follower
	 *
	 * @return bool
	 */
	public function send_follower_password(Diller_Loyalty_Follower $follower) {
		if(empty($follower->get_password())) {
			_doing_it_wrong( __FUNCTION__, "You have to call \$follower->set_password('xxxxx') and set a password before calling this function", '2.0' );
		}

		if(empty($follower->get_first_name()) || empty($follower->get_last_name())) {
			_doing_it_wrong( __FUNCTION__, "You have to provide a value for first_name and last_name ( eg. \$follower->set_first_name('john') \$follower->set_last_name('doe') ) before calling this function.", '2.0' );
		}

		//Map Follower object to the API field
		$request_data = array(
			// NB: If we do not send first and last name, API will return 400 - "You are not allowed to update Country Code, Phone"
			"first_name"           => $follower->get_first_name(),
			"last_name"            => $follower->get_last_name(),
			"country_code"         => $follower->get_phone_country_code(),
			"phone"                => $follower->get_phone_number(),
			"is_sendsms"           => 0, // Diller API Expects 1/0 value
			"is_welcome_sms"       => 0, // Diller API Expects 1/0 value
			"is_password_sms"      => 1, // Diller API Expects 1/0 value
			"password"             => $follower->get_password(),
			"department_id"        => $follower->get_department_ids(), // Mandatory, otherwise departments get reset. Diller API Expects array of ints
			"has_external_account" => 'Yes'
		);

		$api = new Diller_Loyalty_API_Request( '/updateFollower', $request_data, 'POST', "PosV2");
		$result = $api->request();

		if(is_wp_error($result)){
			//Add extra data for logging the error
			$result->add_data($request_data, "request_data");

			DillerLoyalty()->get_logger()->error(sprintf("API: Error while calling `%s`", __FUNCTION__), $follower, $result);
		}

		return !is_wp_error($result);
	}

	public function unsubscribe_follower(Diller_Loyalty_Follower $follower) {

		//Map Follower object to the API field
		$request_data = array(
			"country_code"         => $follower->get_phone_country_code(),
			"phone"                => $follower->get_phone_number(),
			"first_name"           => $follower->get_first_name(),
			"last_name"            => $follower->get_last_name(),
			"email"                => $follower->get_email(),
			"privacy_policy"       => 'No',
			"is_purchase_history"  => 'No',
			"receive_sms"          => 'No',
			"receive_email"        => 'No',
			"is_sendsms"           => 0,
			"is_welcome_sms"       => 0,
			"is_password_sms"      => 0,
			"department_id"        => $follower->get_department_ids(),
			"has_external_account" => 'No' // This will make Diller API to redirect user to Diller Retailer form, rather than webshop URL.
		);

		$api    = new Diller_Loyalty_API_Request( '/updateFollower', $request_data, 'POST', "PosV2");
		$result = $api->request();

		if(!is_wp_error($result)){
			do_action( 'diller_api_follower_unsubscribed', $follower);
		}

		return $result;
	}

	public function save_follower_transactions(Diller_Loyalty_Follower $follower, WC_Order $order){
		// Hint: Refer to "get_order()" in src/wp-content/plugins/woocommerce/includes/legacy/api/v3/class-wc-api-orders.php for the order object structure
		$coupon_codes = $order->get_coupon_codes();
		$transaction_total_amount = $order->get_total() - (float) $order->get_total_refunded();
		$transaction_net_amount = $transaction_total_amount - $order->get_total_tax() - $order->get_total_tax_refunded();
		$created_date = get_gmt_from_date($order->get_date_created(),'Y-m-d');
		$created_time_utc = get_gmt_from_date($order->get_date_created(), 'H:i:s');
		$product_details = array();

		if ( false === $order->get_prices_include_tax() ) {
			$transaction_net_amount = $transaction_total_amount -= $order->get_total_tax();
		}

		$temp = [];

		foreach( $order->get_items(array("line_item", "shipping", "fee")) as $item_id => $item_data ) {
			// Get item subtotal - this is the cost before discount, with VAT.
			$item_price_with_tax = $order->get_item_subtotal($item_data, true);
			$line_total_tax = $item_data->get_total_tax();
			$line_total = $item_data->get_total();
			$line_total_with_taxes = $order->get_line_total( $item_data, true );
			$original_qty = $item_data->get_quantity();
			$refunded_qty = $order->get_qty_refunded_for_item( $item_id );
			$remaining_qty = $original_qty + $refunded_qty;
			$refunded_amount = $order->get_total_refunded_for_item( $item_id );
			$vat_percentage = (float) round($line_total_tax > 0 ? (( $line_total_tax / $line_total ) * 100) : 0);
			$product_discount_amount = 0;
			$refunded_tax_amount = 0;

			foreach ($order->get_taxes() as $tax_item ) {
				$refunded_tax_amount += $order->get_tax_refunded_for_item($item_id, $tax_item->get_rate_id());
			}

			if( ($refunded_qty * -1) === $original_qty ){
				continue; // Skip fully refunded items
			}

			if ( $item_data->get_type() === "line_item" ) {
				$product = $item_data->get_product();
				$product_category = "";
				if( ($product_categories = get_the_terms( $product->get_id(), 'product_cat' )) && !is_wp_error($product_categories) ){
					$product_category = count($product_categories) > 0 ? $product_categories[0]->term_taxonomy_id : "";
				}

				if ( $item_data->get_subtotal() !== $item_data->get_total() ) {
					$item_unit_price = ($item_data->get_subtotal() - $item_data->get_total()) / $original_qty;
					$product_discount_amount = $item_unit_price * $remaining_qty;
				}

				$product_details[] = array(
					'product_id'              => $product->get_id(),
					'product_name'            => $product->get_name(),
					'product_category_id'     => $product_category,
					'product_qty'             => $original_qty + $refunded_qty, // Refunded qty is negative
					'product_price'           => $item_price_with_tax,
					'product_total_price'     => $line_total_with_taxes - $refunded_amount - $refunded_tax_amount,
					'product_discount_amount' => $product_discount_amount,
					'vat_percentage'          => $vat_percentage,
					'unit_type'               => $product->get_meta( '_unit', true )
				);

				continue;
			}

			if ( in_array( $item_data->get_type(), array( "shipping", "fee" ) ) ) {
				$item_name_prefix = $item_data->get_type() == 'shipping' ? 'Shipping' : 'Fee';
				$product_details[] = array(
					'product_id'          => $item_data->get_id(),
					'product_category_id' => '',
					'product_qty'         => $original_qty - $refunded_qty,
					'product_price'       => $line_total,
					'vat_percentage'      => $vat_percentage,
					'product_total_price' => $line_total,
					'product_name'        => sprintf( "%s: %s", __( $item_name_prefix, 'woocommerce' ), $item_data->get_name() )
				);
			}
		}

		$transaction_details = array(
			"transaction_date"                  => $created_date,
			"transaction_time"                  => $created_time_utc,
			"transaction_id"                    => $order->get_id(),
			"transaction_total_amount"          => $transaction_total_amount,
			"VAT_total_amount"                  => $order->get_total_tax(),
			"NET_total_amount"                  => $transaction_net_amount,
			"employee_name"                     => $this->get_current_employee_name(),
			"transaction_total_discount_amount" => (float) $order->get_discount_total(),
			"payment_method"                    => $order->get_payment_method_title(),
			"currency"                          => $order->get_currency(),
			"eftpos_text"                       => "Online", 
		);

		$request_data = array(
			'country_code'        => $follower->get_phone_country_code(),
			'phone'               => $follower->get_phone_number(),
			'promo_code'          => $coupon_codes,
			'transaction_details' => $transaction_details,
			'product_details'     => $product_details,
			'department_id'       => DillerLoyalty()->get_store()->get_default_store_department(),
		);

		$api    = new Diller_Loyalty_API_Request( '/addTransaction', $request_data, 'POST');
		$result = $api->request();

		if(is_wp_error($result)){
			//Add extra data for logging the error
			$result->add_data($request_data, "request_data");
			DillerLoyalty()->get_logger()->error(sprintf("API: Error while calling `%s`", __FUNCTION__), $follower, $result);
			return $result;
		}

		do_action( 'diller_api_follower_transaction_saved', $order->get_id());

		return $result;
	}

	/**
	 * Updates Follower's point balance in Diller Api with the refunded amount deducted.
	 * This will create a special transaction in Diller, flagged as "REFUND" to keep track of the refunds
	 *
	 * @since 2.4.0
	 *
	 * @param Diller_Loyalty_Follower $follower
	 * @param WC_Order_Refund $refund
	 *
	 * @return mixed|WP_Error
	 */
	public function refund_order_transaction(Diller_Loyalty_Follower $follower, WC_Order_Refund $refund){
		$order = wc_get_order($refund->get_parent_id());
		$refund_total_amount = (float) $refund->get_amount() * - 1;
		$vat_total_amount = $refund->get_total_tax();
		$products = array();


		// Loop through the order refund line items
		foreach( $refund->get_items(array("line_item", "shipping", "fee")) as $item ){
			$refunded_quantity   = (float) $item->get_quantity(); // Quantity: zero or negative integer
			$item_price          = - 1 * $order->get_item_subtotal( $item, true );
			$line_total          = $order->get_line_total($item);
			$line_total_tax      = (float) $item->get_total_tax();
			$vat_percentage      = round(abs($line_total_tax) > 0 ? (abs($line_total_tax) * 100) / abs($line_total) : 0);

			if($item->get_type() === "line_item"){
				$product = $item->get_product();
				$product_category = "";
				if( ($product_categories = get_the_terms( $product->get_id(), 'product_cat' )) && !is_wp_error($product_categories) ){
					$product_category = count($product_categories) > 0 ? $product_categories[0]->term_taxonomy_id : "";
				}

				$products[] = array(
					'product_id'          => $product->get_id(),
					'product_name'        => $product->get_name(),
					'product_category_id' => $product_category,
					'product_qty'         => $refunded_quantity,
					'product_price'       => $item_price,
					'product_total_price' => $line_total,
					'vat_percentage'      => $vat_percentage,
					'unit_type'           => $product->get_meta('_unit', true ),
				);

				continue;
			}

			if ( in_array($item->get_type(), array("shipping", "fee") ) ) {
				$item_name_prefix = $item->get_type() == 'shipping' ? 'Shipping' : 'Fee';
				$products[] = array(
					'product_id'          => $item->get_id(),
					'product_category_id' => '',
					'product_qty'         => $refunded_quantity * -1,
					'product_price'       => $line_total,
					'vat_percentage'      => $vat_percentage,
					'product_total_price' => $line_total,
					'product_name'        => sprintf( "%s: %s", __($item_name_prefix, 'woocommerce'), $item->get_name() )
				);
			}
		}

		$request_data = array(
			'country_code'    => $follower->get_phone_country_code(),
			'phone'           => $follower->get_phone_number(),
			'department_id'   => DillerLoyalty()->get_store()->get_default_store_department(),
			'product_details' => $products,
			'transaction_details' => array(
				"transaction_id"           => sprintf( "REFUND/%s/%s", $order->get_id(), $refund->get_id() ),
				"original_transaction_id"  => $order->get_id(),
				"transaction_date"         => gmdate('Y-m-d', strtotime( $refund->get_date_created() ) ),
				"transaction_time"         => gmdate('H:i:s', strtotime( $refund->get_date_created() ) ),
				"transaction_total_amount" => $refund_total_amount,
				"VAT_total_amount"         => $vat_total_amount,
				"NET_total_amount"         => $refund_total_amount - $vat_total_amount,
				"employee_name"            => $this->get_current_employee_name(),
				"payment_method"           => $this->try_get_order_refund_method( $order ),
				"currency"                 => $refund->get_currency(),
				"eftpos_text"              => "Online", 
			),
		);

		$api    = new Diller_Loyalty_API_Request( "/addTransaction", $request_data, 'POST', );
		$result = $api->request();

		if(is_wp_error($result)){
			//Add extra data for logging the error
			$result->add_data($request_data, "request_data");
			DillerLoyalty()->get_logger()->error(sprintf("API: Error while calling `%s`", __FUNCTION__), $follower, $result);
		}

		return $result;
	}

	/**
	 * Updates Follower's point balance in Diller Api, restoring the refunded amount deleted.
	 * This will call the cancel order endpoint in Diller, to cancel the REFUND transaction.
	 *
	 * @since 2.4.0
	 *
	 * @param int $refund_id
	 * @param WC_Order $order
	 * @param Diller_Loyalty_Follower $follower
	 *
	 * @return mixed|WP_Error
	 */
	public function delete_transaction_refund(int $refund_id, WC_Order $order, Diller_Loyalty_Follower $follower){
		$transaction_id = sprintf( "REFUND/%s/%s", $order->get_id(), $refund_id );
		return $this->cancel_order_transaction_for($follower, $transaction_id);
	}

	/**
	 * Reserves the coupons for the given follower, based on the order.
	 * This will call will ensure that the coupons are reserved for the follower and be used the allowed number of times.
	 * The need for this call is because between the order creation and the order fulfillment and registration in Diller,
	 * the coupons could be abused and used multiple times.
	 *
	 * @since 2.4.0
	 *
	 * @param Diller_Loyalty_Follower $follower
	 * @param WC_Order $order
	 *
	 * @return mixed|WP_Error
	 *
	 */
	public function update_coupon_reservation(Diller_Loyalty_Follower $follower, WC_Order $order){
		$reservations = array();

		foreach ( $order->get_coupon_codes() as $coupon_code ) {

			if ( ! ( $coupon = new WC_Coupon( $coupon_code ) ) ){
				DillerLoyalty()->get_logger()->error(sprintf("API: Error while calling `%s`. Reason: coupon with promo code \"%s\" was not found.", __FUNCTION__, $coupon_code), $follower);
				continue;
			}

			// Check if the coupon of a valid typed supported by Diller
			$allowed_coupon_types = array("percent", "free_shipping", "fixed_cart");
			if( !in_array($coupon->get_discount_type(), $allowed_coupon_types) ){
				DillerLoyalty()->get_logger()->error(
					sprintf(
						"API: Error while calling `%s`. Reason: coupon with promo code \"%s\" is of type \"%s\" but only types \"%s\" are allowed in Diller.",
						__FUNCTION__,
						$coupon_code,
						$coupon->get_discount_type(),
						implode(", ", $allowed_coupon_types)
					),
					$follower
				);

				continue;
			}

			$is_diller_coupon = ((int)$coupon->get_meta("store_id")) > 0;
			$is_public_coupon = ((int)$coupon->get_meta("is_public")) === 1;
			$product_ids = $coupon->get_meta("product_ids") ?? [];

			if(!$is_diller_coupon || $is_public_coupon) continue;

			// No product ids
			if(count($product_ids) === 0){
				$reservations[] = array(
					"coupon_id"  => $coupon->get_id(),
					"product_id" => '',
				);
				continue;
			}

			// Getting the products in this order_id
			$items = $order->get_items();
			foreach ($items as $item) {
				if ( ($product = $item->get_product()) && in_array( $product->get_id(), $product_ids ) ) {
					$reservations[] = array(
						"coupon_id"  => $coupon->get_id(),
						"product_id" => $product->get_id(),
					);
				}
			}

			

			
		}

//		// Assumption for refunded orders: once a full refund was made, then the coupon should be available again.
//		// Therefore, we release the coupon reservation by forcing an empty array of reservations.
//		if($order->get_status() === "refunded"){
//			$reservations = array();
//		}

		$request_data = array(
			'country_code'   => $follower->get_phone_country_code(),
			'phone'          => $follower->get_phone_number(),
			'department_id'  => DillerLoyalty()->get_store()->get_default_store_department(),
			'transaction_id' => $order->get_id(),
			'coupons'        => $reservations, // Empty array will release all coupons for this order in Diller
		);

		$api    = new Diller_Loyalty_API_Request( "/updateCouponsReservation", $request_data, 'POST', 'PosV3' );
		$result = $api->request();

		if(is_wp_error($result)){
			//Add extra data for logging the error
			$result->add_data($request_data, "request_data");
			DillerLoyalty()->get_logger()->error(sprintf("API: Error while calling `%s`", __FUNCTION__), $follower, $result);
		}
		else{
			DillerLoyalty()->get_logger()->info("API: Coupons successfully reserved in Diller.", $follower, $request_data);
		}

		return $result;
	}

	public function remove_coupon_reservation(Diller_Loyalty_Follower $follower, WC_Order $order){
		$request_data = array(
			'country_code'   => $follower->get_phone_country_code(),
			'phone'          => $follower->get_phone_number(),
			'department_id'  => DillerLoyalty()->get_store()->get_default_store_department(),
			'transaction_id' => $order->get_id(),
			'coupons'        => array(), // Empty array will release all coupons for this order in Diller
		);

		$api    = new Diller_Loyalty_API_Request( "/removeCouponsReservation", $request_data, 'POST', 'PosV3' );
		$result = $api->request();

		if(is_wp_error($result)){
			//Add extra data for logging the error
			$result->add_data($request_data, "request_data");
			DillerLoyalty()->get_logger()->error(sprintf("API: Error while calling `%s`", __FUNCTION__), $follower, $result);
		}

		return $result;
	}

	/**
	 * Helper method that extracts the name of the refund method used for an order.
	 *
	 * @since 2.4.0
	 *
	 * @param WC_Order $order
	 *
	 * @return string The refund method name
	 *
	 */
	private function try_get_order_refund_method($order) {
		if ( is_int( $order ) ) {
			$order = wc_get_order( $order );
		}

		if ( false === is_a( $order, 'WC_Order' )) {
			return '';
		}

		// Vipps refund
		if ( $order->get_payment_method() === "vipps" && intval( $order->get_meta( '_vipps_refund_transid' ) ) > 0 ) {
			return 'Refund: via Vipps payment gateway';
		}

		// Dintero refund
		// Dintero refunds are store as notes and not metadata, so we have to parse the notes.
		// Code extracted from their original plugin: https://github.com/Dintero/Dintero.Checkout.WooCommerce
		if ( $order->get_payment_method() === 'dintero-hp' ) {
			$notes = wc_get_order_notes( [
				'order_id' => $order->get_id(),
				'type'     => 'internal'
			] );

			// Search for word "dintero" inside $notes content
			foreach ( $notes as $note ) {
				if ( strpos( $note->content, 'Payment refunded via Dintero.' ) !== false ) {
					return 'Refund: via Dintero payment gateway';
				}
			}
		}

		// Klarna refund
		// AS per 10.2023 klarna-checkout-for-woocommerce plugin (from Krokedil) doesn't handle order management,
		// but it allows Klarna Order Management plugin to process refunds

		//... Add others refund methods here

		return 'Refund: Manual';
	}

	/**
	 * The function returns a formatted string containing the user's display name
	 * and email address, or an empty string if no valid user is found.
	 *
	 * @return string Formatted string "Display Name (email@example.com)" or empty string if no user found.
	 *
	 * @since 2.4.5
	 * @access private
	 *
	 * @uses DillerLoyalty::get_current_admin_user() To get the current admin WP_User object.
	 */
	private function get_current_employee_name() {
		if(!($admin_user = DillerLoyalty()->is_admin_context() ? wp_get_current_user() : null)){
			return "";
		}
		return sprintf( "%s (%s)", $admin_user->display_name, $admin_user->user_email );
	}
}
