<?php

class WPCode_Pixel_Admin_Page_Pixel extends WPCode_Admin_Page_Pixel {

	use WPCode_Pixel_Custom_Events_UI;

	/**
	 * Register hook on admin init just for this page.
	 *
	 * @return void
	 */
	public function page_hooks() {
		$this->process_message();
		add_action( 'admin_init', array( $this, 'submit_listener' ) );
		add_filter( 'admin_body_class', array( $this, 'maybe_add_body_classes' ) );
	}

	/**
	 * If you need to page-specific scripts override this function.
	 * Hooked to 'admin_enqueue_scripts'.
	 *
	 * @return void
	 */
	public function page_scripts() {
		// Admin assets for click tracking & custom events configuration.
		$admin_asset_file = WPCODE_PIXEL_PLUGIN_PATH . 'build/admin-custom-tracking.asset.php';
		if ( file_exists( $admin_asset_file ) ) {
			$admin_asset = include_once $admin_asset_file;

			wp_enqueue_script(
				'wpcode-pixel-admin-js',
				plugins_url( 'build/admin-custom-tracking.js', WPCODE_PIXEL_FILE ),
				$admin_asset['dependencies'],
				$admin_asset['version'],
				true
			);
			wp_enqueue_style(
				'wpcode-pixel-admin-css',
				plugins_url( 'build/admin-custom-tracking.css', WPCODE_PIXEL_FILE ),
				null,
				$admin_asset['version']
			);

			wp_localize_script(
				'wpcode-pixel-admin-js',
				'wpcodePixelAdmin',
				array(
					'remove_row' => __( 'Are you sure you want to remove this item?', 'wpcode-pixel' ),
				)
			);
		}

		// Custom Events specific admin assets.
		$custom_events_asset_file = WPCODE_PIXEL_PLUGIN_PATH . 'build/custom-event-tracking.asset.php';
		if ( file_exists( $custom_events_asset_file ) ) {
			$custom_asset = include_once $custom_events_asset_file;

			wp_enqueue_script(
				'wpcode-pixel-custom-events-admin',
				plugins_url( 'build/custom-event-tracking.js', WPCODE_PIXEL_FILE ),
				$custom_asset['dependencies'],
				$custom_asset['version'],
				true
			);
			wp_enqueue_style(
				'wpcode-pixel-custom-events-admin',
				plugins_url( 'build/custom-event-tracking.css', WPCODE_PIXEL_FILE ),
				null,
				$custom_asset['version']
			);
		}
	}

	/**
	 * Add view specific to the Pixel page.
	 * Remove this after adding this view in the main plugin.
	 *
	 * @return void
	 */
	public function setup_views() {
		parent::setup_views();
		// The main plugin defines a legacy "Click Tracking" view; hide it in the addon
		// now that click tracking is handled via Custom Events in this PRO addon.
		if ( isset( $this->views['click_tracking'] ) ) {
			unset( $this->views['click_tracking'] );
		}
		$this->views['snapchat']              = __( 'Snapchat', 'wpcode-pixel' );
		$this->views['custom_event_tracking'] = __( 'Custom Events', 'wpcode-pixel' );
	}

	/**
	 * Process messages specific to this page.
	 *
	 * @return void
	 */
	public function process_message() {
        if ( empty( $_GET['message'] ) ) { // phpcs:ignore
			return;
		}
		$messages = array(
			1 => __( 'Settings updated.', 'wpcode-pixel' ),
			2 => __( 'We encountered an error saving your settings.', 'wpcode-pixel' ),
		);
        $message  = absint( $_GET['message'] );  // phpcs:ignore
		// phpcs:enable WordPress.Security.NonceVerification

		if ( ! isset( $messages[ $message ] ) ) {
			return;
		}

		if ( $message > 1 ) {
			$this->set_error_message( $messages[ $message ] );
		} else {
			$this->set_success_message( $messages[ $message ] );
		}
	}

	/**
	 * The Conversion Pixels page output.
	 * In the actual addon page wrap elements in a form as the lite plugin
	 * never needs an actual form.
	 *
	 * @return void
	 */
	public function output_content() {
		// Check if notice wpconsent method is available.
		if ( method_exists( $this, 'notice_wpconsent' ) ) {
			$this->notice_wpconsent();
		}
		if ( method_exists( $this, 'output_view_' . $this->view ) ) {
			$this->adblocker_notice();
			?>
			<form action="<?php echo esc_url( $this->get_page_action_url() ); ?>" method="post">
				<?php
				call_user_func( array( $this, 'output_view_' . $this->view ) );
				?>
			</form>
			<?php
			$this->adblocker_notice_js();
		}
	}

	/**
	 * Page for Facebook pixel settings with multiple pixels support.
	 *
	 * @return void
	 */
	public function output_view_facebook() {
		$facebook_pixels = $this->get_option( 'facebook_pixels', array() );
		if ( empty( $facebook_pixels ) ) {
			$facebook_pixels = array(
				array(
					'id'    => $this->get_option( 'facebook_pixel_id', '' ),
					'token' => $this->get_option( 'facebook_pixel_api_token', '' ),
				),
			);
		}
		?>
		<h2><?php esc_html_e( 'Facebook Pixel', 'wpcode-pixel' ); ?></h2>

		<div id="wpcode-facebook-pixels-repeater" class="wpcode-settings-repeater-group">
			<?php
			foreach ( $facebook_pixels as $index => $pixel ) {
				$this->facebook_pixel_row( $index, $pixel );
			}
			?>
		</div>
		<button type="button" class="wpcode-button wpcode-button-secondary wpcode-add-pixel" id="wpcode-add-facebook-pixel">
			<?php esc_html_e( 'Add New Facebook Pixel', 'wpcode-pixel' ); ?>
		</button>
		<?php
		$this->metabox_row(
			__( 'Facebook Pixel Events', 'wpcode-pixel' ),
			$this->get_checkbox_inputs(
				array(
					array(
						'label'       => __( 'Page View Event', 'wpcode-pixel' ),
						'name'        => 'page_view',
						'description' => __( 'Enable the Page View event to track and record page visits on all the pages of your website using the Facebook Pixel.', 'wpcode-pixel' ),
						'ecommerce'   => false,
					),
				),
				'facebook_pixel_events'
			)
		);
		$this->metabox_row(
			__( 'eCommerce Events Tracking', 'wpcode-pixel' ),
			$this->get_ecommerce_events_input() . $this->get_checkbox_inputs( $this->get_fb_pixel_events_inputs(), 'facebook_pixel_events' )
		);
		wp_nonce_field( 'wpcode-save-facebook-pixel-data', 'wpcode-pixel-nonce' );
		?>
		<button type="submit" class="wpcode-button">
			<?php esc_html_e( 'Save Changes', 'wpcode-pixel' ); ?>
		</button>
		<script id="wpcode-facebook-pixel-row-template" type="text/template">
			<?php $this->facebook_pixel_row( '{index}' ); ?>
		</script>
		<?php
	}

	/**
	 * Output the HTML for a single Facebook pixel row.
	 *
	 * @param string|int $index The index of the row.
	 * @param array      $pixel The pixel data.
	 *
	 * @return void
	 */
	public function facebook_pixel_row( $index, $pixel = array() ) {
		$pixel_id  = isset( $pixel['id'] ) ? $pixel['id'] : '';
		$api_token = isset( $pixel['token'] ) ? $pixel['token'] : '';
		?>
		<div class="wpcode-settings-repeater-item" data-index="<?php echo esc_attr( $index ); ?>">
			<?php
			$this->metabox_row(
				__( 'Facebook Pixel ID', 'wpcode-pixel' ),
				$this->get_input_text(
					'facebook_pixels[id][' . $index . ']',
					$pixel_id,
					sprintf(
							// translators: %1$s and %2$s are the opening and closing anchor tags.
						__( 'You can find your Facebook Pixel ID in the Facebook Ads Manager. %1$sRead our step by step directions%2$s. ', 'insert-headers-and-footers' ),
						'<a target="_blank" href="' . wpcode_utm_url( 'https://wpcode.com/docs/how-to-find-your-facebook-pixel-id-and-conversions-api-token/', 'conversion-pixels', 'facebook', 'pixel' ) . '">',
						'</a>'
					)
				)
			);
			$this->metabox_row(
				__( 'Conversions API Token', 'wpcode-pixel' ),
				$this->get_input_text(
					'facebook_pixels[token][' . $index . ']',
					$api_token,
					__( 'The Conversions API token allows you to send API events that are more reliable and can improve audience targeting.', 'insert-headers-and-footers' ),
					true
				)
			);
			?>
			<div class="wpcode-settings-repeater-item-actions">
				<button type="button" class="wpcode-button wpcode-button-secondary wpcode-remove-repeater-item">
					<?php esc_html_e( 'Remove Facebook Pixel', 'wpcode-pixel' ); ?>
				</button>
			</div>
		</div>
		<?php
	}

	/**
	 * Listen for POSTed data and process it.
	 *
	 * @return void
	 */
	public function submit_listener() {

		// Don't allow users who can't change snippet output to save settings.
        if ( ! current_user_can( 'wpcode_manage_conversion_pixels' ) || ! isset( $_POST['wpcode-pixel-nonce'] ) ) {  // phpcs:ignore
			return;
		}

		if ( wp_verify_nonce( sanitize_key( $_POST['wpcode-pixel-nonce'] ), 'wpcode-save-facebook-pixel-data' ) ) {
			$facebook_pixel_events = isset( $_POST['facebook_pixel_events'] ) ? array_map( 'boolval', $_POST['facebook_pixel_events'] ) : array();
            $facebook_pixels_input = isset( $_POST['facebook_pixels'] ) && is_array( $_POST['facebook_pixels'] ) ? wp_unslash( $_POST['facebook_pixels'] ) : array(); // phpcs:ignore
			$facebook_pixels       = array();
			if ( ! empty( $facebook_pixels_input ) ) {
				$ids    = isset( $facebook_pixels_input['id'] ) && is_array( $facebook_pixels_input['id'] ) ? $facebook_pixels_input['id'] : array();
				$tokens = isset( $facebook_pixels_input['token'] ) && is_array( $facebook_pixels_input['token'] ) ? $facebook_pixels_input['token'] : array();
				$max    = max( count( $ids ), count( $tokens ) );
				for ( $i = 0; $i < $max; $i++ ) {
					$id    = isset( $ids[ $i ] ) ? sanitize_text_field( $ids[ $i ] ) : '';
					$token = isset( $tokens[ $i ] ) ? sanitize_text_field( $tokens[ $i ] ) : '';
					if ( '' === $id && '' === $token ) {
						continue;
					}
					$facebook_pixels[] = array(
						'id'    => $id,
						'token' => $token,
					);
				}
			}

			if ( ! empty( $facebook_pixels ) ) {
				$legacy_id    = $facebook_pixels[0]['id'];
				$legacy_token = $facebook_pixels[0]['token'];
			} else {
				// Fallback: accept single inputs if provided.
                $legacy_id    = isset( $_POST['facebook_pixel_id'] ) ? sanitize_text_field( $_POST['facebook_pixel_id'] ) : ''; // phpcs:ignore
                $legacy_token = isset( $_POST['facebook_pixel_api_token'] ) ? sanitize_text_field( $_POST['facebook_pixel_api_token'] ) : ''; // phpcs:ignore
				if ( ! empty( $legacy_id ) || ! empty( $legacy_token ) ) {
					$facebook_pixels[] = array(
						'id'    => $legacy_id,
						'token' => $legacy_token,
					);
				}
			}

			$validation_errors = $this->validate_facebook_pixels_for_duplicates( $facebook_pixels );
			if ( ! empty( $validation_errors ) ) {
				// Set error message but don't redirect - let client-side handle prevention.
				$this->set_error_message( implode( ' ', $validation_errors ) );
				// Only redirect if this is a direct server-side submission (fallback).
				if ( ! wp_doing_ajax() && ! isset( $_POST['_wpnonce'] ) ) {
					wp_safe_redirect(
						add_query_arg(
							array(
								'message' => 2,
							),
							$this->get_page_action_url()
						)
					);
					exit;
				}
				// For normal form submissions, return early to prevent saving.
				return;
			}

			wpcode()->settings->update_option( 'facebook_pixels', $facebook_pixels );
			wpcode()->settings->update_option( 'facebook_pixel_id', $legacy_id );
			wpcode()->settings->update_option( 'facebook_pixel_api_token', $legacy_token );
			wpcode()->settings->update_option( 'facebook_pixel_events', $facebook_pixel_events );
		}

		if ( wp_verify_nonce( sanitize_key( $_POST['wpcode-pixel-nonce'] ), 'wpcode-save-google-pixel-data' ) ) {
			$google_pixel_events = isset( $_POST['google_pixel_events'] ) ? array_map( 'boolval', $_POST['google_pixel_events'] ) : array();
            $google_pixels_input = isset( $_POST['google_pixels'] ) && is_array( $_POST['google_pixels'] ) ? wp_unslash( $_POST['google_pixels'] ) : array(); // phpcs:ignore
			$google_pixels       = array();
			if ( ! empty( $google_pixels_input ) ) {
				$ga_ids  = isset( $google_pixels_input['analytics_id'] ) && is_array( $google_pixels_input['analytics_id'] ) ? $google_pixels_input['analytics_id'] : array();
				$ads_ids = isset( $google_pixels_input['ads_id'] ) && is_array( $google_pixels_input['ads_id'] ) ? $google_pixels_input['ads_id'] : array();
				$labels  = isset( $google_pixels_input['label'] ) && is_array( $google_pixels_input['label'] ) ? $google_pixels_input['label'] : array();
				$max     = max( count( $ga_ids ), count( $ads_ids ), count( $labels ) );
				for ( $i = 0; $i < $max; $i++ ) {
					$ga_id  = isset( $ga_ids[ $i ] ) ? sanitize_text_field( $ga_ids[ $i ] ) : '';
					$ads_id = isset( $ads_ids[ $i ] ) ? sanitize_text_field( $ads_ids[ $i ] ) : '';
					$label  = isset( $labels[ $i ] ) ? sanitize_text_field( $labels[ $i ] ) : '';
					if ( '' === $ga_id && '' === $ads_id && '' === $label ) {
						continue;
					}
					$google_pixels[] = array(
						'analytics_id' => $ga_id,
						'ads_id'       => $ads_id,
						'label'        => $label,
					);
				}
			}

			if ( ! empty( $google_pixels ) ) {
				$legacy_ga_id  = $google_pixels[0]['analytics_id'];
				$legacy_ads_id = $google_pixels[0]['ads_id'];
				$legacy_label  = $google_pixels[0]['label'];
			} else {
				// Fallback: accept single inputs if provided.
                $legacy_ga_id  = isset( $_POST['google_analytics_id'] ) ? sanitize_text_field( $_POST['google_analytics_id'] ) : ''; // phpcs:ignore
                $legacy_ads_id = isset( $_POST['google_ads_id'] ) ? sanitize_text_field( $_POST['google_ads_id'] ) : ''; // phpcs:ignore
                $legacy_label  = isset( $_POST['google_ads_label'] ) ? sanitize_text_field( $_POST['google_ads_label'] ) : ''; // phpcs:ignore
				if ( ! empty( $legacy_ga_id ) || ! empty( $legacy_ads_id ) || ! empty( $legacy_label ) ) {
					$google_pixels[] = array(
						'analytics_id' => $legacy_ga_id,
						'ads_id'       => $legacy_ads_id,
						'label'        => $legacy_label,
					);
				}
			}
			$validation_errors = $this->validate_google_pixels_for_duplicates( $google_pixels );
			if ( ! empty( $validation_errors ) ) {
				// Set error message but don't redirect - let client-side handle prevention.
				$this->set_error_message( implode( ' ', $validation_errors ) );
				// Only redirect if this is a direct server-side submission (fallback).
				if ( ! wp_doing_ajax() && ! isset( $_POST['_wpnonce'] ) ) {
					wp_safe_redirect(
						add_query_arg(
							array(
								'message' => 2,
							),
							$this->get_page_action_url()
						)
					);
					exit;
				}
				// For normal form submissions, return early to prevent saving.
				return;
			}

			wpcode()->settings->update_option( 'google_pixels', $google_pixels );
			wpcode()->settings->update_option( 'google_analytics_id', $legacy_ga_id );
			wpcode()->settings->update_option( 'google_ads_id', $legacy_ads_id );
			wpcode()->settings->update_option( 'google_ads_label', $legacy_label );
			wpcode()->settings->update_option( 'google_pixel_events', $google_pixel_events );
		}

		if ( wp_verify_nonce( sanitize_key( $_POST['wpcode-pixel-nonce'] ), 'wpcode-save-pinterest-pixel-data' ) ) {
            $pinterest_id               = isset( $_POST['pinterest_id'] ) ? sanitize_text_field( $_POST['pinterest_id'] ) : ''; // phpcs:ignore
            $pinterest_ad_account_id    = isset( $_POST['pinterest_ad_account_id'] ) ? sanitize_text_field( $_POST['pinterest_ad_account_id'] ) : array(); // phpcs:ignore
            $pinterest_conversion_token = isset( $_POST['pinterest_conversion_token'] ) ? sanitize_text_field( $_POST['pinterest_conversion_token'] ) : array(); // phpcs:ignore
            $pinterest_pixel_events     = isset( $_POST['pinterest_pixel_events'] ) ? array_map( 'boolval', $_POST['pinterest_pixel_events'] ) : array(); // phpcs:ignore

			wpcode()->settings->update_option( 'pinterest_id', $pinterest_id );
			wpcode()->settings->update_option( 'pinterest_ad_account_id', $pinterest_ad_account_id );
			wpcode()->settings->update_option( 'pinterest_conversion_token', $pinterest_conversion_token );
			wpcode()->settings->update_option( 'pinterest_pixel_events', $pinterest_pixel_events );
		}

		if ( wp_verify_nonce( sanitize_key( $_POST['wpcode-pixel-nonce'] ), 'wpcode-save-tiktok-pixel-data' ) ) {
            $tiktok_pixel_id     = isset( $_POST['tiktok_pixel_id'] ) ? sanitize_text_field( $_POST['tiktok_pixel_id'] ) : ''; // phpcs:ignore
            $tiktok_access_token = isset( $_POST['tiktok_access_token'] ) ? sanitize_text_field( $_POST['tiktok_access_token'] ) : ''; // phpcs:ignore
			$tiktok_pixel_events = isset( $_POST['tiktok_pixel_events'] ) ? array_map( 'boolval', $_POST['tiktok_pixel_events'] ) : array();

			wpcode()->settings->update_option( 'tiktok_pixel_id', $tiktok_pixel_id );
			wpcode()->settings->update_option( 'tiktok_access_token', $tiktok_access_token );
			wpcode()->settings->update_option( 'tiktok_pixel_events', $tiktok_pixel_events );
		}

		if ( wp_verify_nonce( sanitize_key( $_POST['wpcode-pixel-nonce'] ), 'wpcode-save-snapchat-pixel-data' ) ) {
            $tiktok_pixel_id     = isset( $_POST['snapchat_pixel_id'] ) ? sanitize_text_field( $_POST['snapchat_pixel_id'] ) : ''; // phpcs:ignore
            $tiktok_access_token = isset( $_POST['snapchat_pixel_api_token'] ) ? sanitize_text_field( $_POST['snapchat_pixel_api_token'] ) : ''; // phpcs:ignore
			$tiktok_pixel_events = isset( $_POST['snapchat_pixel_events'] ) ? array_map( 'boolval', $_POST['snapchat_pixel_events'] ) : array();

			wpcode()->settings->update_option( 'snapchat_pixel_id', $tiktok_pixel_id );
			wpcode()->settings->update_option( 'snapchat_pixel_api_token', $tiktok_access_token );
			wpcode()->settings->update_option( 'snapchat_pixel_events', $tiktok_pixel_events );
		}

		if ( wp_verify_nonce( sanitize_key( $_POST['wpcode-pixel-nonce'] ), 'wpcode-save-custom-events' ) ) {
			// Detect if WPForms is active so we can preserve WPForms-specific events
			// even when the plugin is inactive (they are hidden from the UI then).
			if ( ! function_exists( 'is_plugin_active' ) ) {
				$plugin_include = ABSPATH . 'wp-admin/includes/plugin.php';
				if ( file_exists( $plugin_include ) ) {
					include_once $plugin_include; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
				}
			}
			$wpforms_active = function_exists( 'is_plugin_active' )
				? ( is_plugin_active( 'wpforms-lite/wpforms.php' ) || is_plugin_active( 'wpforms/wpforms.php' ) )
				: ( function_exists( 'wpforms' ) || class_exists( '\\WPForms\\WPForms' ) );

			$custom_events_input = isset( $_POST['custom_events'] ) && is_array( $_POST['custom_events'] ) ? wp_unslash( $_POST['custom_events'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

			$possible_pixels = array();
			foreach ( wpcode_pixel()->auto_insert->types as $type ) {
				$possible_pixels[] = $type->type;
			}

			$pixels_data   = isset( $_POST['pixels'] ) ? wp_unslash( $_POST['pixels'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
			$custom_events = array();

			// When WPForms is not active, we want to keep any existing WPForms custom events
			// in the database even though they are hidden from the UI and not submitted.
			$preserved_wpforms_events = array();
			if ( ! $wpforms_active ) {
				$existing_events = $this->get_option( 'custom_events', array() );
				if ( is_array( $existing_events ) ) {
					foreach ( $existing_events as $existing_event ) {
						if ( isset( $existing_event['trigger_type'] ) && 'wpforms_form' === $existing_event['trigger_type'] ) {
							$preserved_wpforms_events[] = $existing_event;
						}
					}
				}
			}

			foreach ( $custom_events_input as $index => $event ) {
				if ( ! is_array( $event ) ) {
					continue;
				}

				$internal_name = isset( $event['internal_name'] ) ? sanitize_text_field( $event['internal_name'] ) : '';
				$trigger_type  = isset( $event['trigger_type'] ) ? sanitize_key( $event['trigger_type'] ) : 'home_page';

				// Skip completely empty rows.
				if ( '' === $internal_name && empty( $trigger_type ) ) {
					continue;
				}

				$sanitized                  = array();
				$sanitized['internal_name'] = $internal_name;
				$sanitized['value']         = isset( $event['value'] ) ? sanitize_text_field( $event['value'] ) : '';
				$sanitized['trigger_type']  = $trigger_type;
				$sanitized['delay']         = isset( $event['delay'] ) ? max( 0, intval( $event['delay'] ) ) : 0;

				// Scroll position triggers.
				$sanitized['scroll_pos_triggers'] = array();
				if ( isset( $event['scroll_pos_triggers'] ) && is_array( $event['scroll_pos_triggers'] ) ) {
					foreach ( $event['scroll_pos_triggers'] as $scroll_trigger ) {
						if ( ! is_array( $scroll_trigger ) ) {
							continue;
						}
						$val                                = isset( $scroll_trigger['value'] ) ? intval( $scroll_trigger['value'] ) : 0;
						$val                                = max( 0, min( 100, $val ) );
						$sanitized['scroll_pos_triggers'][] = array(
							'value' => $val,
						);
					}
				}

				// Page scroll URL scope (optional).
				// Allows limiting scroll events to:
				// - any: fire on any page.
				// - contains / notcontains / = / != : based on current URL.
				$allowed_scroll_relations = array( 'any', 'contains', 'notcontains', '=', '!=' );
				// Use sanitize_text_field so operators like "=" and "!=" are preserved.
				$scroll_relation = isset( $event['scroll_page_relation'] ) ? sanitize_text_field( $event['scroll_page_relation'] ) : 'any';
				$scroll_value    = isset( $event['scroll_page_value'] ) ? sanitize_text_field( $event['scroll_page_value'] ) : '';

				if ( ! in_array( $scroll_relation, $allowed_scroll_relations, true ) ) {
					$scroll_relation = 'any';
				}

				if ( 'any' === $scroll_relation ) {
					// "Any page" – ignore any provided value.
					$sanitized['scroll_page_relation'] = 'any';
					$sanitized['scroll_page_value']    = '';
				} elseif ( '' === $scroll_value ) {
						// If a specific relation is chosen but no value is provided,
						// fall back to "any page" to avoid broken configs.
						$sanitized['scroll_page_relation'] = 'any';
						$sanitized['scroll_page_value']    = '';
				} else {
					$sanitized['scroll_page_relation'] = $scroll_relation;
					$sanitized['scroll_page_value']    = $scroll_value;
				}

				// Post type trigger.
				// Allow any registered public post type + "any".
				$registered_post_types = get_post_types(
					array(
						'public' => true,
					),
					'names'
				);
				if ( ! is_array( $registered_post_types ) ) {
					$registered_post_types = array();
				}
				$allowed_post_types           = array_merge( array( 'any' ), array_values( $registered_post_types ) );
				$post_type_value              = isset( $event['post_type_value'] ) ? sanitize_key( $event['post_type_value'] ) : 'any';
				$sanitized['post_type_value'] = in_array( $post_type_value, $allowed_post_types, true ) ? $post_type_value : 'any';

				// Click on HTML link trigger (URL rules + optional overrides).
				$sanitized['url_click_triggers'] = array();
				if ( isset( $event['url_click_triggers'] ) && is_array( $event['url_click_triggers'] ) ) {
					foreach ( $event['url_click_triggers'] as $url_trigger ) {
						if ( ! is_array( $url_trigger ) ) {
							continue;
						}
						// Use sanitize_text_field so operators like "=" and "!=" are preserved.
						$relation = isset( $url_trigger['relation'] ) ? sanitize_text_field( $url_trigger['relation'] ) : 'contains';
						$value    = isset( $url_trigger['value'] ) ? sanitize_text_field( $url_trigger['value'] ) : '';

						if ( '' === $value ) {
							continue;
						}

						// Only allow a subset of the available conditional operators.
						$allowed_relations = array( 'contains', 'notcontains', '=', '!=' );
						if ( ! in_array( $relation, $allowed_relations, true ) ) {
							$relation = 'contains';
						}

						$sanitized['url_click_triggers'][] = array(
							'relation' => $relation,
							'value'    => $value,
						);
					}
				}

				$sanitized['url_click_label'] = isset( $event['url_click_label'] ) ? sanitize_text_field( $event['url_click_label'] ) : '';
				$sanitized['url_click_value'] = isset( $event['url_click_value'] ) ? sanitize_text_field( $event['url_click_value'] ) : '';

				// Email link triggers.
				$sanitized['email_link_triggers'] = array();
				if ( isset( $event['email_link_triggers'] ) && is_array( $event['email_link_triggers'] ) ) {
					foreach ( $event['email_link_triggers'] as $email_trigger ) {
						if ( ! is_array( $email_trigger ) ) {
							continue;
						}
						$rule  = isset( $email_trigger['rule'] ) ? sanitize_key( $email_trigger['rule'] ) : 'match';
						$value = isset( $email_trigger['value'] ) ? sanitize_text_field( $email_trigger['value'] ) : '';

						// "any" rule does not require a value – it means any mailto: link.
						if ( 'any' !== $rule && '' === $value ) {
							continue;
						}

						if ( ! in_array( $rule, array( 'match', 'contains', 'any' ), true ) ) {
							$rule = 'match';
						}

						$sanitized['email_link_triggers'][] = array(
							'rule'  => $rule,
							'value' => $value,
						);
					}
				}

				// Embedded video view trigger.
				$sanitized['video_view_urls']         = isset( $event['video_view_urls'] ) ? sanitize_text_field( $event['video_view_urls'] ) : '';
				$allowed_video_triggers               = array( '0%', '10%', '50%', '90%', '100%' );
				$video_play_trigger                   = isset( $event['video_view_play_trigger'] ) ? sanitize_text_field( $event['video_view_play_trigger'] ) : '50%';
				$sanitized['video_view_play_trigger'] = in_array( $video_play_trigger, $allowed_video_triggers, true ) ? $video_play_trigger : '50%';

				// Form field trigger.
				$sanitized['form_field_urls']        = isset( $event['form_field_urls'] ) ? sanitize_text_field( $event['form_field_urls'] ) : '';
				$sanitized['form_field_field_names'] = isset( $event['form_field_field_names'] ) ? sanitize_text_field( $event['form_field_field_names'] ) : '';

				// CSS mouseover selectors.
				$sanitized['css_mouseover_selectors'] = array();
				if ( isset( $event['css_mouseover_selectors'] ) && is_array( $event['css_mouseover_selectors'] ) ) {
					foreach ( $event['css_mouseover_selectors'] as $selector ) {
						$selector = sanitize_text_field( $selector );
						if ( '' === $selector ) {
							continue;
						}
						$sanitized['css_mouseover_selectors'][] = $selector;
					}
				}

				// Click on HTML element (CSS selector) trigger.
				$sanitized['css_click_selectors'] = array();
				if ( isset( $event['css_click_selectors'] ) && is_array( $event['css_click_selectors'] ) ) {
					foreach ( $event['css_click_selectors'] as $selector ) {
						$selector = sanitize_text_field( $selector );
						if ( '' === $selector ) {
							continue;
						}
						$sanitized['css_click_selectors'][] = $selector;
					}
				}

				// WPForms trigger: store selected form ID (0 = none).
				$sanitized['wpforms_form_id'] = isset( $event['wpforms_form_id'] ) ? absint( $event['wpforms_form_id'] ) : 0;

				// Number of page visits trigger.
				// URL scope options:
				// - any:    count visits across all URLs.
				// - contains: count visits only for URLs that contain the given pattern.
				// - match:  count visits only for URLs that exactly match the given pattern.
				$allowed_scopes                       = array( 'any', 'contains', 'match' );
				$scope                                = isset( $event['number_page_visit_scope'] ) ? sanitize_key( $event['number_page_visit_scope'] ) : 'any';
				$sanitized['number_page_visit_scope'] = in_array( $scope, $allowed_scopes, true ) ? $scope : 'any';

				// For "Any URLs" we ignore the pattern entirely.
				if ( 'any' === $sanitized['number_page_visit_scope'] ) {
					$sanitized['number_page_visit_pattern'] = '';
				} else {
					$sanitized['number_page_visit_pattern'] = isset( $event['number_page_visit_pattern'] ) ? sanitize_text_field( $event['number_page_visit_pattern'] ) : '';
				}

				$allowed_conditions                       = array( '=', '>', '<', '>=', '<=' );
				$condition                                = isset( $event['number_page_visit_condition'] ) ? sanitize_text_field( $event['number_page_visit_condition'] ) : '=';
				$sanitized['number_page_visit_condition'] = in_array( $condition, $allowed_conditions, true ) ? $condition : '=';
				$sanitized['number_page_visit_value']     = isset( $event['number_page_visit_value'] ) ? max( 1, intval( $event['number_page_visit_value'] ) ) : 1;

				// Pixels: map posted pixels data for this index to platforms + labels.
				$sanitized['platforms'] = array();
				$sanitized['labels']    = array();

				if ( isset( $pixels_data[ $index ] ) && is_array( $pixels_data[ $index ] ) ) {
					$pixel_row = array_map( 'sanitize_text_field', $pixels_data[ $index ] );

					foreach ( $possible_pixels as $pixel ) {
						if ( ! empty( $pixel_row[ $pixel ] ) ) {
							$sanitized['platforms'][] = $pixel;
						}
						if ( ! empty( $pixel_row[ $pixel . '_event_name' ] ) ) {
							$sanitized['labels'][ $pixel ] = $pixel_row[ $pixel . '_event_name' ];
						}
					}
				}

				$custom_events[] = $sanitized;
			}

			// If WPForms is inactive, append preserved WPForms events so they remain stored
			// but stay hidden from the UI until WPForms is activated again.
			if ( ! $wpforms_active && ! empty( $preserved_wpforms_events ) ) {
				$custom_events = array_merge( $custom_events, $preserved_wpforms_events );
			}

			wpcode()->settings->update_option( 'custom_events', $custom_events );
		}

		if ( function_exists( 'wpcode_clear_all_plugins_page_cache' ) ) {
			wpcode_clear_all_plugins_page_cache( 'pixels' );
		}

		wp_safe_redirect(
			add_query_arg(
				array(
					'message' => 1,
				),
				$this->get_page_action_url()
			)
		);
		exit;
	}

	/**
	 * Override the main class get option method to actually load data in the addon.
	 *
	 * @param string $key
	 * @param mixed  $default
	 *
	 * @return mixed
	 */
	public function get_option( $key, $default = false ) {
		return wpcode()->settings->get_option( $key, $default );
	}

	/**
	 * Add a simple js check to see if the user is using an adblocker and if so
	 * display a notice that they might have trouble confirming the pixels are
	 * loaded correctly and should whitelist their site.
	 *
	 * @return void
	 */
	public function adblocker_notice_js() {
		$adex_src = WPCODE_PIXEL_PLUGIN_URL . 'assets/adex.js';
		?>
		<script src="<?php echo esc_url( $adex_src ); ?>" type="text/javascript"></script>
		<script type="text/javascript">
			const adblocker_notice = document.getElementById( 'wpcode-adblocker-notice' );
			if ( document.getElementById( 'MepWv8eL' ) ) {

			} else {
				adblocker_notice.style = 'display:block';
			}
		</script>
		<?php
	}

	/**
	 * Display a simple notice if an adblocker is detected.
	 *
	 * @return void
	 */
	public function adblocker_notice() {
		?>
		<div class="wpcode-alert wpcode-alert-warning" id="wpcode-adblocker-notice" style="display:none">
			<p><?php esc_html_e( 'It looks like you are using an ad blocker. If you are having trouble confirming your pixels are loaded correctly, please whitelist this site or disable the adblocker.', 'wpcode-pixel' ); ?></p>
		</div>
		<?php
	}

	/**
	 * Whether we have a supported eCommerce plugin installed.
	 *
	 * @return array
	 */
	public function ecommerce_available() {
		$available_providers = array();

		foreach ( wpcode_pixel()->auto_insert->providers as $provider ) {
			$available_providers[] = $provider->get_name();
		}

		return $available_providers;
	}

	/**
	 * Let's see if the enabled providers have any unsupported events.
	 *
	 * @param string $class The admin body class.
	 *
	 * @return string
	 */
	public function maybe_add_body_classes( $class ) {

		$unsupported_events = array();
		$first              = true;
		foreach ( wpcode_pixel()->auto_insert->providers as $provider ) {
			if ( empty( $unsupported_events ) && true === $first ) {
				$unsupported_events = array_merge( $unsupported_events, $provider->unsupported_events );
			} else {
				// Keep just the overlap of the unsupported events.
				$unsupported_events = array_intersect( $unsupported_events, $provider->unsupported_events );
			}
			$first = false;
		}
		foreach ( $unsupported_events as $event ) {
			$class .= ' wpcode-pixel-unsupported-' . $event;
		}

		return $class;
	}

	/**
	 * Page for Google Analytics & Ads pixel settings.
	 *
	 * @return void
	 */
	public function output_view_google() {
		$google_pixels = $this->get_option( 'google_pixels', array() );
		if ( empty( $google_pixels ) ) {
			$google_pixels = array(
				array(
					'analytics_id' => $this->get_option( 'google_analytics_id', '' ),
					'ads_id'       => $this->get_option( 'google_ads_id', '' ),
					'label'        => $this->get_option( 'google_ads_label', '' ),
				),
			);
		}
		?>
		<h2><?php esc_html_e( 'Google Analytics & Ads Tracking', 'wpcode-pixel' ); ?></h2>

		<div id="wpcode-google-pixels-repeater" class="wpcode-settings-repeater-group">
			<?php
			foreach ( $google_pixels as $index => $pixel ) {
				$this->google_pixel_row( $index, $pixel );
			}
			?>
		</div>
		<button type="button" class="wpcode-button wpcode-button-secondary wpcode-add-pixel"" id="wpcode-add-google-pixel">
		<?php esc_html_e( 'Add Google ID', 'wpcode-pixel' ); ?>
		</button>
		<?php
		$this->metabox_row(
			__( 'Google Pixel Events', 'wpcode-pixel' ),
			$this->get_checkbox_inputs(
				array(
					array(
						'label'       => __( 'Page View Event', 'wpcode-pixel' ),
						'name'        => 'page_view',
						'description' => __( 'Enable the page_view event to track and record page visits on all the pages of your website using Google Analytics.', 'wpcode-pixel' ),
						'ecommerce'   => false,
					),
				),
				'google_pixel_events'
			)
		);
		$this->metabox_row(
			__( 'eCommerce Events Tracking', 'wpcode-pixel' ),
			$this->get_ecommerce_events_input() . $this->get_checkbox_inputs( $this->get_google_pixel_events_inputs(), 'google_pixel_events' )
		);
		wp_nonce_field( 'wpcode-save-google-pixel-data', 'wpcode-pixel-nonce' );
		?>
		<button type="submit" class="wpcode-button">
			<?php esc_html_e( 'Save Changes', 'wpcode-pixel' ); ?>
		</button>
		<script id="wpcode-google-pixel-row-template" type="text/template">
			<?php $this->google_pixel_row( '{index}' ); ?>
		</script>
		<?php
	}

	/**
	 * Page for Snapchat pixel settings.
	 *
	 * @return void
	 */
	public function output_view_snapchat() {
		?>
		<h2><?php esc_html_e( 'Snapchat Pixel', 'wpcode-pixel' ); ?></h2>
		<?php
		$this->metabox_row(
			__( 'Snap Pixel ID', 'wpcode-pixel' ),
			$this->get_input_text(
				'snapchat_pixel_id',
				$this->get_option( 'snapchat_pixel_id', '' ),
				sprintf(
						// translators: %1$s and %2$s are the opening and closing anchor tags.
					__( 'You can find your Snapchat Pixel ID in the Snapchat Ads Manager. %1$sRead our step by step directions%2$s. ', 'wpcode-pixel' ),
					'<a target="_blank" href="' . wpcode_utm_url( 'https://wpcode.com/docs/how-to-find-your-snapchat-pixel-id-and-conversions-api-token/', 'conversion-pixels', 'snapchat', 'pixel' ) . '">',
					'</a>'
				),
				true
			),
			'snapchat_pixel_id'
		);
		$this->metabox_row(
			__( 'Conversions API Token', 'wpcode-pixel' ),
			$this->get_input_text(
				'snapchat_pixel_api_token',
				$this->get_option( 'snapchat_pixel_api_token', '' ),
				__( 'The Conversions API token allows you to send API events that are more reliable and can improve audience targeting.', 'wpcode-pixel' ),
				true
			),
			'snapchat_pixel_api_token'
		);
		$this->metabox_row(
			__( 'Snapchat Pixel Events', 'wpcode-pixel' ),
			$this->get_checkbox_inputs(
				array(
					array(
						'label'       => __( 'Pave View Event', 'wpcode-pixel' ),
						'name'        => 'page_view',
						'description' => __( 'Enable the PAGE_VIEW event to track and record page visits on all the pages of your website using the Snapchat Pixel.', 'wpcode-pixel' ),
						'ecommerce'   => false,
					),
				),
				'snapchat_pixel_events'
			)
		);
		$this->metabox_row(
			__( 'eCommerce Events Tracking', 'wpcode-pixel' ),
			$this->get_ecommerce_events_input() . $this->get_checkbox_inputs( $this->get_snap_pixel_events_inputs(), 'snapchat_pixel_events' )
		);
		wp_nonce_field( 'wpcode-save-snapchat-pixel-data', 'wpcode-pixel-nonce' );
		?>
		<button type="submit" class="wpcode-button">
			<?php esc_html_e( 'Save Changes', 'wpcode-pixel' ); ?>
		</button>
		<?php
	}

	/**
	 * Output the HTML for a single Google pixel row.
	 *
	 * @param string|int $index The index of the row.
	 * @param array      $pixel The pixel data.
	 *
	 * @return void
	 */
	public function google_pixel_row( $index, $pixel = array() ) {
		$analytics_id = isset( $pixel['analytics_id'] ) ? $pixel['analytics_id'] : '';
		$ads_id       = isset( $pixel['ads_id'] ) ? $pixel['ads_id'] : '';
		$label        = isset( $pixel['label'] ) ? $pixel['label'] : '';
		?>
		<div class="wpcode-settings-repeater-item" data-index="<?php echo esc_attr( $index ); ?>">
			<?php
			$this->metabox_row(
				__( 'Google Analytics ID', 'wpcode-pixel' ),
				$this->get_input_text(
					'google_pixels[analytics_id][' . $index . ']',
					$analytics_id,
					sprintf(
							// translators: %1$s and %2$s are the opening and closing anchor tags.
						__( 'You can find your Google Analytics ID in the Google Analytics Admin panel. %1$sRead our step by step directions%2$s. ', 'insert-headers-and-footers' ),
						'<a target="_blank" href="' . wpcode_utm_url( 'https://wpcode.com/docs/how-to-find-your-google-analytics-id/', 'conversion-pixels', 'google', 'pixel' ) . '">',
						'</a>'
					)
				)
			);
			$this->metabox_row(
				__( 'Google Ads Tag ID', 'wpcode-pixel' ),
				$this->get_input_text(
					'google_pixels[ads_id][' . $index . ']',
					$ads_id,
					sprintf(
							// translators: %1$s and %2$s are the opening and closing anchor tags.
						__( 'You can find your Google Ads Tag ID in the Google Ads Settings under Google Tag. %1$sRead our step by step directions%2$s. ', 'insert-headers-and-footers' ),
						'<a target="_blank" href="' . wpcode_utm_url( 'https://wpcode.com/docs/how-to-find-your-google-ads-tag-id/', 'conversion-pixels', 'google', 'pixel' ) . '">',
						'</a>'
					)
				)
			);
			$this->metabox_row(
				__( 'Ads Conversion Label', 'wpcode-pixel' ),
				$this->get_input_text(
					'google_pixels[label][' . $index . ']',
					$label,
					sprintf(
							// translators: %1$s and %2$s are the opening and closing anchor tags.
						__( 'Add your Google Ads Conversion Label for tracking conversion events. %1$sLearn More%2$s.', 'insert-headers-and-footers' ),
						'<a target="_blank" href="' . wpcode_utm_url( 'https://wpcode.com/docs/how-to-find-your-google-ads-tag-id/', 'conversion-pixels', 'google', 'pixel' ) . '">',
						'</a>'
					)
				)
			);
			?>
			<div class="wpcode-settings-repeater-item-actions">
				<button type="button" class="wpcode-button wpcode-button-secondary wpcode-remove-repeater-item">
					<?php esc_html_e( 'Remove Google ID', 'wpcode-pixel' ); ?>
				</button>
			</div>
		</div>
		<?php
	}

	/**
	 * Event options checkboxes for the Google Pixel.
	 *
	 * @return array[]
	 */
	public function get_google_pixel_events_inputs() {
		return array(
			array(
				'label'       => __( 'View Item Event', 'wpcode-pixel' ),
				'name'        => 'view_item',
				'description' => __( 'Turn on the "view_item" event to track views of product pages on your website.', 'wpcode-pixel' ),
				'ecommerce'   => true,
				'css_class'   => 'view-content',
			),
			array(
				'label'       => __( 'Add to Cart Event', 'wpcode-pixel' ),
				'name'        => 'add_to_cart',
				'description' => __( 'Turn on the "add_to_cart" event to track when items are added to a shopping cart on your website.', 'wpcode-pixel' ),
				'ecommerce'   => true,
				'css_class'   => 'add-to-cart',
			),
			array(
				'label'       => __( 'Begin Checkout Event', 'wpcode-pixel' ),
				'name'        => 'begin_checkout',
				'description' => __( 'Turn on the "begin_checkout" event to track when a user reaches the checkout page on your website.', 'wpcode-pixel' ),
				'ecommerce'   => true,
				'css_class'   => 'begin-checkout',
			),
			array(
				'label'       => __( 'Purchase Event', 'wpcode-pixel' ),
				'name'        => 'purchase',
				'description' => __( 'Turn on the "purchase" event to track successful purchases on your website.', 'wpcode-pixel' ),
				'ecommerce'   => true,
				'css_class'   => 'purchase',
			),
			array(
				'label'       => __( 'Conversion Event', 'wpcode-pixel' ),
				'name'        => 'conversion',
				'description' => __( 'Turn on the "conversion" event to track Google Ads conversions using the conversion label.', 'wpcode-pixel' ),
				'ecommerce'   => true,
				'css_class'   => 'conversion',
			),
		);
	}

	/**
	 * Event options checkboxes for the Snapchat Pixel.
	 *
	 * @return array[]
	 */
	public function get_snap_pixel_events_inputs() {
		return array(
			array(
				'label'       => __( 'View Content Event', 'wpcode-pixel' ),
				'name'        => 'view_content',
				'description' => __( 'Turn on the "VIEW_CONTENT" event to track views of product pages on your website.', 'wpcode-pixel' ),
				'ecommerce'   => true,
				'css_class'   => 'view-content',
			),
			array(
				'label'       => __( 'Add to Cart Event', 'wpcode-pixel' ),
				'name'        => 'add_to_cart',
				'description' => __( 'Turn on the "ADD_CART" event to track when items are added to a shopping cart on your website.', 'wpcode-pixel' ),
				'ecommerce'   => true,
				'css_class'   => 'add-to-cart',
			),
			array(
				'label'       => __( 'Start Checkout Event', 'wpcode-pixel' ),
				'name'        => 'begin_checkout',
				'description' => __( 'Turn on the "START_CHECKOUT" event to track when a user reaches the checkout page on your website.', 'wpcode-pixel' ),
				'ecommerce'   => true,
				'css_class'   => 'begin-checkout',
			),
			array(
				'label'       => __( 'Purchase Event', 'wpcode-pixel' ),
				'name'        => 'purchase',
				'description' => __( 'Turn on the "PURCHASE" event to track successful purchases on your website.', 'wpcode-pixel' ),
				'ecommerce'   => true,
				'css_class'   => 'purchase',
			),
		);
	}

	/**
	 * This is the page content for the Custom Events page.
	 *
	 * @return void
	 */
	public function output_view_custom_event_tracking() {
		// Detect if WPForms is active so we can hide WPForms-specific events & UI when it's not.
		if ( ! function_exists( 'is_plugin_active' ) ) {
			$plugin_include = ABSPATH . 'wp-admin/includes/plugin.php';
			if ( file_exists( $plugin_include ) ) {
				include_once $plugin_include; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
			}
		}
		$wpforms_active = function_exists( 'is_plugin_active' )
			? ( is_plugin_active( 'wpforms-lite/wpforms.php' ) || is_plugin_active( 'wpforms/wpforms.php' ) )
			: ( function_exists( 'wpforms' ) || class_exists( '\\WPForms\\WPForms' ) );

		// One-time migration: move legacy click tracking rules into Custom Events
		// as "Click on HTML element" triggers, preserving existing settings.
		$this->maybe_migrate_clicks_to_custom_events();

		$custom_events = $this->get_option(
			'custom_events',
			array(
				array(
					'internal_name'               => '',
					'value'                       => '',
					'trigger_type'                => 'home_page',
					'delay'                       => 0,
					'scroll_pos_triggers'         => array(
						array(
							'value' => 50,
						),
					),
					'post_type_value'             => 'any',
					'url_click_triggers'          => array(
						array(
							'relation' => 'contains',
							'value'    => '',
						),
					),
					'url_click_label'             => '',
					'url_click_value'             => '',
					'email_link_triggers'         => array(
						array(
							'rule'  => 'match',
							'value' => '',
						),
					),
					'video_view_urls'             => '',
					'video_view_play_trigger'     => '50%',
					'form_field_urls'             => '',
					'form_field_field_names'      => '',
					'css_mouseover_selectors'     => array( '' ),
					// WPForms integration: specific form ID to track (0 = none).
					'wpforms_form_id'             => 0,
					// Page scroll URL scope defaults.
					'scroll_page_relation'        => 'any',
					'scroll_page_value'           => '',
					'number_page_visit_scope'     => 'any',
					'number_page_visit_pattern'   => '',
					'number_page_visit_condition' => '=',
					'number_page_visit_value'     => 3,
					'platforms'                   => array(),
					'labels'                      => array(),
				),
			)
		);
		?>
		<h2><?php esc_html_e( 'Custom Events', 'wpcode-pixel' ); ?></h2>
		<p>
			<?php
			esc_html_e(
				'Create custom events that can fire based on different trigger types (page views, scroll depth, clicks, forms, and more). Each event can send data to one or more pixels.',
				'wpcode-pixel'
			);
			?>
		</p>
		<div id="wpcode-custom-events-repeater" class="wpcode-settings-repeater-group wpcode-custom-events-repeater">
			<?php
			foreach ( $custom_events as $index => $event ) {
				// When WPForms is not active, hide WPForms-specific custom events from the UI
				// so they don't appear as "Home page" or get edited accidentally.
				if ( ! $wpforms_active && isset( $event['trigger_type'] ) && 'wpforms_form' === $event['trigger_type'] ) {
					continue;
				}
				$this->custom_event_row( $index, $event );
			}
			?>
		</div>
		<button type="button" class="wpcode-button wpcode-button-secondary" id="wpcode-add-custom-event">
			<?php esc_html_e( 'Add New Event', 'wpcode-pixel' ); ?>
		</button>
		<?php
		wp_nonce_field( 'wpcode-save-custom-events', 'wpcode-pixel-nonce' );
		?>
		<button type="submit" class="wpcode-button">
			<?php esc_html_e( 'Save Changes', 'wpcode-pixel' ); ?>
		</button>
		<script id="wpcode-custom-event-row-template" type="text/template">
			<?php $this->custom_event_row( '{index}' ); ?>
		</script>
		<?php
	}

	/**
	 * One-time migration of legacy click tracking rules into Custom Events.
	 *
	 * @return void
	 */
	private function maybe_migrate_clicks_to_custom_events() {
		$clicks_data = $this->get_option( 'clicks_data', array() );
		if ( empty( $clicks_data ) || ! is_array( $clicks_data ) ) {
			// Nothing to migrate, but mark as done so we don't repeatedly check.
			wpcode()->settings->update_option( 'clicks_migrated_to_custom_events', true );
			return;
		}

		$custom_events = $this->get_option( 'custom_events', array() );
		if ( ! is_array( $custom_events ) ) {
			$custom_events = array();
		}

		$migrated = $this->get_option( 'clicks_migrated_to_custom_events', false );
		// If migration has already run once, never re-run it, even if the user deletes
		// the migrated css_click events later. This prevents deleted migrated events
		// from being re-created from the legacy clicks_data on each page load.
		// In that case, we can safely clear the legacy clicks_data so it doesn't linger
		// in the database.
		if ( $migrated ) {
			if ( ! empty( $clicks_data ) ) {
				wpcode()->settings->update_option( 'clicks_data', array() );
			}
			return;
		}

		foreach ( $clicks_data as $click ) {
			if ( empty( $click['selector'] ) ) {
				continue;
			}

			$selector      = sanitize_text_field( $click['selector'] );
			$internal_name = isset( $click['label'] ) ? sanitize_text_field( $click['label'] ) : '';
			$value         = isset( $click['value'] ) ? sanitize_text_field( $click['value'] ) : '';
			$platforms     = isset( $click['platforms'] ) && is_array( $click['platforms'] ) ? $click['platforms'] : array();
			$labels        = isset( $click['labels'] ) && is_array( $click['labels'] ) ? $click['labels'] : array();

			// Avoid duplicating events if migration is re-run: skip if an identical css_click event already exists.
			$duplicate = false;
			foreach ( $custom_events as $event ) {
				if (
					isset( $event['trigger_type'], $event['css_click_selectors'], $event['internal_name'] )
					&& 'css_click' === $event['trigger_type']
					&& in_array( $selector, (array) $event['css_click_selectors'], true )
					&& $event['internal_name'] === $internal_name
				) {
					$duplicate = true;
					break;
				}
			}

			if ( $duplicate ) {
				continue;
			}

			$custom_events[] = array(
				'internal_name'               => $internal_name,
				'value'                       => $value,
				'trigger_type'                => 'css_click',
				'delay'                       => 0,
				'scroll_pos_triggers'         => array(
					array(
						'value' => 50,
					),
				),
				'post_type_value'             => 'any',
				'url_click_triggers'          => array(
					array(
						'relation' => 'contains',
						'value'    => '',
					),
				),
				'url_click_label'             => '',
				'url_click_value'             => '',
				'email_link_triggers'         => array(
					array(
						'rule'  => 'match',
						'value' => '',
					),
				),
				'video_view_urls'             => '',
				'video_view_play_trigger'     => '50%',
				'form_field_urls'             => '',
				'form_field_field_names'      => '',
				'css_mouseover_selectors'     => array( '' ),
				'css_click_selectors'         => array( $selector ),
				'wpforms_form_id'             => 0,
				'scroll_page_relation'        => 'any',
				'scroll_page_value'           => '',
				'number_page_visit_scope'     => 'any',
				'number_page_visit_pattern'   => '',
				'number_page_visit_condition' => '=',
				'number_page_visit_value'     => 3,
				'platforms'                   => $platforms,
				'labels'                      => $labels,
			);
		}

		wpcode()->settings->update_option( 'custom_events', $custom_events );
		// Once migration is complete, clear the legacy clicks_data so migrated events
		// only live inside custom_events and old click tracking data is removed.
		wpcode()->settings->update_option( 'clicks_data', array() );
		wpcode()->settings->update_option( 'clicks_migrated_to_custom_events', true );
	}


	/**
	 * Built the inputs for the pixel options.
	 *
	 * @param array $values The values.
	 * @param int   $index The index of the current repeater being output.
	 *
	 * @return string
	 */
	public function get_pixels_inputs( $values, $index, $labels = array() ) {
		$available_pixels = wpcode_pixel()->auto_insert->types;

		$markup  = '<p>';
		$markup .= __( 'Choose which pixels to enable for this event. ', 'wpcode-pixel' );
		$markup .= '</p>';
		$markup .= '<p>';
		$markup .= __( 'You can choose a standard event for each pixel to override the custom event label set above.', 'wpcode-pixel' );
		$markup .= '</p>';
		/**
		 *  The pixel type.
		 *
		 * @var WPCode_Pixel_Auto_Insert_Type $pixel
		 */
		foreach ( $available_pixels as $pixel ) {
			$checked = in_array( $pixel->type, $values, true );

			$markup .= '<div class="wpcode-checkbox-row">';
			$markup .= $this->get_checkbox_toggle(
				$checked,
				'pixels[' . $index . '][' . $pixel->type . ']',
				'',
				1,
				$pixel->name
			);

			$standard_events      = $pixel->get_standard_events();
			$standard_event_value = isset( $labels[ $pixel->type ] ) ? $labels[ $pixel->type ] : '';
			if ( ! empty( $standard_events ) ) {
				// Let's add a select for the standard events.
				$markup .= '<div class="wpcode-pixel-event-select">';

				$standard_events = array_keys( $standard_events );
				// Use the standard events array keys as both keys and values.
				$standard_events = array_combine( $standard_events, $standard_events );
				// Prepend an empty option.
				$standard_events = array_merge( array( '' => __( 'Choose Standard Event', 'wpcode-pixel' ) ), $standard_events );

				$markup .= $this->get_select(
					'pixels[' . $index . '][' . $pixel->type . '_event_name]',
					$standard_events,
					array( $standard_event_value ),
					'',
					'',
					'',
					'',
					'',
					'',
					'',
					'',
					''
				);
			} else {
				// No standard event let's provide a text input.
				$markup .= '<div class="wpcode-pixel-event-text">';
				$markup .= $this->get_input_text(
					'pixels[' . $index . '][' . $pixel->type . '_event_name]',
					$standard_event_value,
					'',
					'',
					__( 'Conversion Label', 'wpcode-pixel' )
				);
			}
			$markup .= '</div>';
			$markup .= '</div>';
		}

		return $markup;
	}

	/**
	 * @param $name
	 * @param $options
	 * @param $values
	 * @param $class
	 * @param $id
	 * @param $multiple
	 * @param $size
	 * @param $disabled
	 * @param $required
	 * @param $readonly
	 * @param $placeholder
	 * @param $label
	 *
	 * @return string
	 */
	public function get_select( $name, $options, $values, $class = '', $id = '', $multiple = false, $size = '', $disabled = false, $required = false, $readonly = false, $placeholder = '', $label = '' ) {
		$markup = '';
		if ( ! empty( $label ) ) {
			$markup .= '<label for="' . esc_attr( $id ) . '">' . esc_html( $label ) . '</label>';
		}
		$markup .= '<select name="' . esc_attr( $name ) . '" class="' . esc_attr( $class ) . '" id="' . esc_attr( $id ) . '" ' . ( $multiple ? 'multiple' : '' ) . ' ' . ( ! empty( $size ) ? 'size="' . esc_attr( $size ) . '"' : '' ) . ' ' . ( $disabled ? 'disabled' : '' ) . ' ' . ( $required ? 'required' : '' ) . ' ' . ( $readonly ? 'readonly' : '' ) . '>';
		if ( ! empty( $placeholder ) ) {
			$markup .= '<option value="">' . esc_html( $placeholder ) . '</option>';
		}
		foreach ( $options as $key => $option ) {
			$markup .= '<option value="' . esc_attr( $key ) . '" ' . ( in_array( $key, $values, true ) ? 'selected' : '' ) . '>' . esc_html( $option ) . '</option>';
		}
		$markup .= '</select>';

		return $markup;
	}

	/**
	 * Get a text field markup.
	 *
	 * @param string $id The id of the text field.
	 * @param string $value The value of the text field.
	 * @param string $description The description of the text field.
	 * @param bool   $wide Whether the text field should be wide.
	 * @param string $placeholder The placeholder of the text field.
	 *
	 * @return string
	 */
	public function get_input_text( $id, $value = '', $description = '', $wide = false, $placeholder = '' ) {
		$class = 'wpcode-regular-text';
		if ( $wide ) {
			$class .= ' wpcode-wide-text';
		}
		$markup = '<input type="text" id="' . esc_attr( $id ) . '" name="' . esc_attr( $id ) . '" value="' . esc_attr( $value ) . '" class="' . esc_attr( $class ) . '" autocomplete="off" ' . ( ! empty( $placeholder ) ? 'placeholder="' . esc_attr( $placeholder ) . '"' : '' ) . '>';
		if ( ! empty( $description ) ) {
			$markup .= '<p>' . wp_kses_post( $description ) . '</p>';
		}

		return $markup;
	}

	/**
	 * Validate Facebook pixels for duplicate IDs and tokens.
	 *
	 * @param array $facebook_pixels Array of Facebook pixel data.
	 *
	 * @return array Array of error messages, empty if no duplicates found.
	 */
	private function validate_facebook_pixels_for_duplicates( $facebook_pixels ) {
		$errors   = array();
		$seen_ids = array();

		foreach ( $facebook_pixels as $index => $pixel ) {
			$id = trim( $pixel['id'] );

			// Check for duplicate IDs (only if ID is not empty).
			if ( ! empty( $id ) ) {
				if ( in_array( $id, $seen_ids, true ) ) {
					$errors[] = sprintf(
					// translators: %s is the duplicate Facebook Pixel ID.
						__( 'Duplicate Facebook Pixel ID "%s" found. Each pixel must have a unique ID.', 'wpcode-pixel' ),
						esc_html( $id )
					);
				} else {
					$seen_ids[] = $id;
				}
			}
		}

		return $errors;
	}

	/**
	 * Validate Google pixels for duplicate IDs and labels.
	 *
	 * @param array $google_pixels Array of Google pixel data.
	 *
	 * @return array Array of error messages, empty if no duplicates found.
	 */
	private function validate_google_pixels_for_duplicates( $google_pixels ) {
		$errors             = array();
		$seen_analytics_ids = array();
		$seen_ads_ids       = array();
		$seen_labels        = array();

		foreach ( $google_pixels as $index => $pixel ) {
			$analytics_id = trim( $pixel['analytics_id'] );
			$ads_id       = trim( $pixel['ads_id'] );
			$label        = trim( $pixel['label'] );

			// Check for duplicate Analytics IDs (only if ID is not empty).
			if ( ! empty( $analytics_id ) ) {
				if ( in_array( $analytics_id, $seen_analytics_ids, true ) ) {
					$errors[] = sprintf(
					// translators: %s is the duplicate Google Analytics ID.
						__( 'Duplicate Google Analytics ID "%s" found. Each pixel must have a unique Analytics ID.', 'wpcode-pixel' ),
						esc_html( $analytics_id )
					);
				} else {
					$seen_analytics_ids[] = $analytics_id;
				}
			}

			// Check for duplicate Ads IDs (only if ID is not empty).
			if ( ! empty( $ads_id ) ) {
				if ( in_array( $ads_id, $seen_ads_ids, true ) ) {
					$errors[] = sprintf(
					// translators: %s is the duplicate Google Ads ID.
						__( 'Duplicate Google Ads ID "%s" found. Each pixel must have a unique Ads ID.', 'wpcode-pixel' ),
						esc_html( $ads_id )
					);
				} else {
					$seen_ads_ids[] = $ads_id;
				}
			}

			// Check for duplicate labels (only if label is not empty).
			if ( ! empty( $label ) ) {
				if ( in_array( $label, $seen_labels, true ) ) {
					$errors[] = sprintf(
					// translators: %s is the duplicate Google Ads Conversion Label.
						__( 'Duplicate Google Ads Conversion Label "%s" found. Each pixel must have a unique conversion label.', 'wpcode-pixel' ),
						esc_html( $label )
					);
				} else {
					$seen_labels[] = $label;
				}
			}
		}

		return $errors;
	}
}
