<?php
/**
 * Support for Woo Cart Abandonment Recovery coupon snapshots.
 *
 * @package PeachPay
 */

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

if ( ! defined( 'PEACHPAY_WCAR_ORIGINAL_COUPONS_FIELD' ) ) {
	define( 'PEACHPAY_WCAR_ORIGINAL_COUPONS_FIELD', 'peachpay_original_applied_coupons' );
}

/**
 * Initializes support for Woo Cart Abandonment Recovery.
 */
function peachpay_wcar_init() {
	add_filter( 'wcar_cart_default_other_fields', 'peachpay_wcar_add_coupon_snapshot_to_other_fields', 10, 1 );
	add_filter( 'woo_ca_session_abandoned_data', 'peachpay_wcar_filter_session_abandoned_data', 10, 1 );
	add_action( 'wcar_after_restore_cart_abandonment_data', 'peachpay_wcar_restore_original_coupons', 10, 1 );
}
add_action( 'peachpay_init_compatibility', 'peachpay_wcar_init' );

/**
 * Adds the original applied coupon snapshot to WCA other fields.
 *
 * @param array $other_fields WCA other fields.
 * @return array
 */
function peachpay_wcar_add_coupon_snapshot_to_other_fields( $other_fields ) {
	if ( ! is_array( $other_fields ) ) {
		$other_fields = array();
	}

	$other_fields[ PEACHPAY_WCAR_ORIGINAL_COUPONS_FIELD ] = peachpay_wcar_encode_coupon_snapshot( peachpay_wcar_get_cart_coupon_codes() );
	return $other_fields;
}

/**
 * Defensive fallback for WCA builds that filter the whole abandoned-session row.
 *
 * @param array $checkout_details WCA checkout details row.
 * @return array
 */
function peachpay_wcar_filter_session_abandoned_data( $checkout_details ) {
	if ( ! is_array( $checkout_details ) || ! isset( $checkout_details['other_fields'] ) ) {
		return $checkout_details;
	}

	$other_fields = peachpay_wcar_decode_other_fields( $checkout_details['other_fields'] );
	if ( isset( $other_fields[ PEACHPAY_WCAR_ORIGINAL_COUPONS_FIELD ] ) ) {
		return $checkout_details;
	}

	$other_fields = peachpay_wcar_add_coupon_snapshot_to_other_fields( $other_fields );
	$checkout_details['other_fields'] = maybe_serialize( $other_fields );

	return $checkout_details;
}

/**
 * Restores original coupons after WCA rebuilds the cart.
 *
 * @param array $token_data WCA recovery token data.
 */
function peachpay_wcar_restore_original_coupons( $token_data ) {
	$cart = peachpay_wcar_get_cart();
	if ( ! $cart ) {
		return;
	}

	$session_id = is_array( $token_data ) && isset( $token_data['wcf_session_id'] ) ? sanitize_text_field( $token_data['wcf_session_id'] ) : '';
	if ( '' === $session_id ) {
		peachpay_wcar_calculate_totals( $cart );
		return;
	}

	$checkout_details = peachpay_wcar_get_checkout_details( $session_id );
	$other_fields     = ( $checkout_details && isset( $checkout_details->other_fields ) ) ? peachpay_wcar_decode_other_fields( $checkout_details->other_fields ) : array();
	$original_coupons = peachpay_wcar_decode_coupon_snapshot( isset( $other_fields[ PEACHPAY_WCAR_ORIGINAL_COUPONS_FIELD ] ) ? $other_fields[ PEACHPAY_WCAR_ORIGINAL_COUPONS_FIELD ] : '' );

	if ( empty( $original_coupons ) ) {
		peachpay_wcar_calculate_totals( $cart );
		return;
	}

	$wca_coupon_code = is_array( $token_data ) && isset( $token_data['wcf_coupon_code'] ) ? peachpay_wcar_sanitize_coupon_code( $token_data['wcf_coupon_code'] ) : '';
	$wca_coupon_was_applied = '' !== $wca_coupon_code && peachpay_wcar_cart_has_coupon( $cart, $wca_coupon_code );

	if ( $wca_coupon_was_applied && method_exists( $cart, 'remove_coupon' ) ) {
		$cart->remove_coupon( $wca_coupon_code );
	}

	foreach ( $original_coupons as $coupon_code ) {
		if ( peachpay_wcar_cart_has_coupon( $cart, $coupon_code ) ) {
			continue;
		}

		if ( ! peachpay_wcar_apply_coupon_without_notices( $cart, $coupon_code ) ) {
			peachpay_wcar_log_coupon_skip( $session_id, $coupon_code, 'original_coupon_rejected' );
		}
	}

	$restored_original_coupons = peachpay_wcar_current_cart_snapshot_coupons( $cart, $original_coupons );
	if ( $wca_coupon_was_applied && '' !== $wca_coupon_code && ! peachpay_wcar_cart_has_coupon( $cart, $wca_coupon_code ) ) {
		if ( ! peachpay_wcar_apply_coupon_without_notices( $cart, $wca_coupon_code ) ) {
			peachpay_wcar_log_coupon_skip( $session_id, $wca_coupon_code, 'wca_incentive_rejected_after_originals' );
		} elseif ( ! peachpay_wcar_cart_contains_all_coupons( $cart, $restored_original_coupons ) ) {
			if ( method_exists( $cart, 'remove_coupon' ) ) {
				$cart->remove_coupon( $wca_coupon_code );
			}
			peachpay_wcar_reapply_missing_original_coupons( $cart, $restored_original_coupons, $session_id );
			peachpay_wcar_log_coupon_skip( $session_id, $wca_coupon_code, 'wca_incentive_removed_restored_originals' );
		}
	}

	peachpay_wcar_calculate_totals( $cart );
}

/**
 * Gets the active WooCommerce cart.
 *
 * @return object|null
 */
function peachpay_wcar_get_cart() {
	if ( ! function_exists( 'WC' ) ) {
		return null;
	}

	$wc = WC();
	return ( $wc && isset( $wc->cart ) ) ? $wc->cart : null;
}

/**
 * Gets applied cart coupon codes.
 *
 * @return array
 */
function peachpay_wcar_get_cart_coupon_codes() {
	$cart = peachpay_wcar_get_cart();
	if ( ! $cart || ! method_exists( $cart, 'get_applied_coupons' ) ) {
		return array();
	}

	return $cart->get_applied_coupons();
}

/**
 * Encodes coupon codes for WCA other_fields scalar storage.
 *
 * @param array $coupon_codes Coupon codes.
 * @return string
 */
function peachpay_wcar_encode_coupon_snapshot( $coupon_codes ) {
	return wp_json_encode( peachpay_wcar_normalize_coupon_codes( $coupon_codes ) );
}

/**
 * Decodes a coupon snapshot scalar.
 *
 * @param string $snapshot Encoded snapshot.
 * @return array
 */
function peachpay_wcar_decode_coupon_snapshot( $snapshot ) {
	if ( ! is_string( $snapshot ) || '' === trim( $snapshot ) ) {
		return array();
	}

	$decoded = json_decode( $snapshot, true );
	if ( ! is_array( $decoded ) ) {
		return array();
	}

	return peachpay_wcar_normalize_coupon_codes( $decoded );
}

/**
 * Sanitizes and dedupes coupon codes while preserving order.
 *
 * @param array $coupon_codes Coupon codes.
 * @return array
 */
function peachpay_wcar_normalize_coupon_codes( $coupon_codes ) {
	if ( ! is_array( $coupon_codes ) ) {
		return array();
	}

	$normalized = array();
	$seen       = array();

	foreach ( $coupon_codes as $coupon_code ) {
		$sanitized = peachpay_wcar_sanitize_coupon_code( $coupon_code );
		if ( '' === $sanitized || isset( $seen[ $sanitized ] ) ) {
			continue;
		}

		$seen[ $sanitized ] = true;
		$normalized[]       = $sanitized;
	}

	return $normalized;
}

/**
 * Sanitizes one coupon code for storage/application.
 *
 * @param mixed $coupon_code Coupon code.
 * @return string
 */
function peachpay_wcar_sanitize_coupon_code( $coupon_code ) {
	if ( is_array( $coupon_code ) || is_object( $coupon_code ) ) {
		return '';
	}

	$code = function_exists( 'wc_format_coupon_code' ) ? wc_format_coupon_code( $coupon_code ) : $coupon_code;
	$code = sanitize_text_field( $code );
	return function_exists( 'wc_strtolower' ) ? wc_strtolower( $code ) : strtolower( $code );
}

/**
 * Safely decodes WCA other_fields.
 *
 * @param mixed $other_fields WCA other_fields value.
 * @return array
 */
function peachpay_wcar_decode_other_fields( $other_fields ) {
	$decoded = maybe_unserialize( $other_fields );
	return is_array( $decoded ) ? $decoded : array();
}

/**
 * Gets WCA checkout details for a session.
 *
 * @param string $session_id WCA session id.
 * @return object|null
 */
function peachpay_wcar_get_checkout_details( $session_id ) {
	if ( function_exists( 'wcf_ca' ) ) {
		$wcf_ca = wcf_ca();
		if ( $wcf_ca && isset( $wcf_ca->helper ) && method_exists( $wcf_ca->helper, 'get_checkout_details' ) ) {
			return $wcf_ca->helper->get_checkout_details( $session_id );
		}
	}

	if ( class_exists( 'Cartflows_Ca_Helper' ) && method_exists( 'Cartflows_Ca_Helper', 'get_instance' ) ) {
		$helper = Cartflows_Ca_Helper::get_instance();
		if ( $helper && method_exists( $helper, 'get_checkout_details' ) ) {
			return $helper->get_checkout_details( $session_id );
		}
	}

	return null;
}

/**
 * Checks whether a cart already has a coupon code applied.
 *
 * @param object $cart Cart object.
 * @param string $coupon_code Coupon code.
 * @return bool
 */
function peachpay_wcar_cart_has_coupon( $cart, $coupon_code ) {
	if ( ! $cart || ! method_exists( $cart, 'get_applied_coupons' ) ) {
		return false;
	}

	foreach ( (array) $cart->get_applied_coupons() as $applied_coupon ) {
		if ( 0 === strcasecmp( (string) $applied_coupon, (string) $coupon_code ) ) {
			return true;
		}
	}

	return false;
}

/**
 * Gets original snapshot coupons currently applied to the cart.
 *
 * @param object $cart Cart object.
 * @param array  $coupon_codes Snapshot coupon codes.
 * @return array
 */
function peachpay_wcar_current_cart_snapshot_coupons( $cart, $coupon_codes ) {
	$applied = array();
	foreach ( $coupon_codes as $coupon_code ) {
		if ( peachpay_wcar_cart_has_coupon( $cart, $coupon_code ) ) {
			$applied[] = $coupon_code;
		}
	}
	return $applied;
}

/**
 * Checks whether all expected coupons are still applied to the cart.
 *
 * @param object $cart Cart object.
 * @param array  $coupon_codes Coupon codes expected on the cart.
 * @return bool
 */
function peachpay_wcar_cart_contains_all_coupons( $cart, $coupon_codes ) {
	foreach ( $coupon_codes as $coupon_code ) {
		if ( ! peachpay_wcar_cart_has_coupon( $cart, $coupon_code ) ) {
			return false;
		}
	}
	return true;
}

/**
 * Re-applies missing original coupons after a failed incentive-combination attempt.
 *
 * @param object $cart Cart object.
 * @param array  $coupon_codes Coupon codes to preserve.
 * @param string $session_id WCA session id.
 */
function peachpay_wcar_reapply_missing_original_coupons( $cart, $coupon_codes, $session_id ) {
	foreach ( $coupon_codes as $coupon_code ) {
		if ( peachpay_wcar_cart_has_coupon( $cart, $coupon_code ) ) {
			continue;
		}
		if ( ! peachpay_wcar_apply_coupon_without_notices( $cart, $coupon_code ) ) {
			peachpay_wcar_log_coupon_skip( $session_id, $coupon_code, 'original_coupon_reapply_failed_after_incentive_removal' );
		}
	}
}

/**
 * Applies a coupon without leaking programmatic restore notices into checkout.
 *
 * WooCommerce adds customer-facing error notices when a coupon is invalid. Those
 * notices are correct for direct shopper input, but during WCA restore they can
 * become stale Store API cart errors and block Blocks checkout even though the
 * invalid coupon was not applied.
 *
 * @param object $cart Cart object.
 * @param string $coupon_code Coupon code.
 * @return bool
 */
function peachpay_wcar_apply_coupon_without_notices( $cart, $coupon_code ) {
	if ( ! method_exists( $cart, 'apply_coupon' ) ) {
		return false;
	}

	$session = peachpay_wcar_get_wc_session();
	if ( ! $session || ! method_exists( $session, 'get' ) || ! method_exists( $session, 'set' ) ) {
		return $cart->apply_coupon( $coupon_code );
	}

	$previous_notices = $session->get( 'wc_notices' );
	$result           = $cart->apply_coupon( $coupon_code );
	$session->set( 'wc_notices', $previous_notices );

	return $result;
}

/**
 * Gets the active WooCommerce session object when available.
 *
 * @return object|null
 */
function peachpay_wcar_get_wc_session() {
	if ( ! function_exists( 'WC' ) ) {
		return null;
	}

	$wc = WC();
	return ( $wc && isset( $wc->session ) ) ? $wc->session : null;
}

/**
 * Calculates cart totals when available.
 *
 * @param object $cart Cart object.
 */
function peachpay_wcar_calculate_totals( $cart ) {
	if ( $cart && method_exists( $cart, 'calculate_totals' ) ) {
		$cart->calculate_totals();
	}
}

/**
 * Logs skipped coupons without adding customer-facing notices.
 *
 * @param string $session_id WCA session id.
 * @param string $coupon_code Coupon code.
 * @param string $reason Skip reason.
 */
function peachpay_wcar_log_coupon_skip( $session_id, $coupon_code, $reason ) {
	if ( ! function_exists( 'peachpay_log' ) ) {
		return;
	}

	peachpay_log(
		'warning',
		'Compatibility: Woo Cart Abandonment Recovery coupon restore skipped coupon.',
		array(
			'event'     => 'compatibility.wcar.coupon_restore.skipped',
			'component' => 'compatibility',
			'feature'   => 'woo_cart_abandonment_recovery',
			'operation' => 'restore_coupon',
			'outcome'   => 'skipped',
			'data'      => array(
				'session_id'   => $session_id,
				'coupon_code'  => $coupon_code,
				'skip_reason'  => $reason,
			),
		)
	);
}
