<?php

namespace Gigneo;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

use WP_Query;

/**
 * Class Services
 *
 * Plugin Services class for Gigneo.
 *
 * @since 0.0.1
 */
class Services {



	/**
	 * The single instance of the class.
	 *
	 * @var Services|null
	 * @since 0.0.1
	 */
	private static $instance = null;

	/**
	 * Constructor for the class.
	 *
	 * @since 0.0.1
	 */
	public function __construct() {

		// Save service.
		add_action( 'wp_ajax_gigneo_save_service', array( $this, 'save_service' ) );

		// Publish Service.
		add_action( 'wp_ajax_gigneo_publish_service', array( $this, 'publish_service' ) );

		// Save location.
		add_action( 'wp_ajax_gigneo_save_service_location', array( $this, 'save_location' ) );

		// Remove location.
		add_action( 'wp_ajax_gigneo_remove_service_location', array( $this, 'remove_location' ) );

		// Duplicate service.
		add_action( 'wp_ajax_gigneo_service_clone', array( $this, 'service_clone' ) );

		// Approve service.
		add_action( 'wp_ajax_gigneo_approve_service', array( $this, 'approve_service' ) );

		// Reject service.
		add_action( 'wp_ajax_gigneo_reject_service', array( $this, 'reject_service' ) );

		// Remove service.
		add_action( 'wp_ajax_gigneo_remove_service', array( $this, 'remove_service' ) );

		// Draft service.
		add_action( 'wp_ajax_gigneo_draft_service', array( $this, 'draft_service' ) );

		// Featured service.
		add_action( 'wp_ajax_gigneo_featured_service', array( $this, 'featured_service' ) );

		// Update service status.
		add_action( 'wp_ajax_gigneo_pause_active_service', array( $this, 'pause_active_service' ) );

		// Service Approve Action.
		add_action( 'gigneo_post_type_actions', array( $this, 'post_type_actions' ), 10, 3 );

		// Menu Counter.
		add_action( 'admin_menu', array( $this, 'menu_counter' ) );

		// Service Reject Markup.
		add_action( 'admin_footer', array( $this, 'reject_service_markup' ) );
	}

	/**
	 * Ensures only one instance of the class is loaded or can be loaded.
	 *
	 * @return Services
	 * @since 0.0.1
	 */
	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Render admin menu counter.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function menu_counter() {

		global $submenu;
		$counts  = wp_count_posts( 'gigneo_service' );
		$pending = isset( $counts->{'pending'} ) ? (int) $counts->{'pending'} : 0;

		if ( $pending <= 0 || ! isset( $submenu['gigneo'] ) ) {
			return;
		}

		foreach ( $submenu['gigneo'] as $key => $item ) {
			if ( isset( $item[2] ) && 'edit.php?post_type=gigneo_service' === $item[2] ) {
				// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
				$submenu['gigneo'][ $key ][0] .= sprintf(
					' <span class="update-plugins count-%1$d"><span class="plugin-count">%1$d</span></span>',
					esc_html( $pending )
				);
				break;
			}
		}
	}

	/**
	 * Save service form step data.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function save_service() {

		// Security / Nonce verification.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		// Basic authorization checks.
		$profile_id   = gigneo_active_profile_id();
		$current_user = wp_get_current_user();

		if ( empty( $current_user->ID ) || empty( $profile_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'gigneo' ) ), 403 );
		}

		// Get and sanitize request parameters.
		$step = isset( $_POST['step'] ) ? sanitize_text_field( wp_unslash( $_POST['step'] ) ) : '';
		$type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '';
		$id   = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0;

		// Basic validation.
		if ( empty( $type ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Invalid request type.', 'gigneo' ) ), 400 );
		}

		// Permission check when editing existing service.
		if ( $id > 0 && ! gigneo_validate_post_author( $id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to edit this service.', 'gigneo' ) ), 403 );
		}

		// New service → apply creation limits & conditions.
		if ( empty( $id ) ) {
			$this->validate_service_creation_limit( $current_user->ID );
			$this->validate_service_creation_conditions( $profile_id, $current_user->ID );
		}

		$steps_filled = gigneo_post_meta( $id, '_gigneo_pm_steps_filled' );
		$steps_filled = ! empty( $steps_filled ) && is_array( $steps_filled ) ? $steps_filled : array();

		$response = array();

		// Steps.
		if ( 'basic' === $step ) {
			$response = $this->edit_basic_step( $id, $steps_filled );
		} elseif ( 'pricing' === $step ) {
			$response = $this->edit_pricing_step( $id, $steps_filled );
		} elseif ( 'media' === $step ) {
			$response = $this->edit_media_step( $id, $steps_filled );
		} elseif ( 'details' === $step ) {
			$response = $this->edit_details_step( $id, $steps_filled );
		} elseif ( 'requirements' === $step ) {
			do_action( 'gigneo_ajax_after_service_save', $_POST, $id );
		}

		// Save as draft.
		if ( 'draft' === $type ) {

			if ( 'pending' === get_post_status( $id ) ) {
				wp_send_json_error( array( 'message' => esc_html__( 'You cannot save the service as a draft while it is pending review.', 'gigneo' ) ), 403 );
			}

			wp_update_post(
				array(
					'ID'          => $id,
					'post_status' => 'draft',
				)
			);
		}

		wp_send_json_success( $response, 200 );
	}

	/**
	 * Publish a service post.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function publish_service() {

		// Security / Nonce verification.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		// Basic authorization checks.
		$profile_id   = gigneo_active_profile_id();
		$current_user = wp_get_current_user();

		if ( empty( $current_user->ID ) || empty( $profile_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'gigneo' ) ), 403 );
		}

		$service_id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;

		if ( empty( $service_id ) || ! gigneo_validate_post_author( $service_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to publish this service.', 'gigneo' ) ), 403 );
		}

		$p_status  = get_post_status( $service_id );
		$pm_status = gigneo_post_meta( $service_id, '_gigneo_pm_status' );

		if ( 'draft' === $p_status && 'publish' === $pm_status ) {
			wp_update_post(
				array(
					'ID'          => $service_id,
					'post_status' => 'publish',
				)
			);
			wp_send_json_success( array( 'message' => esc_html__( 'Service published successfully,', 'gigneo' ) ), 200 );
		}

		$steps_filled = gigneo_post_meta( $service_id, '_gigneo_pm_steps_filled' );
		$steps_filled = ! empty( $steps_filled ) && is_array( $steps_filled ) ? $steps_filled : array();

		$required_steps = array( 'basic', 'pricing', 'details', 'media' );

		foreach ( $required_steps as $step ) {
			if ( ! in_array( $step, $steps_filled, true ) ) {
				wp_send_json_error( array( 'message' => esc_html__( 'Please complete all required steps before publishing this service.', 'gigneo' ) ), 403 );
			}
		}

		$creation_type     = gigneo_option( '_gigneo_ts_service_creation_type', 'paid' );
		$creation_credits  = gigneo_option( '_gigneo_ts_service_creation_credits', '10' );
		$manual_approval   = gigneo_option( '_gigneo_ts_service_manually_approve' );
		$available_credits = gigneo_available_credits( $profile_id );

		$mailer = WC()->mailer();
		$emails = $mailer->get_emails();

		$credit_invoice_id = gigneo_post_meta( $service_id, '_gigneo_pm_credit_invoice_id' );

		if ( 'paid' === $creation_type && empty( $credit_invoice_id ) ) {

			if ( $available_credits < $creation_credits ) {
				wp_send_json_error( array( 'message' => esc_html__( 'Insufficient credit balance.', 'gigneo' ) ), 403 );
			}

			$invoice_id = wp_insert_post(
				array(
					'post_type'   => 'gigneo_invoice',
					'post_status' => 'paid',
					'post_title'  => sprintf(
						// translators: %s is Service ID.
						esc_html__( 'Credits Spent — %s', 'gigneo' ),
						esc_html( gigneo_format_id( $service_id, 'service', false ) )
					),
					'post_author' => $current_user->ID,
				)
			);

			if ( is_wp_error( $invoice_id ) ) {
				wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 403 );
			}

			update_post_meta( $invoice_id, '_gigneo_pm_profile_id', $profile_id );
			update_post_meta( $invoice_id, '_gigneo_pm_credit', -intval( $creation_credits ) );
			update_post_meta( $invoice_id, '_gigneo_pm_type', 'service_creation' );
			update_post_meta( $service_id, '_gigneo_pm_credit_invoice_id', $invoice_id );
		}

		wp_update_post(
			array(
				'ID'          => $service_id,
				'post_status' => $manual_approval ? 'pending' : 'publish',
			)
		);

		update_post_meta( $service_id, '_gigneo_pm_status', $manual_approval ? 'pending' : 'publish' );

		if ( empty( $pm_status ) || 'rejected' === $pm_status ) {
			if ( $manual_approval ) {
				// Start Date&Time.
				$manual_approval_data = array(
					'status'        => 'pending',
					'submit_at'     => current_time( 'mysql' ),
					'submit_at_gmt' => current_time( 'mysql', 1 ),
				);
				update_post_meta( $service_id, '_gigneo_pm_manual_approval_info', $manual_approval_data );
			}
			if ( $manual_approval && isset( $emails['Gigneo_Service_Requested'] ) ) {
				$emails['Gigneo_Service_Requested']->trigger( $profile_id, $service_id );
			} elseif ( isset( $emails['Gigneo_Service_Published'] ) ) {
				$emails['Gigneo_Service_Published']->trigger( $profile_id, $service_id );
			}
		}

		$response['message'] = esc_html__( 'Service published successfully.', 'gigneo' );

		if ( $manual_approval ) {
			$response['message'] = esc_html__( 'Service has been successfully submitted for review.', 'gigneo' );
		}

		wp_send_json_success( $response, 200 );
	}

	/**
	 * Validate creation limit per user.
	 *
	 * @param int $user_id User ID.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	private function validate_service_creation_limit( $user_id ) {

		if ( empty( $user_id ) ) {
			return;
		}

		$limit = gigneo_option( '_gigneo_ts_service_creation_limit', 0 );

		if ( empty( $limit ) ) {
			return;
		}

		$query = new WP_Query(
			array(
				'post_type'      => 'gigneo_service',
				'author'         => $user_id,
				'post_status'    => 'any',
				'posts_per_page' => -1,
				'fields'         => 'ids',
			)
		);

		// Check if limit exceeded.
		if ( $query->post_count >= $limit ) {
			wp_send_json_error(
				array(
					'message' => esc_html__( 'You have reached the maximum number of services allowed.', 'gigneo' ),
				),
				403
			);
		}
	}

	/**
	 * Validate user conditions for service creation.
	 *
	 * @param int $profile_id Profile ID.
	 * @param int $user_id    User ID.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function validate_service_creation_conditions( $profile_id, $user_id ) {

		if ( empty( $profile_id ) || empty( $user_id ) ) {
			return;
		}

		$creation_conditions = gigneo_option( '_gigneo_ts_service_creation_conditions' );

		if ( empty( $creation_conditions ) && ! is_array( $creation_conditions ) ) {
			return;
		}

		// Profile Image.
		if ( in_array( 'profile_image', $creation_conditions, true ) ) {
			if ( empty( get_post_thumbnail_id( $profile_id ) ) ) {
				wp_send_json_error(
					array( 'message' => esc_html__( 'To continue, please upload a profile image first', 'gigneo' ) ),
					403
				);
			}
		}

		// Email Verification.
		if ( in_array( 'email_verified', $creation_conditions, true ) && in_array( 'email_verification', gigneo_active_modules(), true ) ) {
			$email_verified = get_user_meta( $user_id, '_gigneo_um_email_verified', true );

			if ( empty( $email_verified ) ) {
				wp_send_json_error(
					array( 'message' => esc_html__( 'Please verify your email address before creating a service', 'gigneo' ) ),
					403
				);
			}
		}

		// Identity Verification.
		do_action( 'gigneo_validate_service_creation_conditions', $creation_conditions, $profile_id, $user_id );
	}

	/**
	 * Handle basic step save.
	 *
	 * @param int   $post_id      Service ID.
	 * @param array $steps_filled Filled steps.
	 *
	 * @return array
	 * @since 0.0.1
	 */
	public function edit_basic_step( $post_id, $steps_filled ) {

		// Define required fields + error messages.
		$required_fields = array(
			'service_title'       => esc_html__( 'Title is required.', 'gigneo' ),
			'service_category'    => esc_html__( 'Category is required.', 'gigneo' ),
			'service_subcategory' => esc_html__( 'Subcategory is required.', 'gigneo' ),
			'service_skills'      => esc_html__( 'Skills are required.', 'gigneo' ),
		);

		// Service type is required only when platform setting allows both.
		if ( gigneo_option( '_gigneo_ts_service_type', 'both' ) === 'both' ) {
			$required_fields['service_type'] = esc_html__( 'Type is required.', 'gigneo' );
		}

		// Validate required fields.
		foreach ( $required_fields as $required_field => $error_message ) {
			// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
			if ( empty( $_POST[ $required_field ] ) ) {
				wp_send_json_error( array( 'message' => $error_message ), 422 );
			}
		}

		// phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended -- Nonce verified upstream in save_service().

		// Prepare clean title.
		$title  = isset( $_POST['service_title'] ) ? sanitize_text_field( wp_unslash( $_POST['service_title'] ) ) : '';
		$prefix = trim( gigneo_option( '_gigneo_ts_service_title_prefix' ) );
		$title  = ! empty( $prefix ) ? $prefix . ' ' . $title : $title;

		// Taxonomy / Facets — sanitize and allowlist against gigneo_facet hierarchy.
		$category    = isset( $_POST['service_category'] ) ? sanitize_text_field( wp_unslash( $_POST['service_category'] ) ) : '';
		$subcategory = isset( $_POST['service_subcategory'] ) ? sanitize_text_field( wp_unslash( $_POST['service_subcategory'] ) ) : '';

		// phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
		$category_list = gigneo_term_list(
			array(
				'taxonomy' => 'gigneo_facet',
				'parent'   => 0,
			),
			false
		);

		if ( empty( $category ) || ! array_key_exists( $category, $category_list ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Category is required.', 'gigneo' ) ), 422 );
		}

		$category_term = get_term_by( 'slug', $category, 'gigneo_facet' );
		if ( ! $category_term || is_wp_error( $category_term ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Category is required.', 'gigneo' ) ), 422 );
		}

		$subcategory_list = gigneo_term_list(
			array(
				'taxonomy' => 'gigneo_facet',
				'parent'   => $category_term->term_id,
			),
			false
		);

		if ( empty( $subcategory ) || ! array_key_exists( $subcategory, $subcategory_list ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Subcategory is required.', 'gigneo' ) ), 422 );
		}

		$subcategory_term = get_term_by( 'slug', $subcategory, 'gigneo_facet' );
		$facets           = array( $category, $subcategory );

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$service_specific_types = isset( $_POST['service_specific_types'] ) && is_array( $_POST['service_specific_types'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['service_specific_types'] ) ) : array();

		if ( ! empty( $service_specific_types ) ) {
			$max_specific_types = gigneo_option( '_gigneo_ts_service_max_specific_types', '5' );
			$specific_types     = gigneo_filter_term_slugs(
				$service_specific_types,
				gigneo_term_list(
					array(
						'taxonomy' => 'gigneo_facet',
						'parent'   => $subcategory_term->term_id,
					),
					false
				)
			);

			if ( count( $specific_types ) > intval( $max_specific_types ) ) {
				wp_send_json_error(
					array(
						'message' => sprintf(
							// translators: %d: maximum number of specific types allowed
							esc_html__( 'You can select up to %d specialties only.', 'gigneo' ),
							$max_specific_types
						),
					),
					422
				);
			}

			$facets = array_merge( $facets, $specific_types );
		}

		// Taxonomy Skills — sanitize and allowlist against gigneo_skill.
		$max_skills = gigneo_option( '_gigneo_ts_service_max_skills', '5' );
		$skill_list = gigneo_term_list( array( 'taxonomy' => 'gigneo_skill' ), false );
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$service_skills = isset( $_POST['service_skills'] ) && is_array( $_POST['service_skills'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['service_skills'] ) ) : array();
		$skills         = gigneo_filter_term_slugs( $service_skills, $skill_list );

		if ( empty( $skills ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Skills are required.', 'gigneo' ) ), 422 );
		}

		if ( count( $skills ) > intval( $max_skills ) ) {
			wp_send_json_error(
				array(
					'message' => sprintf(
						// translators: %d: maximum number of skills allowed
						esc_html__( 'You can select up to %d skills only.', 'gigneo' ),
						$max_skills
					),
					'field'   => 'service_skills', // optional - for frontend focus.
				),
				422
			);
		}

		$post_data = array(
			'post_type'  => 'gigneo_service',
			'post_title' => $title,
			'post_name'  => sanitize_title( $title ),
		);

		if ( $post_id > 0 ) {
			// Update existing service.
			$post_data['ID'] = $post_id;
			$result          = wp_update_post( $post_data );

			if ( is_wp_error( $result ) || 0 === $result ) {
				wp_send_json_error( array( 'message' => esc_html__( 'Failed to update service.', 'gigneo' ) ), 500 );
			}
		} else {
			$current_user = wp_get_current_user();
			// Create new service.
			$post_data['post_status'] = 'draft';
			$post_data['post_author'] = $current_user->ID;

			$post_id = wp_insert_post( $post_data );

			if ( is_wp_error( $post_id ) || 0 === $post_id ) {
				wp_send_json_error( array( 'message' => esc_html__( 'Failed to create service.', 'gigneo' ) ), 500 );
			}
		}

		wp_set_object_terms( $post_id, $skills, 'gigneo_skill' );
		wp_set_object_terms( $post_id, $facets, 'gigneo_facet' );
		// Service Type Meta.
		$default_type     = gigneo_option( '_gigneo_ts_service_type', 'both' );
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$service_type     = isset( $_POST['service_type'] ) ? sanitize_text_field( wp_unslash( $_POST['service_type'] ) ) : $default_type;
		$completed_orders = gigneo_post_meta( $post_id, '_gigneo_pm_total_completed_order') ?? 0;

		update_post_meta( $post_id, '_gigneo_pm_type', $service_type );
		update_post_meta( $post_id, '_gigneo_pm_total_completed_order', $completed_orders );

		// Update Steps.
		$steps_filled[] = 'basic';
		update_post_meta( $post_id, '_gigneo_pm_steps_filled', array_unique( $steps_filled ) );

		// Render Features.
		ob_start();
		gigneo_load_template( 'dashboard/service/parts/edit/features', true, array( 'p_id' => $post_id ) );
		$features_html = ob_get_clean();

		// Render Details.
		ob_start();
		gigneo_load_template( 'dashboard/service/parts/edit/details', true, array( 'p_id' => $post_id ) );
		$details_html = ob_get_clean();

		return array(
			'id'       => $post_id,
			'features' => $features_html,
			'details'  => $details_html,
		);
	}

	/**
	 * Handle pricing step save.
	 *
	 * @param int   $post_id      Service ID.
	 * @param array $steps_filled Filled steps.
	 *
	 * @return array
	 * @since 0.0.1
	 */
	public function edit_pricing_step( $post_id, $steps_filled ) {

		$plans = array( 'plan_one', 'plan_two', 'plan_three' );

		// Determine if multi-tier pricing is enabled.
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Read-only parameters no nonce required.
		$multi_tier = isset( $_POST['price_multi_tier'] );
		if ( ! $multi_tier ) {
			$plans = array( 'plan_one' );
		}

		// Define base required fields per plan.
		$required_fields = array(
			'title'              => esc_html__( 'Title is required.', 'gigneo' ),
			'description'        => esc_html__( 'Description is required.', 'gigneo' ),
			'delivery_time'      => esc_html__( 'Delivery time is required.', 'gigneo' ),
			'delivery_time_type' => esc_html__( 'Delivery time type is required.', 'gigneo' ),
			'price'              => esc_html__( 'Price is required.', 'gigneo' ),
		);

		// Remove title requirement if global setting disables custom titles.
		if ( gigneo_option( '_gigneo_ts_service_pricing_title', 'custom' ) !== 'custom' ) {
			unset( $required_fields['title'] );
		}

		// Validate required fields for active plans.
		foreach ( $plans as $plan ) {
			foreach ( $required_fields as $key => $message ) {
				$field = "{$plan}_$key";
				// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Read-only parameters no nonce required.
				if ( empty( $_POST[ $field ] ) ) {
					wp_send_json_error( array( 'message' => $message ), 422 );
				}
			}
		}

		// Save standard pricing fields per plan.
		foreach ( $plans as $plan ) {
			foreach ( array_keys( $required_fields ) as $key ) {
				$meta_key = "{$plan}_$key";
				// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
				$value = isset( $_POST[ $meta_key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $meta_key ] ) ) : '';
				update_post_meta( $post_id, "_gigneo_pm_price_{$plan}_$key", $value );
			}

			// Revisions (not in required fields, but always saved).
			$revisions_key = "{$plan}_revisions";
			// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
			$revisions = isset( $_POST[ $revisions_key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $revisions_key ] ) ) : '';
			update_post_meta( $post_id, "_gigneo_pm_price_{$plan}_revisions", $revisions );

			// Reset featured flag first.
			delete_post_meta( $post_id, "_gigneo_pm_price_plan_{$plan}_featured" );
		}

		// Save per-plan features.
		$facets = get_the_terms( $post_id, 'gigneo_facet' );

		if ( $facets && ! is_wp_error( $facets ) ) {
			foreach ( $facets as $facet ) {
				$features = gigneo_term_meta( $facet->term_id, '_gigneo_tm_service_features' );

				if ( ! $features || ! is_array( $features ) ) {
					continue;
				}

				foreach ( $features as $f => $feature ) {
					if ( empty( $feature['title'] ) ) {
						continue;
					}

					foreach ( $plans as $plan ) {
						$meta_key = "{$plan}_feature_{$facet->slug}_$f";
                        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Read-only parameters no nonce required.
						$value = ! empty( $_POST[ $meta_key ] ) ? 'yes' : '';
						update_post_meta( $post_id, "_gigneo_pm_price_$meta_key", $value );
					}
				}
			}
		}

		// Multi-tier.
		update_post_meta( $post_id, '_gigneo_pm_price_multi_tier', $multi_tier ? 'yes' : 'no' );

		// Custom offers.
		$custom_offers = array();

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$custom_offers_raw = isset( $_POST['custom_offers'] ) && is_array( $_POST['custom_offers'] ) ? map_deep( wp_unslash( $_POST['custom_offers'] ), 'sanitize_text_field' ) : array();

		if ( ! empty( $custom_offers_raw ) ) {
			foreach ( $custom_offers_raw as $index => $offer ) {
				$offer_name = ! empty( $offer['name'] ) ? sanitize_text_field( $offer['name'] ) : '';
				if ( empty( $offer_name ) ) {
					wp_send_json_error( array( 'message' => esc_html__( 'Required field could not be empty.', 'gigneo' ) ) );
				}
				$custom_offers[ $index ]['name']       = $offer_name;
				$custom_offers[ $index ]['plan_one']   = ! empty( $offer['plan_one'] ) ? 'yes' : '';
				$custom_offers[ $index ]['plan_two']   = ! empty( $offer['plan_two'] ) ? 'yes' : '';
				$custom_offers[ $index ]['plan_three'] = ! empty( $offer['plan_three'] ) ? 'yes' : '';
			}
		}

		update_post_meta( $post_id, '_gigneo_pm_custom_offers', $custom_offers );

		// Add-ons.
		do_action( 'gigneo_save_service_pricing_addons', $post_id );

		// Update Steps.
		$steps_filled[] = 'pricing';
		update_post_meta( $post_id, '_gigneo_pm_steps_filled', array_unique( $steps_filled ) );

		return array( 'id' => $post_id );
	}

	/**
	 * Handle media step save.
	 *
	 * @param int   $post_id      Service ID.
	 * @param array $steps_filled Filled steps.
	 *
	 * @return array
	 * @since 0.0.1
	 */
	public function edit_media_step( $post_id, $steps_filled ) {

		$current_user_id = get_current_user_id();

		$gallery_limit  = (int) gigneo_option( '_gigneo_ts_service_gallery_limit', 3 );
		$document_limit = (int) gigneo_option( '_gigneo_ts_service_document_limit', 1 );

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$gallery_raw = isset( $_POST['gigneo_pm_gallery'] ) && is_array( $_POST['gigneo_pm_gallery'] ) ? array_map( 'absint', wp_unslash( $_POST['gigneo_pm_gallery'] ) ) : array();
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$documents_raw = isset( $_POST['gigneo_pm_documents'] ) && is_array( $_POST['gigneo_pm_documents'] ) ? array_map( 'absint', wp_unslash( $_POST['gigneo_pm_documents'] ) ) : array();
		$gallery       = ! empty( $gallery_raw ) ? array_filter( $gallery_raw ) : array();
		$documents     = ! empty( $documents_raw ) ? array_filter( $documents_raw ) : array();

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$video_type = isset( $_POST['gigneo_pm_video_type'] ) ? sanitize_text_field( wp_unslash( $_POST['gigneo_pm_video_type'] ) ) : '';
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$video_link = isset( $_POST['gigneo_pm_video_link'] ) ? sanitize_text_field( wp_unslash( $_POST['gigneo_pm_video_link'] ) ) : '';
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$video_file = isset( $_POST['gigneo_pm_video'] ) ? absint( wp_unslash( $_POST['gigneo_pm_video'] ) ) : 0;

		if ( ! empty( $video_type ) && ! in_array( $video_type, array( 'video', 'link' ), true ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 400 );
		}

		// Validate attachments ownership.
		$validate = function ( array $ids ) use ( $current_user_id ) {
			if ( empty( $ids ) ) {
				return true;
			}

			$valid_ids = get_posts(
				array(
					'post_type'      => 'attachment',
					'post__in'       => $ids,
					'author'         => $current_user_id,
					'fields'         => 'ids',
					'posts_per_page' => -1,
				)
			);

			return count( $valid_ids ) === count( $ids );
		};

		if ( empty( $gallery ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Service images are required.', 'gigneo' ) ), 400 );
		}

		if ( count( $gallery ) !== count( array_unique( $gallery ) ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Duplicate images are not allowed.', 'gigneo' ) ), 400 );
		}

		if ( count( $gallery ) > $gallery_limit ) {
			wp_send_json_error(
				array(
					'message' => sprintf(
						// translators: %d: maximum number of images allowed.
						esc_html__( 'Maximum %d images allowed.', 'gigneo' ),
						$gallery_limit
					),
				),
				400
			);
		}

		if ( count( $documents ) !== count( array_unique( $documents ) ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Duplicate documents are not allowed.', 'gigneo' ) ), 400 );
		}

		if ( count( $documents ) > $document_limit ) {
			wp_send_json_error(
				array(
					'message' => sprintf(
						// translators: %d: maximum number of documents allowed.
						esc_html__( 'Maximum %d document allowed.', 'gigneo' ),
						$document_limit
					),
				),
				400
			);
		}

		if ( ! $validate( $gallery ) || ! $validate( $documents ) || ( $video_file && ! $validate( array( $video_file ) ) ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Invalid media files.', 'gigneo' ) ), 400 );
		}

		// Save data.
		update_post_meta( $post_id, '_gigneo_pm_video_type', $video_type );
		update_post_meta( $post_id, '_gigneo_pm_gallery', $gallery );
		update_post_meta( $post_id, '_gigneo_pm_documents', $documents );

		if ( 'video' === $video_type ) {
			update_post_meta( $post_id, '_gigneo_pm_video', $video_file ?? 0 );
		} elseif ( 'link' === $video_type && $video_link ) {
			update_post_meta( $post_id, '_gigneo_pm_video_link', esc_url_raw( $video_link ) );
		}

		if ( ! empty( $gallery[0] ) ) {
			set_post_thumbnail( $post_id, $gallery[0] );
		}

		$steps_filled[] = 'media';
		update_post_meta( $post_id, '_gigneo_pm_steps_filled', array_unique( $steps_filled ) );

		return array( 'id' => $post_id );
	}

	/**
	 * Handle details step save.
	 *
	 * @param int   $post_id      Service ID.
	 * @param array $steps_filled Filled steps.
	 *
	 * @return array
	 * @since 0.0.1
	 */
	public function edit_details_step( $post_id, $steps_filled ) {

		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
		$description = isset( $_POST['description'] ) ? wp_kses( wp_unslash( $_POST['description'] ), gigneo_allowed_html_tags() ) : '';

		if ( empty( $description ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Required field could not be empty.', 'gigneo' ) ) );
		}

		update_post_meta( $post_id, '_gigneo_pm_description', $description );

		$service_type_option = gigneo_option( '_gigneo_ts_service_type', 'both' );
		$service_type_meta   = gigneo_post_meta( $post_id, '_gigneo_pm_type' );

		$locations = gigneo_post_meta( $post_id, '_gigneo_pm_locations' );

		if ( in_array( 'map', gigneo_active_modules(), true ) && empty( $locations ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Location details are required.', 'gigneo' ) ) );
		}

		if ( 'remote' !== $service_type_option && 'remote' !== $service_type_meta ) {

			if ( empty( $locations ) ) {
				wp_send_json_error( array( 'message' => esc_html__( 'Location details are required.', 'gigneo' ) ) );
			}

			// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified upstream in save_service().
			$delivery_type = isset( $_POST['delivery_type'] ) ? sanitize_textarea_field( wp_unslash( $_POST['delivery_type'] ) ) : '';
			$delivery_type = 'visit_me' === $delivery_type || 'on_location' === $delivery_type ? $delivery_type : 'both';
			update_post_meta( $post_id, '_gigneo_pm_delivery_type', $delivery_type );
		} else {
			delete_post_meta( $post_id, '_gigneo_pm_delivery_type' );
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verified upstream; nested data is sanitized in the loop below.
		$faqs     = isset( $_POST['faqs'] ) && is_array( $_POST['faqs'] ) ? wp_unslash( $_POST['faqs'] ) : array();
		$new_faqs = array();
		if ( ! empty( $faqs ) ) {
			foreach ( $faqs as $f => $faq ) {
				if ( empty( $faq['question'] ) || empty( $faq['answer'] ) ) {
					wp_send_json_error( array( 'message' => esc_html__( 'Required field could not be empty.', 'gigneo' ) ) );
				}
				$new_faqs[ $f ]['question'] = sanitize_text_field( $faq['question'] );
				$new_faqs[ $f ]['answer']   = sanitize_textarea_field( $faq['answer'] );
			}
		}

		update_post_meta( $post_id, '_gigneo_pm_faqs', $new_faqs );

		// Update Steps.
		$steps_filled[] = 'details';
		update_post_meta( $post_id, '_gigneo_pm_steps_filled', array_unique( $steps_filled ) );

		$average_rating = gigneo_post_meta( $post_id, '_gigneo_pm_average_rating' );
		if ( empty( $average_rating ) ) {
			update_post_meta( $post_id, '_gigneo_pm_average_rating', '0' );
		}

		return array( 'id' => $post_id );
	}

	/**
	 * Save service location.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function save_location() {

		// Security / Nonce verification.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		// Basic authorization checks.
		$profile_id   = gigneo_active_profile_id();
		$current_user = wp_get_current_user();

		if ( empty( $current_user->ID ) || empty( $profile_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'gigneo' ) ), 403 );
		}

		$id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;

		if ( $id > 0 && ! gigneo_validate_post_author( $id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to edit this service.', 'gigneo' ) ), 403 );
		}

		$default_service_type = gigneo_option( '_gigneo_ts_service_type', 'both' );

		if ( 'remote' === $default_service_type ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 403 );
		}

		$location_id      = isset( $_POST['location_id'] ) ? sanitize_text_field( wp_unslash( $_POST['location_id'] ) ) : '';
		$country_state    = ! empty( $_POST['country_state'] ) ? sanitize_text_field( wp_unslash( $_POST['country_state'] ) ) : '';
		$city             = ! empty( $_POST['city'] ) ? sanitize_text_field( wp_unslash( $_POST['city'] ) ) : '';
		$area             = ! empty( $_POST['area'] ) ? sanitize_text_field( wp_unslash( $_POST['area'] ) ) : '';
		$zip_code         = ! empty( $_POST['zip_code'] ) ? sanitize_text_field( wp_unslash( $_POST['zip_code'] ) ) : '';
		$friendly_address = ! empty( $_POST['friendly_address'] ) ? sanitize_text_field( wp_unslash( $_POST['friendly_address'] ) ) : '';

		if ( empty( $country_state ) || empty( $zip_code ) || empty( $friendly_address ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Required field could not be empty.', 'gigneo' ) ), 403 );
		}

		$location = array();

		$max_radius = ! empty( $_POST['max_radius'] ) ? sanitize_text_field( wp_unslash( $_POST['max_radius'] ) ) : '';

		$location_details = array(
			'location'   => $location,
			'max_radius' => $max_radius,
		);
		$location_details = apply_filters( 'gigneo_save_location_before', $location_details, $_POST );
		$location         = $location_details['location'];
		$max_radius       = $location_details['max_radius'];

		$max_location = 1;
		if ( in_array( 'multi_location', gigneo_active_modules(), true ) ) {
			$max_location = gigneo_option( '_gigneo_ts_service_max_locations', 3 );
		}

		$locations_meta = gigneo_post_meta( $id, '_gigneo_pm_locations' );
		$locations_meta = is_array( $locations_meta ) ? $locations_meta : array();
		$location_count = count( $locations_meta );

		if ( ! isset( $location_id ) && $location_count >= $max_location ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You have exceed maximum location limit.', 'gigneo' ) ), 403 );
		}

		if ( empty( $max_radius ) ) {
			$max_radius = gigneo_option( '_gigneo_ts_map_radius', '10' );
		}

		$location_data = array(
			'value'         => '_',
			'country_state' => $country_state,
			'city'          => $city,
			'area'          => $area,
			'zip_code'      => $zip_code,
			'max_radius'    => $max_radius,
			'address'       => $friendly_address,
			'location'      => array( $location ),
		);

		if ( isset( $location_id ) && '' !== $location_id && isset( $locations_meta[ absint( $location_id ) ] ) ) {
			$locations_meta[ absint( $location_id ) ] = $location_data;
		} else {
			array_unshift( $locations_meta, $location_data );
		}

		update_post_meta( $id, '_gigneo_pm_locations', $locations_meta );

		$disable_cities       = gigneo_option( '_gigneo_ts_disable_cities' );
		$country_state_list   = gigneo_countries_with_states( 'active' );
		$country_state_cities = gigneo_country_state_cities();

		$template_args = array(
			'p_id'                 => $id,
			'disable_cities'       => $disable_cities,
			'country_state_list'   => $country_state_list,
			'country_state_cities' => $country_state_cities,
			'map_enabled'          => false,
		);

		// Render Locations.
		ob_start();
		gigneo_load_template( 'dashboard/service/parts/edit/locations', true, $template_args );
		$locations_html = ob_get_clean();

		$response = array(
			'message'   => esc_html__( 'Service location saved successfully.', 'gigneo' ),
			'locations' => $locations_html,
		);

		wp_send_json_success( $response, 200 );
	}

	/**
	 * Remove service location.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function remove_location() {

		// Security / Nonce verification.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		// Basic authorization checks.
		$profile_id   = gigneo_active_profile_id();
		$current_user = wp_get_current_user();

		if ( empty( $current_user->ID ) || empty( $profile_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'gigneo' ) ), 403 );
		}

		$id          = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
		$location_id = isset( $_POST['location_id'] ) ? absint( wp_unslash( $_POST['location_id'] ) ) : null;

		if ( empty( $id ) || null === $location_id || ! gigneo_validate_post_author( $id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to edit this service.', 'gigneo' ) ), 403 );
		}

		$locations_meta = gigneo_post_meta( $id, '_gigneo_pm_locations' );

		if ( ! isset( $locations_meta[ $location_id ] ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to edit this service.', 'gigneo' ) ), 403 );
		}

		unset( $locations_meta[ $location_id ] );

		update_post_meta( $id, '_gigneo_pm_locations', $locations_meta );

		$disable_cities       = gigneo_option( '_gigneo_ts_disable_cities' );
		$country_state_list   = gigneo_countries_with_states( 'active' );
		$country_state_cities = gigneo_country_state_cities();
		$map_enabled          = false;

		$template_args = array(
			'p_id'                 => $id,
			'disable_cities'       => $disable_cities,
			'country_state_list'   => $country_state_list,
			'country_state_cities' => $country_state_cities,
			'map_enabled'          => $map_enabled,
		);

		// Render Locations.
		ob_start();
		gigneo_load_template( 'dashboard/service/parts/edit/locations', true, $template_args );
		$locations_html = ob_get_clean();

		$response = array(
			'message'   => esc_html__( 'Service location removed successfully.', 'gigneo' ),
			'locations' => $locations_html,
		);

		wp_send_json_success( $response, 200 );
	}

	/**
	 * Duplicate service entry.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function service_clone() {

		// Verify security nonce.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		// Get current user and active profile.
		$profile_id   = gigneo_active_profile_id();
		$current_user = wp_get_current_user();

		if ( empty( $current_user->ID ) || empty( $profile_id ) ) {
			wp_send_json_error(
				array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ),
				403
			);
		}

		// Retrieve and sanitize the original service ID.
		$original_id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;

		if ( empty( $original_id ) ) {
			wp_send_json_error(
				array( 'message' => esc_html__( 'Please select a service', 'gigneo' ) ),
				400
			);
		}

		// Verify the user owns the service.
		if ( ! gigneo_validate_post_author( $original_id ) ) {
			wp_send_json_error(
				array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ),
				403
			);
		}

		// Load the original post object.
		$original_post = get_post( $original_id );

		if ( ! $original_post || 'gigneo_service' !== $original_post->post_type ) {
			wp_send_json_error(
				array( 'message' => esc_html__( 'Service not found.', 'gigneo' ) ),
				404
			);
		}

		// Enforce creation conditions (profile image, email, identity).
		$this->validate_service_creation_conditions( $profile_id, $current_user->ID );

		// Enforce creation limit (how many services the user may have).
		$this->validate_service_creation_limit( $current_user->ID );

		// Create the new draft post (duplicate core fields).
		$new_post_args = array(
			'post_type'    => 'gigneo_service',
			'post_title'   => $original_post->post_title . ' (Copy)',
			'post_content' => $original_post->post_content,
			'post_status'  => 'draft',
			'post_author'  => $current_user->ID,
		);

		$new_service_id = wp_insert_post( $new_post_args );

		if ( is_wp_error( $new_service_id ) || empty( $new_service_id ) ) {
			wp_send_json_error(
				array( 'message' => esc_html__( 'Failed to duplicate service.', 'gigneo' ) ),
				400
			);
		}

		// Duplicate taxonomies (facets & tags).
		$taxonomies = array( 'gigneo_facet', 'gigneo_skill' );
		foreach ( $taxonomies as $taxonomy ) {
			$terms = wp_get_object_terms( $original_id, $taxonomy, array( 'fields' => 'ids' ) );
			if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
				wp_set_object_terms( $new_service_id, $terms, $taxonomy );
			}
		}

		// Duplicate ALL meta fields automatically (no key list).
		global $wpdb;
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$all_meta = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id = %d",
				$original_id
			),
			ARRAY_A
		);
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

		if ( $all_meta ) {
			foreach ( $all_meta as $meta ) {
				$key   = $meta['meta_key'];
				$value = maybe_unserialize( $meta['meta_value'] );

				// Skip metas.
				if (
					in_array(
						$key,
						array(
							'_edit_lock',
							'_edit_last',
							'_gigneo_pm_status',
							'_gigneo_pm_credit_invoice_id',
							'_gigneo_pm_manual_approval_info',
                            '_gigneo_pm_featured',
						),
						true
					)
				) {
					continue;
				}

				// Copy every other meta field.
				update_post_meta( $new_service_id, $key, $value );
			}
		}

		// Duplicate featured image.
		$thumbnail_id = get_post_thumbnail_id( $original_id );
		if ( $thumbnail_id ) {
			set_post_thumbnail( $new_service_id, $thumbnail_id );
		}

		$response['message']      = esc_html__( 'Service cloned successfully.', 'gigneo' );
		$response['redirect_url'] = gigneo_dashboard_url( 'service', 'edit', array( 'p_id' => $new_service_id ) );

		// Send JSON success.
		wp_send_json_success( $response, 200 );
	}

	/**
	 * Approve service request.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function approve_service() {

		// Check if nonce is valid.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		// Check if the user has permission to edit settings (usually admin).
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 403 );
		}

		// Parse serialized data manually.
		$service_id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;

		if ( empty( $service_id ) || 'gigneo_service' !== get_post_type( $service_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 409 );
		}

		update_post_meta( $service_id, '_gigneo_pm_status', 'publish' );

		$manual_approval_data           = gigneo_post_meta( $service_id, '_gigneo_pm_manual_approval_info' );
		$manual_approval_data['status'] = 'approved';

		update_post_meta( $service_id, '_gigneo_pm_manual_approval_info', $manual_approval_data );

		wp_update_post(
			array(
				'ID'          => $service_id,
				'post_status' => 'publish',
			)
		);

		$service_author = get_post_field( 'post_author', $service_id );
		$profile_id     = gigneo_profile_id( $service_author, 'freelancer' );

		// Approved service Notification.
		$approved_service_nf_title   = esc_html__( 'Service approved', 'gigneo' );
		$approved_service_nf_content = sprintf(
			// translators: 1: Service Title, 2: Profile Name.
			esc_html__( 'Hey %1$s, Your service "%2$s" has been approved.', 'gigneo' ),
			gigneo_profile_name( $profile_id ),
			get_the_title( $service_id )
		);
		Notifications::trigger_notification( $approved_service_nf_title, $approved_service_nf_content, 'service', 'service_approved', $profile_id, $service_id );

		$wc_mailer = WC()->mailer();
		$wc_emails = $wc_mailer->get_emails();

		$wc_emails['Gigneo_Service_Approved']->trigger( $profile_id, $service_id );

		$response            = array();
		$response['message'] = esc_html__( 'Service has been approved successfully.', 'gigneo' );

		wp_send_json_success( $response, 200 );
	}

	/**
	 * Reject service request.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function reject_service() {

		// Check if nonce is valid.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		// Check if the user has permission to edit settings (usually admin).
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 403 );
		}

		$id      = isset( $_POST['service_id'] ) ? absint( wp_unslash( $_POST['service_id'] ) ) : 0;
		$type    = isset( $_POST['rejection_type'] ) ? sanitize_text_field( wp_unslash( $_POST['rejection_type'] ) ) : '';
		$detail  = isset( $_POST['rejection_detail'] ) ? sanitize_textarea_field( wp_unslash( $_POST['rejection_detail'] ) ) : '';
		$changes = isset( $_POST['request_changes'] ) ? sanitize_textarea_field( wp_unslash( $_POST['request_changes'] ) ) : '';

		if ( empty( $id ) || empty( $type ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 409 );
		}

		if ( 'soft' !== $type && 'hard' !== $type ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Rejection type required.', 'gigneo' ) ), 409 );
		}

		if ( 'soft' === $type && empty( $changes ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Required field could not be empty.', 'gigneo' ) ), 409 );
		}

		if ( 'hard' === $type && empty( $detail ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Required field could not be empty.', 'gigneo' ) ), 409 );
		}

		$pm_status = gigneo_post_meta( $id, '_gigneo_pm_status' );

		if ( 'pending' !== $pm_status ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do this.', 'gigneo' ) ), 403 );
		}

		$post_author_id    = get_post_field( 'post_author', $id );
		$author_profile_id = gigneo_active_profile_id( $post_author_id );

		$manual_approval_data = gigneo_post_meta( $id, '_gigneo_pm_manual_approval_info' );

		$manual_approval_data['status']              = 'rejected';
		$manual_approval_data['rejection']['type']   = $type;
		$manual_approval_data['rejection']['detail'] = 'soft' === $type ? $changes : $detail;

		update_post_meta( $id, '_gigneo_pm_status', 'rejected' );
		update_post_meta( $id, '_gigneo_pm_manual_approval_info', $manual_approval_data );

		wp_update_post(
			array(
				'ID'          => $id,
				'post_status' => 'rejected',
			)
		);

		if ( 'hard' === $type ) {
			wp_trash_post( $id );
		}

		// Reject service Notification.
		$reject_service_nf_title   = esc_html__( 'Service Hard Rejected!', 'gigneo' );
		$reject_service_nf_content = sprintf(
			// translators: 1: Service Title, 2: Profile Name.
			esc_html__( 'Hey %1$s, Your service "%2$s" has been rejected and no longer available.', 'gigneo' ),
			gigneo_profile_name( $author_profile_id ),
			get_the_title( $id )
		);

		if ( 'soft' === $type ) {
			// Reject service Notification.
			$reject_service_nf_title   = esc_html__( 'Service rejected', 'gigneo' );
			$reject_service_nf_content = sprintf(
				// translators: 1: Service Title, 2: Profile Name.
				esc_html__( 'Hey %1$s, Your service "%2$s" has been soft rejected. Please review and update the service details to resubmit for approval.', 'gigneo' ),
				gigneo_profile_name( $author_profile_id ),
				get_the_title( $id )
			);

		}
		Notifications::trigger_notification( $reject_service_nf_title, $reject_service_nf_content, 'service', 'service_rejected', $author_profile_id, $id );

		$wc_mailer = WC()->mailer();
		$wc_emails = $wc_mailer->get_emails();

		$wc_emails['Gigneo_Service_Rejected']->trigger( $author_profile_id, $id, $type );

		$response            = array();
		$response['message'] = esc_html__( 'Service has been rejected successfully.', 'gigneo' );

		wp_send_json_success( $response, 200 );
	}

	/**
	 * Remove service post.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function remove_service() {

		// Check if nonce is valid.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		$profile_id   = gigneo_active_profile_id();
		$current_user = wp_get_current_user();

		if ( empty( $current_user->ID ) || empty( $profile_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 403 );
		}

		// Parse serialized data manually.
		$id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;

		if ( empty( $id ) || ! gigneo_validate_post_author( $id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Please select a service', 'gigneo' ) ), 400 );
		}

		// Get the original service post.
		$original_post = get_post( $id );
		if ( ! $original_post || 'gigneo_service' !== $original_post->post_type ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Service not found.', 'gigneo' ) ), 404 );
		}

		// Remove the service post.
		$deleted = wp_delete_post( $id, true );

		if ( $deleted ) {
			wp_send_json_success( array( 'message' => esc_html__( 'Service removed successfully.', 'gigneo' ) ), 200 );
		}

		wp_send_json_error( array( 'message' => esc_html__( 'Failed to remove service.', 'gigneo' ) ), 400 );
	}

	/**
	 * Move service post to draft.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function draft_service() {

		// Verify nonce.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		$current_user = wp_get_current_user();
		$profile_id   = gigneo_active_profile_id();

		if ( empty( $current_user->ID ) || empty( $profile_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 403 );
		}

		// Validate post ID.
		$id = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0;

		if ( ! $id || ! gigneo_validate_post_author( $id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 400 );
		}

		$post = get_post( $id );

		if ( ! $post || 'gigneo_service' !== $post->post_type ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Service not found.', 'gigneo' ) ), 404 );
		}

		if ( 'draft' === $post->post_status ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Service is already in draft.', 'gigneo' ) ), 409 );
		}

		$updated = wp_update_post(
			array(
				'ID'          => $id,
				'post_status' => 'draft',
			),
			true
		);

		if ( is_wp_error( $updated ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Failed to update service.', 'gigneo' ) ), 500 );
		}

		delete_post_meta( $id, '_gigneo_pm_featured' );

		wp_send_json_success( array( 'message' => esc_html__( 'Service moved to draft successfully.', 'gigneo' ) ), 200 );
	}

	/**
	 * Toggle service featured status.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function featured_service() {

		// Check if nonce is valid.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		$profile_id   = gigneo_active_profile_id();
		$current_user = wp_get_current_user();

		if ( empty( $current_user->ID ) || empty( $profile_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 403 );
		}

		$id     = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
		$action = isset( $_POST['featured'] ) ? intval( $_POST['featured'] ) : 0;

		if ( empty( $id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Please select a service', 'gigneo' ) ), 400 );
		}

		if ( ! gigneo_validate_post_author( $id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 403 );
		}

		if ( 'gigneo_service' !== get_post_type( $id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Service not found.', 'gigneo' ) ), 404 );
		}

		if ( ! $action ) {
			delete_post_meta( $id, '_gigneo_pm_featured' );
			wp_send_json_success( array( 'message' => esc_html__( 'Service featured removed successfully.', 'gigneo' ) ), 200 );
		}

		$service_status = get_post_status( $id );

		if ( ! in_array( $service_status, array( 'publish', 'paused' ) ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 400 );
		}

		$featured_limit = gigneo_option( '_gigneo_ts_service_featured_limit', 0 );

		if ( absint( $featured_limit ) < 1 ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You are not allowed to feature services.', 'gigneo' ) ), 400 );
		}

		$author_id = get_post_field( 'post_author', $id );

		$featured_count = count(
			get_posts(
				array(
					'post_type'      => 'gigneo_service',
					'post_status'    => array( 'publish', 'paused', 'draft' ),
					'author'         => $author_id,
					'posts_per_page' => - 1,
					'fields'         => 'ids',
                    // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
						'meta_key'   => '_gigneo_pm_featured',
                    // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
						'meta_value' => true,
				)
			)
		);

		if ( $featured_count >= $featured_limit ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You have reached the maximum number of featured services allowed.', 'gigneo' ) ), 400 );
		}

		update_post_meta( $id, '_gigneo_pm_featured', true );

		wp_send_json_success( array( 'message' => esc_html__( 'Service featured successfully.', 'gigneo' ) ), 200 );
	}

	/**
	 * Pause or resume a service.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function pause_active_service() {

		// Check if nonce is valid.
		$security = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : '';
		if ( empty( $security ) || ! wp_verify_nonce( $security, 'gigneo_security' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Security verification failed. Please reload and try again.', 'gigneo' ) ), 403 );
		}

		$profile_id   = gigneo_active_profile_id();
		$current_user = wp_get_current_user();

		if ( empty( $current_user->ID ) || empty( $profile_id ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 403 );
		}

		if ( empty( $_POST['id'] ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 400 );
		}

		$id            = absint( wp_unslash( $_POST['id'] ) );
		$validate_post = gigneo_validate_post_author( $id );

		if ( empty( $validate_post ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to do that.', 'gigneo' ) ), 403 );
		}

		// Get the original service post.
		$original_post = get_post( $id );

		if ( ! $original_post || 'gigneo_service' !== $original_post->post_type ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 400 );
		}

		if ( 'publish' !== $original_post->post_status && 'paused' !== $original_post->post_status ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 400 );
		}

		$status = isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : 'paused';

		if ( 'publish' !== $status && 'paused' !== $status ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 400 );
		}

		// Remove the service post.
		$post_data = array(
			'ID'          => $id,
			'post_status' => $status,
		);

		$post_update = wp_update_post( $post_data );

		if ( is_wp_error( $post_update ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Something went wrong.', 'gigneo' ) ), 400 );
		}

		wp_send_json_success( array( 'message' => esc_html__( 'Service status has been updated successfully.', 'gigneo' ) ), 200 );
	}

	/**
	 * Render service manual-approval actions.
	 *
	 * @param int    $post_id   Post ID.
	 * @param int    $author_id Author ID.
	 * @param string $post_type Post type.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function post_type_actions( $post_id, $author_id, $post_type ) {

		if ( 'gigneo_service' !== $post_type ) {
			return;
		}

		$service_manual_approval_option = gigneo_option( '_gigneo_ts_service_manually_approve', false );
		$service_manual_approval_info   = gigneo_post_meta( $post_id, '_gigneo_pm_manual_approval_info' );
		$service_manual_approval_status = $service_manual_approval_info['status'] ?? '';

		if ( ! $service_manual_approval_option || ! in_array( $service_manual_approval_status, array( 'pending', 'approved', 'rejected' ), true ) ) {
			return;
		}

		$service_icon = GIGNEO_DIR_URL . "assets/img/icon/service-$service_manual_approval_status.png";
		?>
		<div class="gn-post-type-action gn-action-service">
			<button type="button" data-gn-modal-toggle="gn-service-review-modal-<?php echo esc_attr( $post_id ); ?>">
				<img src="<?php echo esc_url( $service_icon ); ?>"
					alt="<?php echo esc_html__( 'Service Approval', 'gigneo' ); ?>">
			</button>
		</div>
		<?php
		gigneo_load_template( 'admin/parts/service/pending-review', false, array( 'p_id' => $post_id ) );
	}

	/**
	 * Render reject service modal.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function reject_service_markup() {
		$manual_approval_option = gigneo_option( '_gigneo_ts_service_manually_approve', false );
		if ( $manual_approval_option ) {
			gigneo_load_template( 'admin/parts/service/reject-review' );
		}
	}
}
