<?php
/**
 * Paid Listings module.
 *
 * @since   1.6
 * @license GNU General Public License v3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
 * @copyright:
 *     2019 27collective
 *     2017 Astoundify
 *     2015 Automattic
 */

namespace MyListing\Src\Paid_Listings;

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

class Paid_Listings {
	use \MyListing\Src\Traits\Instantiatable;

	public function __construct() {
		if ( ! class_exists( '\\WooCommerce' ) ) {
			return;
		}

		// Migrate WPJM WC Paid Listing DB.
		WCPL_Importer::instance();

		// Load User Packages.
		User_Packages::instance();

		// claims are enabled
		if ( mylisting_get_setting( 'claims_enabled' ) ) {
			\MyListing\Src\Claims\Claims::instance();
			Controllers\Claim_Listing::instance();
		}

		// paid listings enabled
		if ( mylisting_get_setting( 'paid_listings_enabled' ) ) {

			// add listing form handler
			Controllers\Add_Listing::instance();

			// switch package
			if ( apply_filters( 'mylisting/paid-listing/enable-switch-package', true ) ) {
				Controllers\Switch_Package::instance();
			}
		} else {
			if ( apply_filters( 'mylisting/paid-listing/enable-switch-package', true ) ) {
				Controllers\Relist_Package::instance();
			}
		}

		// add "listing package" custom woocommerce product type
		add_filter( 'product_type_selector', [ $this, 'add_product_type' ] );
		add_filter( 'woocommerce_product_class' , [ $this, 'set_product_class' ], 10, 3 );
		add_action( 'woocommerce_product_options_general_product_data', [ $this, 'add_product_settings' ] );
		add_filter( 'woocommerce_process_product_meta_job_package', [ $this, 'save_product_settings' ] );
		add_filter( 'woocommerce_process_product_meta_job_package_subscription', [ $this, 'save_product_settings' ] );

		// handle cart processing
		add_action( 'woocommerce_job_package_add_to_cart', 'woocommerce_simple_add_to_cart', 30 );
		add_filter( 'woocommerce_get_cart_item_from_session', [ $this, 'get_cart_item_from_session' ], 10, 2 );
		add_action( 'woocommerce_checkout_create_order_line_item', [ $this, 'checkout_create_order_line_item' ], 10, 4 );

		// display listing in cart
		add_filter( 'woocommerce_get_item_data', [ $this, 'display_listing_in_cart' ], 10, 2 );

		// display listing under product on order details (and emails)
		add_filter( 'woocommerce_order_item_get_formatted_meta_data', [ $this, 'display_listing_in_order' ], 10, 2 );

		// disable guest checkout when purchasing listing and enable checkout signup
		add_filter( 'option_woocommerce_enable_signup_and_login_from_checkout', [ $this, 'enable_signup_and_login_from_checkout' ] );
		add_filter( 'option_woocommerce_enable_guest_checkout', [ $this, 'enable_guest_checkout' ] );

		// order placed - thank you page
		add_action( 'woocommerce_thankyou', [ $this, 'woocommerce_thankyou' ], 5 );

		// process order
		add_action( 'woocommerce_order_status_processing', [ $this, 'order_paid' ] );
		add_action( 'woocommerce_order_status_completed', [ $this, 'order_paid' ] );
		add_action( 'woocommerce_order_status_cancelled', [ $this, 'order_cancelled' ] );

		// disable repeat product purchase, if enabled for the product
		add_filter( 'mylisting/paid-listings/product/is-purchasable', [ $this, 'disable_repeat_purchase' ], 10, 2 );
		add_filter( 'woocommerce_is_purchasable', [ $this, 'disable_repeat_purchase' ], 10, 2 );
		add_action( 'woocommerce_single_product_summary', [ $this, 'purchase_disabled_message' ], 31 );

		// hide product meta fields
		add_filter( 'woocommerce_order_item_display_meta_key', [ $this, 'display_meta_key' ] );
		add_filter( 'woocommerce_order_item_display_meta_value', [ $this, 'display_meta_value' ], 10, 2 );

		// Native replacement for ACF "Product Settings" (pricing plan image) on Product edit screen.
		add_action( 'add_meta_boxes', [ $this, 'add_product_settings_metabox' ] );
		add_action( 'save_post', [ $this, 'save_product_settings_metabox' ], 10, 2 );

		// Register post status.
		add_action( 'init', [ $this, 'register_post_status' ], 7 );
		add_filter( 'mylisting/valid-submission-statuses', [ $this, 'add_valid_listing_status' ] );

		// Implement field visibility by package.
		add_filter( 'mylisting/submission/fields', [ $this, 'listing_fields_visibility' ], 30, 2 );
		add_filter( 'mylisting/admin/submission/fields', [ $this, 'listing_fields_visibility' ], 30, 2 );

		// WC Subscriptions.
		Subscriptions::instance();
	}

	/**
	 * Add "Listing Package" product type.
	 *
	 * @since 1.6
	 */
	public function add_product_type( $types ) {
		$types['job_package'] = esc_html__( 'Listing Package', 'my-listing' );
		return $types;
	}

	/**
	 * Set custom product class for listing package products.
	 *
	 * @since 1.6
	 */
	public function set_product_class( $classname, $product_type ) {
		if ( $product_type === 'job_package' ) {
			return '\MyListing\Src\Paid_Listings\Product';
		}

		return $classname;
	}

	/**
	 * Product settings.
	 *
	 * @since 1.6
	 */
	public function add_product_settings() {
		require locate_template( 'templates/admin/single-product-screen/package-settings.php' );
	}

	/**
	 * Register a side metabox for Product Settings (Pricing Plan Image) on products.
	 */
	public function add_product_settings_metabox() {
		add_meta_box(
			'ml-product-settings',
			_x( 'Product Settings', 'product settings metabox title', 'my-listing' ),
			[ $this, 'render_product_settings_metabox' ],
			'product',
			'side',
			'default'
		);

		// Hide legacy ACF group (if ACF is active) to avoid duplicate UI.
		remove_meta_box( 'acf-group_5b08ab4415a45', 'product', 'side' );
	}

	/**
	 * Render the Pricing Plan Image control using the WP media modal.
	 */
	public function render_product_settings_metabox( $post ) {
		if ( ! ( $post && $post->post_type === 'product' ) ) {
			return;
		}

		// Ensure media modal is available.
		if ( function_exists( 'wp_enqueue_media' ) ) { wp_enqueue_media(); }

		// Nonce for saving.
		wp_nonce_field( 'ml_product_settings_save', 'ml_product_settings_nonce' );

		$attachment_id = absint( get_post_meta( $post->ID, 'pricing_plan_image', true ) );
		$img_html = $attachment_id ? wp_get_attachment_image( $attachment_id, 'thumbnail', false ) : '';

		echo '<p style="margin-bottom:12px">'
			. '<label class="ml-backend-label" for="ml_pricing_plan_image">' . esc_html__( 'Pricing Plan Image', 'my-listing' ) . '</label>'
			. '<input type="hidden" id="ml_pricing_plan_image" name="ml_pricing_plan_image" value="' . esc_attr( $attachment_id ) . '" />'
			. '<div class="ml-image-preview" data-target="ml_pricing_plan_image">' . ( $img_html ?: '<em style="color:#666">' . esc_html__( 'No image selected', 'my-listing' ) . '</em>' ) . '</div>'
			. '<button type="button" class="button select-ml-image" data-target="ml_pricing_plan_image">' . esc_html__( 'Select Image', 'my-listing' ) . '</button> '
			. '<button type="button" class="button link-button clear-ml-image" data-target="ml_pricing_plan_image">' . esc_html__( 'Clear', 'my-listing' ) . '</button>'
			. '</p>';

		echo '<p class="description">' . esc_html__( 'This image is displayed in the pricing plans step on Add Listing page.', 'my-listing' ) . '</p>';

		?>
		<script type="text/javascript">
		jQuery(function($){
			function mlSelectImage(target){
				var frame = wp.media({ title: '<?php echo esc_js( _x( 'Select Image', 'media modal', 'my-listing' ) ); ?>', multiple: false });
				frame.on('select', function(){
					var attachment = frame.state().get('selection').first().toJSON();
					$('#'+target).val( attachment.id );
					var html = attachment.sizes && attachment.sizes.thumbnail ? '<img src="'+attachment.sizes.thumbnail.url+'" />' : '<img src="'+attachment.url+'" />';
					$('.ml-image-preview[data-target="'+target+'"]').html( html );
				});
				frame.open();
			}
			$('.select-ml-image').on('click', function(e){ e.preventDefault(); mlSelectImage($(this).data('target')); });
			$('.clear-ml-image').on('click', function(e){ e.preventDefault(); var t=$(this).data('target'); $('#'+t).val(''); $('.ml-image-preview[data-target="'+t+'"]').html('<em style="color:#666"><?php echo esc_js( __( 'No image selected', 'my-listing' ) ); ?></em>'); });
		});
		</script>
		<?php
	}

	/**
	 * Save handler for the Product Settings metabox.
	 */
	public function save_product_settings_metabox( $post_id, $post ) {
		if ( ! ( $post && $post->post_type === 'product' ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reading nonce from $_POST to verify it.
		if ( empty( $_POST['ml_product_settings_nonce'] ) || ! wp_verify_nonce( $_POST['ml_product_settings_nonce'], 'ml_product_settings_save' ) ) {
			return;
		}
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
			return;
		}
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return;
		}

		// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified above; safe to read $_POST values below.
		$attachment_id = isset( $_POST['ml_pricing_plan_image'] ) ? absint( $_POST['ml_pricing_plan_image'] ) : 0;
		// phpcs:enable WordPress.Security.NonceVerification.Missing

		if ( $attachment_id && get_post_type( $attachment_id ) !== 'attachment' ) {
			$attachment_id = 0;
		}

		if ( $attachment_id ) {
			update_post_meta( $post_id, 'pricing_plan_image', $attachment_id );
		} else {
			delete_post_meta( $post_id, 'pricing_plan_image' );
		}
	}



	/**
	 * Save Product Data
	 *
	 * @since 1.6
	 * @param int $post_id Product ID.
	 */
	public function save_product_settings( $post_id ) {
		$types = \MyListing\get_listing_types();
		$max_limit = absint( $_POST['_job_listing_limit'] ?? 0 );
		$overall_count = 0;

		foreach ( $types as $type ) {
			$slug = sprintf( '%s_limit', $type->get_slug() );
			if ( ! isset( $_POST[ $slug ] ) || empty( $_POST[ $slug ] ) ) {
				continue;
			}

			$limit = absint( $_POST[ $slug ] );
			$overall_count += $limit;
		}
		
		$listing_type_limit = false;
		if ( $overall_count <= $max_limit ) {
			
			foreach ( $types as $type ) {
				$slug = sprintf( '%s_limit', $type->get_slug() );
				$limit = absint( $_POST[ $slug ] ?? 0 );
				if ( $limit > 0 ) {
					update_post_meta( $post_id, $slug, $limit );
					$listing_type_limit = true;
				} else {
					delete_post_meta( $post_id, $slug );
				}
			}
		}

		update_post_meta( $post_id, '_listing_type_limit_enable', $listing_type_limit );
		
		// listing limit setting
		if ( ! empty( $_POST['_job_listing_limit'] ) ) {
			update_post_meta( $post_id, '_job_listing_limit', absint( $_POST['_job_listing_limit'] ) );
		} else {
			delete_post_meta( $post_id, '_job_listing_limit' );
		}

		// listing duration
		if ( ! empty( $_POST['_job_listing_duration'] ) ) {
			update_post_meta( $post_id, '_job_listing_duration', absint( $_POST['_job_listing_duration'] ) );
		} else {
			delete_post_meta( $post_id, '_job_listing_duration' );
		}

		// featured status
		update_post_meta( $post_id, '_job_listing_featured', ! empty( $_POST['_job_listing_featured'] ) ? 'yes' : 'no' );

		// mark verified setting
		update_post_meta( $post_id, '_listing_mark_verified', ! empty( $_POST['_listing_mark_verified'] ) ? 'yes' : 'no' );

		// use on claims
		update_post_meta( $post_id, '_use_for_claims', ! empty( $_POST['_use_for_claims'] ) ? 'yes' : 'no' );
		
		update_post_meta( $post_id, '_is_claimable', ! empty( $_POST['_is_claimable'] ) ? 'yes' : 'no' );

		// hide in add listing
		update_post_meta( $post_id, '_hide_in_add_listing', ! empty( $_POST['_hide_in_add_listing'] ) ? 'yes' : 'no' );

		// disable repeat purchase
		update_post_meta( $post_id, '_disable_repeat_purchase', ! empty( $_POST['_disable_repeat_purchase'] ) ? 'yes' : 'no' );

		// Subscription type.
		if ( isset( $_POST['_job_listing_package_subscription_type'] ) ) {
			$type = 'package' === $_POST['_job_listing_package_subscription_type'] ? 'package' : 'listing';
			update_post_meta( $post_id, '_package_subscription_type', $type );
		}
	}

	/**
	 * Get the data from the session on page load
	 *
	 * @since 1.6
	 */
	public function get_cart_item_from_session( $cart_item, $values ) {
		if ( ! empty( $values['job_id'] ) ) {
			$cart_item['job_id'] = $values['job_id'];
			$cart_item['assignment_type'] = ! empty( $values['assignment_type'] ) ? $values['assignment_type'] : 'submission';
		}

		return $cart_item;
	}

	/**
	 * Set the order line item's meta data prior to being saved.
	 *
	 * @since 1.6
	 *
	 * @param WC_Order_Item_Product $order_item
	 * @param string                $cart_item_key  The hash used to identify the item in the cart
	 * @param array                 $cart_item_data The cart item's data.
	 * @param WC_Order              $order          The order or subscription object to which the line item relates
	 */
	public function checkout_create_order_line_item( $order_item, $cart_item_key, $cart_item_data, $order ) {
		if ( isset( $cart_item_data['job_id'] ) ) {
			$order_item->update_meta_data( '_job_id', $cart_item_data['job_id'] );
			if ( ! empty( $cart_item_data['assignment_type'] ) ) {
				$order_item->update_meta_data( '_assignment_type', $cart_item_data['assignment_type'] );
			}
		}
	}

	/**
	 * Output listing name in cart
	 *
	 * @since 1.6
	 */
	public function display_listing_in_cart( $data, $cart_item ) {
		if ( isset( $cart_item['job_id'] ) ) {
			$name = esc_html__( 'Listing', 'my-listing' );
			if ( ! empty( $cart_item['assignment_type'] ) ) {
				switch ( $cart_item['assignment_type'] ) {
					case 'claim':
						$name = esc_html__( 'Claim for', 'my-listing' );
						break;
					case 'switch':
						$name = esc_html__( 'Switch package for', 'my-listing' );
						break;
					case 'relist':
						$name = esc_html__( 'Relist for', 'my-listing' );
						break;
					default:
						break;
				}
			}
			$data[] = [
				'name'  => $name,
				'value' => get_the_title( absint( $cart_item['job_id'] ) ),
			];
		}

		return $data;
	}

	/**
	 * Append the listing related to the order item under the product on
	 * the order details page (and emails). Uses saved _job_id and assignment_type.
	 *
	 * @since 2.13
	 */
	public function display_listing_in_order( $formatted_meta, $item ) {
		try {
			// Retrieve saved listing id (prefer hidden key, fallback to non-hidden if present).
			$listing_id = is_callable( [ $item, 'get_meta' ] ) ? absint( $item->get_meta( '_job_id', true ) ) : 0;
			if ( ! $listing_id && is_callable( [ $item, 'get_meta' ] ) ) {
				$listing_id = absint( $item->get_meta( 'job_id', true ) );
			}

			if ( ! $listing_id ) {
				return $formatted_meta;
			}

			// Determine label similar to cart output based on assignment type.
			$label = esc_html__( 'Listing', 'my-listing' );
			$assignment_type = is_callable( [ $item, 'get_meta' ] ) ? (string) $item->get_meta( '_assignment_type', true ) : '';
			if ( ! $assignment_type ) {
				$assignment_type = is_callable( [ $item, 'get_meta' ] ) ? (string) $item->get_meta( 'assignment_type', true ) : '';
			}
			if ( $assignment_type === 'claim' ) {
				$label = esc_html__( 'Claim for', 'my-listing' );
			} elseif ( $assignment_type === 'switch' ) {
				$label = esc_html__( 'Switch package for', 'my-listing' );
			} elseif ( $assignment_type === 'relist' ) {
				$label = esc_html__( 'Relist for', 'my-listing' );
			}

			// Avoid duplicates if a row with same display_key already exists.
			foreach ( (array) $formatted_meta as $meta ) {
				if ( isset( $meta->display_key ) && $meta->display_key === $label ) {
					return $formatted_meta;
				}
			}

			$title = get_the_title( $listing_id );
			$link  = get_permalink( $listing_id );
			$display_value = $title ? sprintf( '<a href="%s" target="_blank" rel="nofollow noopener">%s</a>', esc_url( $link ), esc_html( $title ) ) : '';

			$formatted_meta[] = (object) [
				'key'           => 'listing',
				'value'         => $title ?: ( $listing_id ? (string) $listing_id : '' ),
				'display_key'   => $label,
				'display_value' => $display_value ?: esc_html( $title ?: sprintf( __( 'Listing #%d', 'my-listing' ), $listing_id ) ),
				'hidden'        => false,
			];
		} catch ( \Throwable $e ) {
			// Fail silently to avoid breaking order rendering.
		}

		return $formatted_meta;
	}

	/**
	 * When cart contains a listing package, always set to "yes".
	 *
	 * @since 1.6
	 */
	public function enable_signup_and_login_from_checkout( $value ) {
		global $woocommerce;
		$contain_listing = false;
		if ( ! empty( $woocommerce->cart->cart_contents ) ) {
			foreach ( $woocommerce->cart->cart_contents as $cart_item ) {
				$product = $cart_item['data'];
				if ( $product instanceof \WC_Product && $product->is_type( [ 'job_package', 'job_package_subscription' ] ) ) {
					$contain_listing = true;
				}
			}
		}

		return $contain_listing ? 'yes' : $value;
	}

	/**
	 * When cart contains a listing package, always set to "no".
	 *
	 * @since 1.6
	 */
	public function enable_guest_checkout( $value ) {
		global $woocommerce;
		$contain_listing = false;
		if ( ! empty( $woocommerce->cart->cart_contents ) ) {
			foreach ( $woocommerce->cart->cart_contents as $cart_item ) {
				$product = $cart_item['data'];
				if ( $product instanceof \WC_Product && $product->is_type( [ 'job_package', 'job_package_subscription' ] ) ) {
					$contain_listing = true;
				}
			}
		}

		return $contain_listing ? 'no' : $value;
	}

	/**
	 * Thank you page after checkout completed.
	 *
	 * @since 1.6
	 */
	public function woocommerce_thankyou( $order_id ) {
		global $wp_post_types;
		$order = wc_get_order( $order_id );
		$is_paid = in_array( $order->get_status(), [ 'completed', 'processing' ] );

		foreach ( $order->get_items() as $item ) {
			if ( ! isset( $item['job_id'] ) ) {
				continue;
			}

			$listing_status = get_post_status( $item['job_id'] );
			$is_claim = ! empty( $item['assignment_type'] ) && $item['assignment_type'] === 'claim';
			$is_switch = ! empty( $item['assignment_type'] ) && $item['assignment_type'] === 'switch';
			$is_relist = ! empty( $item['assignment_type'] ) && $item['assignment_type'] === 'relist';

			if ( $is_switch ) {
				switch( get_post_status( $item['job_id'] ) ):
					case 'pending':
						echo wpautop( sprintf( __( 'The plan for %s has been updated. Your listing will be visible once approved.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
					break;
					case 'pending_payment':
						echo wpautop( sprintf( __( 'The plan switch for %s has been submitted successfully. The listing will be updated once the payment is confirmed.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
					break;
					default :
						echo wpautop( sprintf( __( 'The plan for %s has been updated.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
					break;
				endswitch;
			} elseif ( $is_claim ) {
				if ( $is_paid ) {
					echo wpautop( sprintf( __( 'Your claim to %s has been submitted successfully.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
				} else {
					echo wpautop( sprintf( __( 'Your claim to %s will be processed after the order is completed.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
				}
			} elseif ( $is_relist ) {
				switch ( get_post_status( $item['job_id'] ) ) {
					case 'pending' :
						echo wpautop( sprintf( __( '%s has been relisted successfully and will be visible once approved.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
					break;
					case 'pending_payment' :
						echo wpautop( sprintf( __( '%s has been submitted for relisting and will be visible once payment has been confirmed.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
					break;
					default :
						echo wpautop( sprintf( __( '%s has been relisted successfully.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
					break;
				}
			} else {
				switch ( get_post_status( $item['job_id'] ) ) {
					case 'pending' :
						echo wpautop( sprintf( __( '%s has been submitted successfully and will be visible once approved.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
					break;
					case 'pending_payment' :
					case 'expired' :
						echo wpautop( sprintf( __( '%s has been submitted successfully and will be visible once payment has been confirmed.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
					break;
					default :
						echo wpautop( sprintf( __( '%s has been submitted successfully.', 'my-listing' ), get_the_title( $item['job_id'] ) ) );
					break;
				}

				/**
				 * Make sure the `mylisting/submission/order-placed` hook is only
				 * run once after the order is done.
				 */
				$order_placed_handler_key = 'wc_paid_listings_order_placed_handler_'.absint( $item['job_id'] );
				if ( ! get_post_meta( $order_id, $order_placed_handler_key, true ) && ! get_post_meta( $order_id, '_subscription_renewal', true ) ) {
					do_action( 'mylisting/submission/order-placed', $item['job_id'] );
					update_post_meta( $order_id, $order_placed_handler_key, true );
				}
			}
			?>
			<p class="job-manager-submitted-paid-listing-actions">
				<?php
				if ( get_post_status( $item['job_id'] ) === 'publish' ) {
					echo '<a class="button" href="' . get_permalink( $item['job_id'] ) . '">' . __( 'View Listing', 'my-listing' ) . '</a> ';
				} else {
					echo '<a class="button" href="' . esc_url( wc_get_account_endpoint_url( \MyListing\my_listings_endpoint_slug() ) ) . '">' . __( 'Go to Dashboard', 'my-listing' ) . '</a> ';
				}
				?>
			</p>
			<?php
		}
	}

	/**
	 * Triggered when an order is paid
	 *
	 * @since 1.6
	 */
	public function order_paid( $order_id ) {
		$order = wc_get_order( $order_id );

		// Bail if already processed. Using WCPL prefix for back-compat.
		if ( get_post_meta( $order_id, 'wc_paid_listings_packages_processed', true ) ) {
			return;
		}

		foreach ( $order->get_items() as $item ) {
			$product = wc_get_product( $item['product_id'] );
			if ( ! ( $product->is_type( [ 'job_package' ] ) && $order->get_customer_id() ) ) {
				continue;
			}

			// Give packages to user
			for ( $i = 0; $i < $item['qty']; $i++ ) {
				$package = \MyListing\Src\Package::create( [
					'user_id'        => $order->get_customer_id(),
					'order_id'       => $order_id,
					'product_id'     => $product->get_id(),
					'duration'       => $product->get_duration(),
					'limit'          => $product->get_limit(),
					'featured'       => $product->is_listing_featured(),
					'mark_verified'  => $product->mark_verified(),
					'use_for_claims' => $product->use_for_claims(),
					'is_claimable' => $product->is_claimable(),
					'hide_in_add_listing' => $product->hide_in_add_listing(),
				] );

				// user package created & make sure listing id is set
				if ( ! ( $package && isset( $item['job_id'] ) ) ) {
					continue;
				}

				// validate listing
				$listing = \MyListing\Src\Listing::get( $item['job_id'] );
				if ( ! $listing ) {
					continue;
				}

				$assignment_type = ! empty( $item['assignment_type'] ) ? $item['assignment_type'] : 'submission';
				if ( ! empty( $item['is_claim'] ) ) {
					$assignment_type = 'claim'; // backwards compatibility pre v2.1.3
				}

				/**
				 * Order has been processed, payment package has been created, and a valid
				 * listing has been passed to the order. This can be used by Add Listing and
				 * other forms to modify the listing information, assign the package, etc.
				 *
				 * @since 2.1.6
				 */
				do_action( sprintf( 'mylisting/payments/%s/order-processed', $assignment_type ), $listing, $package );
			}
		}

		// mark order as processed
		update_post_meta( $order_id, 'wc_paid_listings_packages_processed', true );
	}

	/**
	 * Fires when a order was canceled. Looks for listing
	 * packages in order and deletes the package if found.
	 *
	 * @since 1.6
	 */
	public function order_cancelled( $order_id ) {
		$packages = get_posts( [
			'post_type'        => 'case27_user_package',
			'post_status'      => 'any',
			'posts_per_page'   => -1,
			'suppress_filters' => false,
			'fields'           => 'ids',
			'meta_query' => [
				'relation' => 'AND',
				[
					'key'     => '_order_id',
					'value'   => $order_id,
					'compare' => 'IN',
				],
			],
		] );

		if ( $packages && is_array( $packages ) ) {
			foreach ( $packages as $package_id ) {
				wp_update_post( [
					'ID'          => $package_id,
					'post_status' => 'case27_cancelled',
				] );
			}
		}
	}

	/**
	 * Disables repeat purchase for packages.
	 * Useful for allowing users to only have one free package plan.
	 *
	 * @since  1.6.3
	 *
	 * @param  bool        $purchasable
	 * @param  \WC_Product $product
	 * @return bool        $purchasable
	 */
	public function disable_repeat_purchase( $purchasable, $product ) {
	    if ( ! $product->is_type( [ 'job_package', 'job_package_subscription' ] ) ) {
	        return $purchasable;
	    }

	    if ( $product->get_meta( '_disable_repeat_purchase' ) !== 'yes' ) {

	    	if ( $product->get_type() === 'job_package_subscription' ) {
	    		$user_id = get_current_user_id();

	    		if ( 0 !== $user_id && ! in_array( wcs_get_product_limitation( $product ), [ 'no', 'any' ] ) && wcs_is_product_limited_for_user( $product, $user_id ) ) {
	    			return false;
	    		}
	    	}

	        return $purchasable;
	    }

	    if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $product->get_id() ) ) {
	        $purchasable = false;
	    }

	    return $purchasable;
	}

	/**
	 * Shows a "purchase disabled" message to the customer.
	 *
	 * @since 1.6.3
	 */
	public function purchase_disabled_message() {
	    global $product;

	    if ( ! $product->is_type( [ 'job_package', 'job_package_subscription' ] ) ) {
	        return false;
	    }

	    if ( $product->get_meta( '_disable_repeat_purchase' ) !== 'yes' ) {
	        return false;
	    }

	    if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $product->get_id() ) ) {
	    	printf(
	    		'<div class="woocommerce"><div class="woocommerce-info wc-nonpurchasable-message">%s</div></div>',
	    		__( 'You\'ve already purchased this product! It can only be purchased once.', 'my-listing' )
	    	);
	    }
	}

	/**
	 * Display a nicename instead of the meta key in advanced order settings.
	 *
	 * @since 2.1
	 */
	public function display_meta_key( $display_key ) {
		if ( $display_key === '_job_id' ) {
			return _x( 'Listing ID', 'Product meta', 'my-listing' );
		}

		if ( $display_key === '_is_claim' || $display_key === '_assignment_type' ) {
			return _x( 'Type', 'Product meta', 'my-listing' );
		}

		return $display_key;
	}

	/**
	 * Display a formatted value instead of the default meta value in advanced order settings.
	 *
	 * @since 2.1
	 */
	public function display_meta_value( $value, $meta ) {
		if ( $meta->key === '_is_claim' ) {
			return (bool) $value ? _x( 'Claim', 'Product meta', 'my-listing' ) : _x( 'Submission', 'Product meta', 'my-listing' );
		}

		if ( $meta->key === '_assignment_type' ) {
			if ( $value === 'claim' ) {
				return _x( 'Claim', 'Product meta', 'my-listing' );
			} elseif ( $value === 'switch' ) {
				return _x( 'Switch Plan', 'Product meta', 'my-listing' );
			} else {
				return _x( 'Submission', 'Product meta', 'my-listing' );
			}
		}

		return $value;
	}

	/**
	 * Field visibility conditions handler.
	 *
	 * @since 1.0
	 */
	public function listing_fields_visibility( $fields, $listing ) {
		return array_filter( $fields, function( $field ) use ( $listing ) {
			$conditions = new \MyListing\Src\Conditions( $field, $listing );
			return $conditions->passes();
		} );
	}

	/**
	 * Register Listing Status.
	 *
	 * @since 1.6
	 */
	public function register_post_status() {
		register_post_status( 'pending_payment', [
			'label'                     => esc_html__( 'Pending Payment', 'my-listing' ),
			'public'                    => false,
			'exclude_from_search'       => false,
			'show_in_admin_all_list'    => false,
			'show_in_admin_status_list' => true,
			// translators: %s is label count.
			'label_count'               => _n_noop( 'Pending Payment <span class="count">(%s)</span>', 'Pending Payment <span class="count">(%s)</span>', 'my-listing' ),
		] );
	}

	/**
	 * Set "Pending Payment" as Valid Status.
	 *
	 * @since 1.6
	 */
	public function add_valid_listing_status( $statuses ) {
		$statuses[] = 'pending_payment';
		$statuses[] = 'expired';
		$statuses[] = 'publish';
		return $statuses;
	}
}
