<?php
defined( 'ABSPATH' ) || exit;

/**
 * Admin UI: settings page with Settings / Targets / UTM Builder / Test / Help tabs.
 */
class BW_Lead_AI_Admin {

	const PAGE_SLUG = 'bw-lead-ai';

	private static $instance = null;

	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	public function register() {
		add_action( 'admin_menu', array( $this, 'menu' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
	}

	public function menu() {
		add_options_page(
			__( 'BW Lead Attribution Intelligence', 'bw-lead-ai' ),
			__( 'BW Lead AI', 'bw-lead-ai' ),
			'manage_options',
			self::PAGE_SLUG,
			array( $this, 'render_page' )
		);
	}

	public function enqueue( $hook ) {
		if ( 'settings_page_' . self::PAGE_SLUG !== $hook ) {
			return;
		}
		wp_enqueue_style(
			'bw-lead-ai-admin',
			BW_LEAD_AI_URL . 'assets/css/admin.css',
			array(),
			BW_LEAD_AI_VERSION
		);
		wp_register_script(
			'bw-lead-ai-capture',
			BW_LEAD_AI_URL . 'assets/js/capture.js',
			array(),
			BW_LEAD_AI_VERSION,
			true
		);
		wp_localize_script( 'bw-lead-ai-capture', 'bwLeadAI', BW_Lead_AI_Frontend::instance()->client_config() );
		wp_register_script(
			'bw-lead-ai-admin-test',
			BW_LEAD_AI_URL . 'assets/js/admin-test.js',
			array( 'bw-lead-ai-capture' ),
			BW_LEAD_AI_VERSION,
			true
		);
		wp_register_script(
			'bw-lead-ai-utm-builder',
			BW_LEAD_AI_URL . 'assets/js/utm-builder.js',
			array(),
			BW_LEAD_AI_VERSION,
			true
		);
	}

	public function render_page() {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		$tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'settings'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$tabs = array(
			'settings' => __( 'Settings', 'bw-lead-ai' ),
			'events'   => __( 'Interactions', 'bw-lead-ai' ),
			'handoff'  => __( 'Handoff', 'bw-lead-ai' ),
			'targets'  => __( 'Form Fields', 'bw-lead-ai' ),
			'utm'      => __( 'UTM Builder', 'bw-lead-ai' ),
			'test'     => __( 'Test', 'bw-lead-ai' ),
			'help'     => __( 'Help', 'bw-lead-ai' ),
		);
		if ( ! isset( $tabs[ $tab ] ) ) {
			$tab = 'settings';
		}
		echo '<div class="wrap bw-lead-ai">';
		echo '<h1>' . esc_html__( 'BW Lead Attribution Intelligence', 'bw-lead-ai' ) . '</h1>';
		echo '<h2 class="nav-tab-wrapper">';
		foreach ( $tabs as $key => $label ) {
			$class = ( $tab === $key ) ? ' nav-tab-active' : '';
			$url   = add_query_arg(
				array(
					'page' => self::PAGE_SLUG,
					'tab'  => $key,
				),
				admin_url( 'options-general.php' )
			);
			echo '<a class="nav-tab' . esc_attr( $class ) . '" href="' . esc_url( $url ) . '">' . esc_html( $label ) . '</a>';
		}
		echo '</h2>';

		switch ( $tab ) {
			case 'events':
				$this->render_events_tab();
				break;
			case 'handoff':
				$this->render_handoff_tab();
				break;
			case 'targets':
				$this->render_targets_tab();
				break;
			case 'utm':
				$this->render_utm_tab();
				break;
			case 'test':
				$this->render_test_tab();
				break;
			case 'help':
				$this->render_help_tab();
				break;
			case 'settings':
			default:
				$this->render_settings_tab();
				break;
		}
		echo '</div>';
	}

	/**
	 * Emit hidden inputs carrying every setting this tab does not itself render.
	 *
	 * All tabs post to the same option, and BW_Lead_AI_Settings::sanitize() falls
	 * back to the DEFAULT for any key missing from the POST. Without this, saving
	 * one tab silently resets everything the other tabs own.
	 *
	 * @param array $settings Current settings.
	 * @param array $skip     Keys this tab renders real inputs for.
	 */
	private function render_passthrough( $settings, $skip = array() ) {
		$opt = esc_attr( BW_LEAD_AI_OPTION );

		foreach ( array_keys( BW_Lead_AI_Settings::defaults() ) as $key ) {
			if ( in_array( $key, $skip, true ) ) {
				continue;
			}
			$val = isset( $settings[ $key ] ) ? $settings[ $key ] : '';

			// field_targets is the one nested setting — carry each leaf through.
			if ( 'field_targets' === $key && is_array( $val ) ) {
				foreach ( $val as $target_key => $row ) {
					printf(
						'<input type="hidden" name="%1$s[field_targets][%2$s][attr]" value="%3$s">',
						$opt,
						esc_attr( $target_key ),
						esc_attr( isset( $row['attr'] ) ? $row['attr'] : '' )
					);
					printf(
						'<input type="hidden" name="%1$s[field_targets][%2$s][val]" value="%3$s">',
						$opt,
						esc_attr( $target_key ),
						esc_attr( isset( $row['val'] ) ? $row['val'] : '' )
					);
				}
				continue;
			}
			if ( is_array( $val ) ) {
				continue;
			}
			printf(
				'<input type="hidden" name="%1$s[%2$s]" value="%3$s">',
				$opt,
				esc_attr( $key ),
				esc_attr( $val )
			);
		}
	}

	private function render_settings_tab() {
		$settings = BW_Lead_AI_Settings::get();
		$opt      = esc_attr( BW_LEAD_AI_OPTION );
		$standard_list = '<code>' . implode( '</code>, <code>', BW_Lead_AI_Settings::STANDARD_ALIAS_KEYS ) . '</code>';
		?>
		<form action="options.php" method="post">
			<?php settings_fields( BW_Lead_AI_Settings::OPTION_GROUP ); ?>
			<?php
			$this->render_passthrough(
				$settings,
				array( 'parameter_aliases', 'referrer_classification', 'click_ids', 'channels', 'source_medium_separator', 'debug', 'capture_ga_client_id' )
			);
			?>

			<p class="description" style="max-width: 900px;"><?php
				echo wp_kses(
					__( 'All mapping sections share one format: <code>label : value1, value2, ...</code>. One rule per line. Rules are walked in order — earlier rules win over later ones when both could match.', 'bw-lead-ai' ),
					array( 'code' => array() )
				);
				?></p>

			<?php
			$aliases       = BW_Lead_AI_Settings::parse_parameter_aliases( $settings['parameter_aliases'] );
			$source_val    = implode( ', ', $aliases['standard']['source'] );
			$medium_val    = implode( ', ', $aliases['standard']['medium'] );
			$other_lines   = array();
			foreach ( array( 'campaign', 'term', 'content', 'adgroup' ) as $std_key ) {
				if ( ! empty( $aliases['standard'][ $std_key ] ) ) {
					$other_lines[] = $std_key . ' : ' . implode( ', ', $aliases['standard'][ $std_key ] );
				}
			}
			foreach ( $aliases['custom'] as $cust_key => $cust_vals ) {
				$other_lines[] = $cust_key . ' : ' . implode( ', ', $cust_vals );
			}
			$other_text = implode( "\n", $other_lines );
			?>
			<details class="bw-lead-ai-section" open>
				<summary><h2><?php esc_html_e( 'Parameter Aliases', 'bw-lead-ai' ); ?></h2></summary>
				<p class="description"><?php
					echo wp_kses(
						__( '<strong>Source</strong> and <strong>Medium</strong> are required — without them nothing else can be attributed, so they live in their own inputs and cannot be deleted. Enter URL parameter names comma-separated; earlier aliases win. Order matters: put <code>utm_source</code> first to prefer UTMs over custom conventions.', 'bw-lead-ai' ),
						array( 'code' => array(), 'strong' => array() )
					);
					?></p>
				<table class="form-table" role="presentation">
					<tr>
						<th scope="row"><label for="bw-lead-ai-pa-source"><?php esc_html_e( 'Source', 'bw-lead-ai' ); ?></label></th>
						<td><input type="text" id="bw-lead-ai-pa-source" name="<?php echo $opt; ?>[parameter_aliases_source]" value="<?php echo esc_attr( $source_val ); ?>" class="large-text" placeholder="utm_source, source, src"></td>
					</tr>
					<tr>
						<th scope="row"><label for="bw-lead-ai-pa-medium"><?php esc_html_e( 'Medium', 'bw-lead-ai' ); ?></label></th>
						<td><input type="text" id="bw-lead-ai-pa-medium" name="<?php echo $opt; ?>[parameter_aliases_medium]" value="<?php echo esc_attr( $medium_val ); ?>" class="large-text" placeholder="utm_medium, medium, med"></td>
					</tr>
				</table>
				<p class="description" style="margin-top: 16px;"><?php
					echo wp_kses(
						__( '<strong>Other dimensions &amp; custom parameters.</strong> One per line, format <code>label : param1, param2</code>. The remaining built-ins are <code>campaign</code>, <code>term</code>, <code>content</code>, <code>adgroup</code>. Any <em>other</em> label becomes a <strong>custom dimension</strong> exposed as a merge tag <code>{bw:your_label}</code>.', 'bw-lead-ai' ),
						array( 'code' => array(), 'strong' => array(), 'em' => array() )
					);
					?></p>
				<p class="description"><?php
					echo wp_kses(
						__( 'Example — add a row like <code>match_type : mt, match, utm_match_type</code> to track match type from any of those URL parameters, then use <code>{bw:match_type}</code> in a hidden form field.', 'bw-lead-ai' ),
						array( 'code' => array() )
					);
					?></p>
				<textarea name="<?php echo $opt; ?>[parameter_aliases_other]" rows="8" class="large-text code" placeholder="campaign : utm_campaign, cmp&#10;match_type : mt, utm_match_type"><?php echo esc_textarea( $other_text ); ?></textarea>
			</details>

			<details class="bw-lead-ai-section">
				<summary><h2><?php esc_html_e( 'Default Referrer Classification', 'bw-lead-ai' ); ?></h2></summary>
				<p class="description"><?php
					echo wp_kses(
						__( 'When a visit has no UTM tags and no click-ID, the referring hostname is used to guess the medium. The label on the left becomes the visit medium. This is a <strong>fallback</strong> only — explicit UTMs (<code>utm_medium=cpc</code>) and click-IDs always override these rules. Hostname matches are suffix-aware, so <code>google</code> matches <code>www.google.co.uk</code>.', 'bw-lead-ai' ),
						array( 'code' => array(), 'strong' => array() )
					);
					?></p>
				<p class="description"><?php
					echo wp_kses(
						__( 'Example — add <code>ai : openai.com, gemini.google.com, claude.ai, perplexity.ai</code> to classify AI-referrer traffic under the <code>ai</code> medium.', 'bw-lead-ai' ),
						array( 'code' => array() )
					);
					?></p>
				<textarea name="<?php echo $opt; ?>[referrer_classification]" rows="6" class="large-text code" placeholder="organic : google, bing&#10;ai : openai.com, claude.ai"><?php echo esc_textarea( $settings['referrer_classification'] ); ?></textarea>
			</details>

			<details class="bw-lead-ai-section">
				<summary><h2><?php esc_html_e( 'Click-ID Inference', 'bw-lead-ai' ); ?></h2></summary>
				<p class="description"><?php
					echo wp_kses(
						__( 'When a URL contains one of these click-ID parameters and no explicit UTMs are set, the visit is tagged with the <code>source/medium</code> on the left. Format per line: <code>source/medium : param1, param2</code>.', 'bw-lead-ai' ),
						array( 'code' => array() )
					);
					?></p>
				<textarea name="<?php echo $opt; ?>[click_ids]" rows="8" class="large-text code" placeholder="google/cpc : gclid, gclsrc&#10;facebook/social : fbclid"><?php echo esc_textarea( $settings['click_ids'] ); ?></textarea>
			</details>

			<details class="bw-lead-ai-section">
				<summary><h2><?php esc_html_e( 'Channel Mappings', 'bw-lead-ai' ); ?></h2></summary>
				<p class="description"><?php
					echo wp_kses(
						__( 'Map <code>source / medium</code> combinations to friendly channel labels (e.g. "Google Ads"). Rules are checked top-to-bottom; the first match wins. Unmatched visits fall back to the raw <code>source / medium</code> string.', 'bw-lead-ai' ),
						array( 'code' => array() )
					);
					?></p>
				<p class="description"><?php
					echo wp_kses(
						__( 'Wildcards: use <code>*</code> to match any source or medium. In the label, <code>{bw:source}</code> and <code>{bw:medium}</code> are substituted with the actual visit values (useful for generic rules like <code>{bw:source} : */referral</code>). Exposed as merge tags <code>{bw:channel}</code> and <code>{bw:first_channel}</code>.', 'bw-lead-ai' ),
						array( 'code' => array() )
					);
					?></p>
				<textarea name="<?php echo $opt; ?>[channels]" rows="14" class="large-text code" placeholder="Google Ads : google/cpc, google/ppc&#10;{bw:source} : */referral"><?php echo esc_textarea( $settings['channels'] ); ?></textarea>
			</details>

			<details class="bw-lead-ai-section">
				<summary><h2><?php esc_html_e( 'Google Analytics', 'bw-lead-ai' ); ?></h2></summary>
				<p class="description"><?php
					echo wp_kses(
						__( 'Capture the GA4 <strong>client ID</strong> — the anonymous per-browser identifier Google Analytics stores in the <code>_ga</code> cookie — and expose it as <code>{bw:ga_client_id}</code>. Sending it with a lead lets that lead be joined back to its GA4 session in reporting or BigQuery.', 'bw-lead-ai' ),
						array( 'code' => array(), 'strong' => array() )
					);
					?></p>
				<p class="description"><?php
					echo wp_kses(
						__( 'The value is read from the cookie at the moment a form is filled — this plugin never stores it. If Google Analytics is blocked in the visitor\'s browser the cookie does not exist and the tag resolves empty; the plugin\'s own attribution is unaffected either way.', 'bw-lead-ai' ),
						array( 'code' => array() )
					);
					?></p>
				<table class="form-table" role="presentation">
					<tr>
						<th scope="row"><?php esc_html_e( 'Client ID', 'bw-lead-ai' ); ?></th>
						<td><label><input type="checkbox" name="<?php echo esc_attr( BW_LEAD_AI_OPTION ); ?>[capture_ga_client_id]" value="1" <?php checked( 1, (int) $settings['capture_ga_client_id'] ); ?>> <?php esc_html_e( 'Capture the GA client ID and enable the {bw:ga_client_id} merge tag.', 'bw-lead-ai' ); ?></label></td>
					</tr>
				</table>
			</details>

			<details class="bw-lead-ai-section">
				<summary><h2><?php esc_html_e( 'Formatting & Debug', 'bw-lead-ai' ); ?></h2></summary>
				<table class="form-table" role="presentation">
					<tr><th scope="row"><label for="source_medium_separator"><?php esc_html_e( 'Source / medium separator', 'bw-lead-ai' ); ?></label></th>
						<td><input type="text" id="source_medium_separator" name="<?php echo esc_attr( BW_LEAD_AI_OPTION ); ?>[source_medium_separator]" value="<?php echo esc_attr( $settings['source_medium_separator'] ); ?>" class="small-text"> <span class="description"><?php esc_html_e( 'Used when rendering {bw:source_medium}. Default: " / "', 'bw-lead-ai' ); ?></span></td>
					</tr>
					<tr><th scope="row"><?php esc_html_e( 'Debug mode', 'bw-lead-ai' ); ?></th>
						<td><label><input type="checkbox" name="<?php echo esc_attr( BW_LEAD_AI_OPTION ); ?>[debug]" value="1" <?php checked( 1, (int) $settings['debug'] ); ?>> <?php esc_html_e( 'Log capture activity to the browser console and expose window.BWLeadAI.', 'bw-lead-ai' ); ?></label></td>
					</tr>
				</table>
			</details>

			<?php submit_button(); ?>
		</form>
		<?php
	}

	private function render_events_tab() {
		$settings = BW_Lead_AI_Settings::get();
		$opt      = esc_attr( BW_LEAD_AI_OPTION );
		$active   = BW_Lead_AI_Settings::parse_event_types( $settings['event_types'] );

		$descriptions = array(
			'video'      => __( 'Plays and progress milestones for HTML5 &lt;video&gt;, YouTube and Vimeo embeds.', 'bw-lead-ai' ),
			'download'   => __( 'Clicks on links to files with one of the extensions below — on your site or off it.', 'bw-lead-ai' ),
			'phone'      => __( 'Clicks on <code>tel:</code> links.', 'bw-lead-ai' ),
			'email'      => __( 'Clicks on <code>mailto:</code> links.', 'bw-lead-ai' ),
			'social'     => __( 'Clicks on links to the social hostnames listed below.', 'bw-lead-ai' ),
			'outbound'   => __( 'Clicks on links to any other external site (anything not social and not a download).', 'bw-lead-ai' ),
			'custom'     => __( 'Clicks on elements you name yourself by CSS selector, below.', 'bw-lead-ai' ),
			'scroll'     => __( 'How far down the page the visitor read. Fires once per milestone per page.', 'bw-lead-ai' ),
			'form_start' => __( 'The first time a visitor interacts with any form field — useful for spotting abandoned forms.', 'bw-lead-ai' ),
		);
		?>
		<form action="options.php" method="post">
			<?php settings_fields( BW_Lead_AI_Settings::OPTION_GROUP ); ?>
			<?php
			$this->render_passthrough(
				$settings,
				array( 'event_types', 'event_downloads', 'event_social_hosts', 'event_video_milestones', 'event_scroll_thresholds', 'event_custom_selectors' )
			);
			?>
			<input type="hidden" name="<?php echo $opt; ?>[event_types_present]" value="1">

			<h2><?php esc_html_e( 'Interaction tracking', 'bw-lead-ai' ); ?></h2>
			<p class="description" style="max-width: 900px;">
				<?php esc_html_e( 'Record what visitors did on the way to converting — videos watched, files downloaded, numbers called — and attach it to the lead alongside the traffic source.', 'bw-lead-ai' ); ?>
			</p>
			<p class="description" style="max-width: 900px;">
				<?php
				echo wp_kses(
					__( '<strong>Everything here is off by default.</strong> With nothing ticked, no extra JavaScript is loaded and nothing changes anywhere — so leaving this tab alone costs a site nothing. Enabled interactions appear in <code>{bw:summary}</code>, and as the merge tags <code>{bw:events}</code>, <code>{bw:events_list}</code> and <code>{bw:event.&lt;type&gt;}</code>.', 'bw-lead-ai' ),
					array( 'code' => array(), 'strong' => array() )
				);
				?>
			</p>

			<table class="form-table" role="presentation">
				<?php foreach ( BW_Lead_AI_Settings::EVENT_TYPES as $type ) : ?>
					<tr>
						<th scope="row"><?php echo esc_html( BW_Lead_AI_GF_Field::event_type_label( $type ) ); ?></th>
						<td>
							<label>
								<input type="checkbox" name="<?php echo $opt; ?>[event_types_checked][<?php echo esc_attr( $type ); ?>]" value="1" <?php checked( in_array( $type, $active, true ) ); ?>>
								<?php esc_html_e( 'Track this', 'bw-lead-ai' ); ?>
							</label>
							<p class="description"><?php
								echo wp_kses(
									isset( $descriptions[ $type ] ) ? $descriptions[ $type ] : '',
									array( 'code' => array() )
								);
								?></p>
						</td>
					</tr>
				<?php endforeach; ?>
			</table>

			<details class="bw-lead-ai-section" open>
				<summary><h2><?php esc_html_e( 'Interaction options', 'bw-lead-ai' ); ?></h2></summary>
				<p class="description">
					<?php esc_html_e( 'These only apply to the interaction types you have ticked above.', 'bw-lead-ai' ); ?>
				</p>
				<table class="form-table" role="presentation">
					<tr>
						<th scope="row"><label for="bw-lead-ai-ev-downloads"><?php esc_html_e( 'Download extensions', 'bw-lead-ai' ); ?></label></th>
						<td>
							<input type="text" id="bw-lead-ai-ev-downloads" name="<?php echo $opt; ?>[event_downloads]" value="<?php echo esc_attr( $settings['event_downloads'] ); ?>" class="large-text" placeholder="pdf, doc, docx, zip">
							<p class="description"><?php esc_html_e( 'Comma-separated, no dots. A link ending in one of these counts as a download.', 'bw-lead-ai' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row"><label for="bw-lead-ai-ev-social"><?php esc_html_e( 'Social hostnames', 'bw-lead-ai' ); ?></label></th>
						<td>
							<input type="text" id="bw-lead-ai-ev-social" name="<?php echo $opt; ?>[event_social_hosts]" value="<?php echo esc_attr( $settings['event_social_hosts'] ); ?>" class="large-text" placeholder="facebook.com, instagram.com">
							<p class="description"><?php
								echo wp_kses(
									__( 'Links to these hosts are recorded as <strong>social</strong> instead of <strong>outbound</strong>. Subdomains match automatically.', 'bw-lead-ai' ),
									array( 'strong' => array() )
								);
								?></p>
						</td>
					</tr>
					<tr>
						<th scope="row"><label for="bw-lead-ai-ev-videomarks"><?php esc_html_e( 'Video milestones', 'bw-lead-ai' ); ?></label></th>
						<td>
							<input type="text" id="bw-lead-ai-ev-videomarks" name="<?php echo $opt; ?>[event_video_milestones]" value="<?php echo esc_attr( $settings['event_video_milestones'] ); ?>" class="regular-text" placeholder="25, 50, 75, 100">
							<p class="description"><?php esc_html_e( 'Percentages, 1–100. Each fires once per video per page, so a real watch is distinguishable from an accidental click. Leave empty to record plays only.', 'bw-lead-ai' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row"><label for="bw-lead-ai-ev-scrollmarks"><?php esc_html_e( 'Scroll milestones', 'bw-lead-ai' ); ?></label></th>
						<td>
							<input type="text" id="bw-lead-ai-ev-scrollmarks" name="<?php echo $opt; ?>[event_scroll_thresholds]" value="<?php echo esc_attr( $settings['event_scroll_thresholds'] ); ?>" class="regular-text" placeholder="25, 50, 75, 100">
							<p class="description"><?php esc_html_e( 'Percentages, 1–100. Scroll depth is the noisiest interaction type — trim this list if summaries get long.', 'bw-lead-ai' ); ?></p>
						</td>
					</tr>
				</table>

				<p class="description" style="margin-top: 16px;"><?php
					echo wp_kses(
						__( '<strong>Custom actions.</strong> One per line, format <code>Label : selector1, selector2</code>. Any click on a matching element (or inside one) is recorded under your label.', 'bw-lead-ai' ),
						array( 'code' => array(), 'strong' => array() )
					);
					?></p>
				<p class="description"><?php
					echo wp_kses(
						__( 'Example — <code>Pricing CTA : .pricing-cta, #get-quote</code>. Note that a custom match <em>wins</em>: if the element is also an outbound link or a download, only the custom action is recorded, so totals are not double-counted.', 'bw-lead-ai' ),
						array( 'code' => array(), 'em' => array() )
					);
					?></p>
				<textarea name="<?php echo $opt; ?>[event_custom_selectors]" rows="6" class="large-text code" placeholder="Pricing CTA : .pricing-cta, #get-quote&#10;Book a demo : a.demo-button"><?php echo esc_textarea( $settings['event_custom_selectors'] ); ?></textarea>
			</details>

			<?php submit_button(); ?>
		</form>
		<?php
	}

	private function render_handoff_tab() {
		$settings   = BW_Lead_AI_Settings::get();
		$opt        = esc_attr( BW_LEAD_AI_OPTION );
		$selected   = BW_Lead_AI_Settings::parse_handoff_datapoints( $settings );
		$available  = BW_Lead_AI_Settings::handoff_available_datapoints();
		$sensitive  = BW_Lead_AI_Settings::handoff_sensitive_datapoints();
		$retention  = (string) $settings['handoff_retention'];
		$presets    = array( '90', '365', '730', '1825', 'unlimited' );
		$is_preset  = in_array( $retention, $presets, true );
		$live       = BW_Lead_AI_Settings::handoff_enabled( $settings );
		$stats      = BW_Lead_AI_Handoff_Store::stats();

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only confirmation after a nonce-checked redirect.
		$purged = isset( $_GET['bw_purged'] ) ? absint( wp_unslash( $_GET['bw_purged'] ) ) : null;
		if ( null !== $purged ) {
			echo '<div class="notice notice-success is-dismissible"><p>' . sprintf(
				/* translators: %d is a number of deleted records. */
				esc_html__( 'Deleted %d handoff record(s).', 'bw-lead-ai' ),
				$purged
			) . '</p></div>';
		}
		?>
		<form action="options.php" method="post">
			<?php settings_fields( BW_Lead_AI_Settings::OPTION_GROUP ); ?>
			<?php
			$this->render_passthrough(
				$settings,
				array(
					'handoff_enabled', 'handoff_mode_data', 'handoff_mode_link', 'handoff_ack',
					'handoff_domains', 'handoff_origins', 'handoff_param', 'handoff_ttl',
					'handoff_retention', 'handoff_datapoints',
				)
			);
			?>
			<input type="hidden" name="<?php echo $opt; ?>[handoff_datapoints_present]" value="1">

			<h2><?php esc_html_e( 'Cross-domain handoff', 'bw-lead-ai' ); ?></h2>
			<p class="description" style="max-width: 900px;">
				<?php esc_html_e( 'When a visitor leaves for a form on a different domain — an enrolment portal, a CRM, a booking tool — their journey stays behind on this site and the conversion arrives with no attribution. Handoff carries it across.', 'bw-lead-ai' ); ?>
			</p>
			<p class="description" style="max-width: 900px;">
				<?php
				echo wp_kses(
					__( 'A short-lived random token rides on the outbound link. <strong>The journey itself is never in the URL.</strong> What the destination can do with that token is decided by the two modes below.', 'bw-lead-ai' ),
					array( 'strong' => array() )
				);
				?>
			</p>

			<table class="form-table" role="presentation">
				<tr>
					<th scope="row"><?php esc_html_e( 'Enable', 'bw-lead-ai' ); ?></th>
					<td>
						<label><input type="checkbox" name="<?php echo $opt; ?>[handoff_enabled]" value="1" <?php checked( 1, (int) $settings['handoff_enabled'] ); ?>> <?php esc_html_e( 'Turn on cross-domain handoff', 'bw-lead-ai' ); ?></label>
						<p class="description"><?php esc_html_e( 'While off, no endpoints exist, no script is loaded, and nothing is stored. Handoff also stays off until at least one destination domain and one mode are set.', 'bw-lead-ai' ); ?></p>
						<?php if ( ! empty( $settings['handoff_enabled'] ) && ! $live ) : ?>
							<p class="description" style="color:#d63638;"><strong><?php esc_html_e( 'Not active yet — add a destination domain and choose a mode below.', 'bw-lead-ai' ); ?></strong></p>
						<?php endif; ?>
					</td>
				</tr>
			</table>

			<details class="bw-lead-ai-section" open>
				<summary><h2><?php esc_html_e( 'Modes', 'bw-lead-ai' ); ?></h2></summary>
				<table class="form-table" role="presentation">
					<tr>
						<th scope="row"><?php esc_html_e( 'Journey link', 'bw-lead-ai' ); ?><br><span class="description" style="font-weight:400;"><?php esc_html_e( 'recommended', 'bw-lead-ai' ); ?></span></th>
						<td>
							<label><input type="checkbox" name="<?php echo $opt; ?>[handoff_mode_link]" value="1" <?php checked( 1, (int) $settings['handoff_mode_link'] ); ?>> <?php esc_html_e( 'Let the destination store a link back to this site', 'bw-lead-ai' ); ?></label>
							<p class="description"><?php esc_html_e( 'Only the opaque token crosses domains. Your team follows the link and reads the journey here, signed in to WordPress. No visitor data leaves this server.', 'bw-lead-ai' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row"><?php esc_html_e( 'Data handoff', 'bw-lead-ai' ); ?></th>
						<td>
							<label><input type="checkbox" name="<?php echo $opt; ?>[handoff_mode_data]" value="1" <?php checked( 1, (int) $settings['handoff_mode_data'] ); ?>> <?php esc_html_e( 'Let the destination read the datapoints and fill its own fields', 'bw-lead-ai' ); ?></label>
							<p class="description"><?php esc_html_e( 'The destination exchanges the token for the datapoints you tick below, and can populate its own form fields with them.', 'bw-lead-ai' ); ?></p>
							<div class="notice notice-warning inline" style="margin:12px 0;max-width:760px;">
								<p><strong><?php esc_html_e( 'This sends visitor data to a third-party origin.', 'bw-lead-ai' ); ?></strong>
								<?php esc_html_e( 'That is a privacy decision you are making on your visitors\' behalf, and it may need to appear in your privacy policy. Keep the datapoints to the minimum the destination genuinely needs.', 'bw-lead-ai' ); ?></p>
								<p><label><input type="checkbox" name="<?php echo $opt; ?>[handoff_ack]" value="1" <?php checked( 1, (int) $settings['handoff_ack'] ); ?>> <strong><?php esc_html_e( 'I understand and want to enable this', 'bw-lead-ai' ); ?></strong></label></p>
								<p class="description"><?php esc_html_e( 'Data handoff stays off until this is ticked.', 'bw-lead-ai' ); ?></p>
							</div>
						</td>
					</tr>
				</table>
			</details>

			<details class="bw-lead-ai-section" open>
				<summary><h2><?php esc_html_e( 'Destinations', 'bw-lead-ai' ); ?></h2></summary>
				<p class="description"><?php
					echo wp_kses(
						__( 'Hostnames whose links get a token. One per line. Subdomains match automatically, so <code>example.com</code> covers <code>portal.example.com</code>.', 'bw-lead-ai' ),
						array( 'code' => array() )
					);
					?></p>
				<textarea name="<?php echo $opt; ?>[handoff_domains]" rows="4" class="large-text code" placeholder="portal.example.com"><?php echo esc_textarea( str_replace( ', ', "\n", (string) $settings['handoff_domains'] ) ); ?></textarea>

				<p class="description" style="margin-top:16px;"><?php
					echo wp_kses(
						__( '<strong>Allowed origins</strong> — which sites may read the token. Leave empty to allow <code>https://</code> plus each destination above, which is usually right. Set explicitly if the destination runs on a different scheme or port.', 'bw-lead-ai' ),
						array( 'code' => array(), 'strong' => array() )
					);
					?></p>
				<textarea name="<?php echo $opt; ?>[handoff_origins]" rows="3" class="large-text code" placeholder="https://portal.example.com"><?php echo esc_textarea( str_replace( ', ', "\n", (string) $settings['handoff_origins'] ) ); ?></textarea>
				<?php if ( $live ) : ?>
					<p class="description"><?php esc_html_e( 'Currently allowing:', 'bw-lead-ai' ); ?> <code><?php echo esc_html( implode( ', ', BW_Lead_AI_Settings::parse_handoff_origins( $settings ) ) ); ?></code></p>
				<?php endif; ?>
			</details>

			<details class="bw-lead-ai-section">
				<summary><h2><?php esc_html_e( 'Datapoints sent', 'bw-lead-ai' ); ?></h2></summary>
				<p class="description"><?php esc_html_e( 'What a journey record holds. These are also exactly what the Data handoff mode can return — the server drops anything not ticked here, so this list is the boundary, not a suggestion.', 'bw-lead-ai' ); ?></p>
				<table class="form-table" role="presentation"><tr><th scope="row"><?php esc_html_e( 'Include', 'bw-lead-ai' ); ?></th><td>
				<?php foreach ( $available as $point ) : ?>
					<?php $is_sensitive = in_array( $point, $sensitive, true ); ?>
					<p style="margin:4px 0;">
						<label>
							<input type="checkbox" name="<?php echo $opt; ?>[handoff_datapoints_checked][<?php echo esc_attr( $point ); ?>]" value="1" <?php checked( in_array( $point, $selected, true ) ); ?>>
							<code><?php echo esc_html( '{bw:' . $point . '}' ); ?></code>
							<?php if ( $is_sensitive ) : ?>
								<span style="color:#d63638;"><strong><?php esc_html_e( '— contains the visitor\'s browsing history', 'bw-lead-ai' ); ?></strong></span>
							<?php endif; ?>
						</label>
					</p>
				<?php endforeach; ?>
				<p class="description" style="margin-top:12px;"><?php esc_html_e( 'The marked datapoints list the pages a visitor viewed. Safe to include for the Journey link mode, where they never leave this server — think twice before adding them to Data handoff.', 'bw-lead-ai' ); ?></p>
				</td></tr></table>
			</details>

			<details class="bw-lead-ai-section">
				<summary><h2><?php esc_html_e( 'Timing and retention', 'bw-lead-ai' ); ?></h2></summary>
				<table class="form-table" role="presentation">
					<tr>
						<th scope="row"><label for="bw-lead-ai-handoff-param"><?php esc_html_e( 'Link parameter', 'bw-lead-ai' ); ?></label></th>
						<td>
							<input type="text" id="bw-lead-ai-handoff-param" name="<?php echo $opt; ?>[handoff_param]" value="<?php echo esc_attr( $settings['handoff_param'] ); ?>" class="regular-text">
							<p class="description"><?php esc_html_e( 'The query parameter carrying the token. The destination reads this name.', 'bw-lead-ai' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row"><label for="bw-lead-ai-handoff-ttl"><?php esc_html_e( 'Token lifetime', 'bw-lead-ai' ); ?></label></th>
						<td>
							<input type="number" id="bw-lead-ai-handoff-ttl" name="<?php echo $opt; ?>[handoff_ttl]" value="<?php echo esc_attr( (int) $settings['handoff_ttl'] ); ?>" min="5" max="240" class="small-text"> <?php esc_html_e( 'minutes', 'bw-lead-ai' ); ?>
							<p class="description"><?php esc_html_e( 'How long an unconfirmed record lives. It only has to survive the click-through, and keeping it short is what stops the endpoint being used as free storage. Unconfirmed records are deleted automatically.', 'bw-lead-ai' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row"><label for="bw-lead-ai-handoff-retention"><?php esc_html_e( 'Keep confirmed journeys', 'bw-lead-ai' ); ?></label></th>
						<td>
							<select id="bw-lead-ai-handoff-retention" name="<?php echo $opt; ?>[handoff_retention]">
								<?php
								$labels = array(
									'90'        => __( '90 days', 'bw-lead-ai' ),
									'365'       => __( '1 year', 'bw-lead-ai' ),
									'730'       => __( '2 years', 'bw-lead-ai' ),
									'1825'      => __( '5 years', 'bw-lead-ai' ),
									'unlimited' => __( 'Unlimited — never delete', 'bw-lead-ai' ),
								);
								foreach ( $labels as $value => $label ) {
									printf(
										'<option value="%1$s"%2$s>%3$s</option>',
										esc_attr( $value ),
										selected( $retention, $value, false ),
										esc_html( $label )
									);
								}
								?>
							</select>
							<label style="margin-left:12px;"><?php esc_html_e( 'or custom:', 'bw-lead-ai' ); ?>
								<input type="number" name="<?php echo $opt; ?>[handoff_retention_custom]" value="<?php echo $is_preset ? '' : esc_attr( $retention ); ?>" min="1" class="small-text" placeholder="<?php esc_attr_e( 'days', 'bw-lead-ai' ); ?>">
							</label>
							<p class="description">
								<?php esc_html_e( 'Only applies once the destination reports a submission. A record is kept because a real person produced it, so this can safely be years — a confirmed journey is a few kilobytes and they arrive at roughly the rate of form submissions.', 'bw-lead-ai' ); ?>
							</p>
							<p class="description"><strong><?php esc_html_e( 'Unlimited keeps a visitor\'s browsing history indefinitely.', 'bw-lead-ai' ); ?></strong> <?php esc_html_e( 'That is a data-retention decision, not a storage one. Set a finite value if you have a retention policy.', 'bw-lead-ai' ); ?></p>
						</td>
					</tr>
				</table>
			</details>

			<?php submit_button(); ?>
		</form>

		<?php if ( $live ) : ?>
			<details class="bw-lead-ai-section" open>
				<summary><h2><?php esc_html_e( 'Stored journeys', 'bw-lead-ai' ); ?></h2></summary>
				<p>
					<?php
					printf(
						/* translators: 1: pending count, 2: confirmed count. */
						esc_html__( 'Awaiting confirmation: %1$d — these expire on their own. Confirmed and kept: %2$d.', 'bw-lead-ai' ),
						(int) $stats['pending'],
						(int) $stats['confirmed']
					);
					?>
				</p>
				<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" onsubmit="return confirm('<?php echo esc_js( __( 'Delete every stored journey, including confirmed ones? This cannot be undone.', 'bw-lead-ai' ) ); ?>');">
					<input type="hidden" name="action" value="<?php echo esc_attr( BW_Lead_AI_Handoff_Admin::PURGE_ACTION ); ?>">
					<?php wp_nonce_field( BW_Lead_AI_Handoff_Admin::PURGE_ACTION ); ?>
					<button type="submit" class="button button-link-delete"><?php esc_html_e( 'Delete all stored journeys', 'bw-lead-ai' ); ?></button>
				</form>
			</details>

			<details class="bw-lead-ai-section">
				<summary><h2><?php esc_html_e( 'Setting up the destination', 'bw-lead-ai' ); ?></h2></summary>
				<p class="description"><?php esc_html_e( 'Give these to whoever manages the tag manager on the destination site.', 'bw-lead-ai' ); ?></p>
				<table class="form-table" role="presentation">
					<tr><th scope="row"><?php esc_html_e( 'Parameter to read', 'bw-lead-ai' ); ?></th>
						<td><code><?php echo esc_html( $settings['handoff_param'] ); ?></code></td></tr>
					<?php if ( BW_Lead_AI_Settings::handoff_mode_data( $settings ) ) : ?>
						<tr><th scope="row"><?php esc_html_e( 'Claim the data', 'bw-lead-ai' ); ?></th>
							<td><code>GET <?php echo esc_html( rest_url( BW_Lead_AI_Handoff_REST::NAMESPACE_V1 . '/handoff/' ) ); ?>&lt;token&gt;</code>
							<p class="description"><?php esc_html_e( 'Works once per token. Retry twice on a 404 to cover the race between the click and the page loading.', 'bw-lead-ai' ); ?></p></td></tr>
					<?php endif; ?>
					<?php if ( BW_Lead_AI_Settings::handoff_mode_link( $settings ) ) : ?>
						<tr><th scope="row"><?php esc_html_e( 'Journey link to store', 'bw-lead-ai' ); ?></th>
							<td><code><?php echo esc_html( BW_Lead_AI_Handoff_Admin::viewer_url() ); ?>&amp;token=&lt;token&gt;</code></td></tr>
						<tr><th scope="row"><?php esc_html_e( 'Confirm on submit', 'bw-lead-ai' ); ?></th>
							<td><code>POST <?php echo esc_html( rest_url( BW_Lead_AI_Handoff_REST::NAMESPACE_V1 . '/handoff/' ) ); ?>&lt;token&gt;/confirm</code>
							<p class="description"><?php esc_html_e( 'Fire this from the form-submit trigger. Without it the journey is discarded when the token expires — this is what marks a record worth keeping.', 'bw-lead-ai' ); ?></p></td></tr>
					<?php endif; ?>
				</table>
			</details>
		<?php endif; ?>
		<?php
	}

	private function render_targets_tab() {
		$settings = BW_Lead_AI_Settings::get();
		$targets  = $settings['field_targets'];
		$labels   = array(
			'summary'    => __( 'Full summary', 'bw-lead-ai' ),
			'source'     => __( 'Source (last)', 'bw-lead-ai' ),
			'medium'     => __( 'Medium (last)', 'bw-lead-ai' ),
			'sources'    => __( 'Sources (all, comma-joined)', 'bw-lead-ai' ),
			'terms'      => __( 'Terms (all, comma-joined)', 'bw-lead-ai' ),
			'first_page' => __( 'First landing page', 'bw-lead-ai' ),
		);
		?>
		<form action="options.php" method="post">
			<?php settings_fields( BW_Lead_AI_Settings::OPTION_GROUP ); ?>

			<?php foreach ( array_keys( BW_Lead_AI_Settings::defaults() ) as $hidden_key ) : ?>
				<?php if ( in_array( $hidden_key, array( 'field_targets' ), true ) ) continue; ?>
				<?php $val = $settings[ $hidden_key ]; ?>
				<?php if ( is_array( $val ) ) continue; ?>
				<input type="hidden" name="<?php echo esc_attr( BW_LEAD_AI_OPTION ); ?>[<?php echo esc_attr( $hidden_key ); ?>]" value="<?php echo esc_attr( $val ); ?>">
			<?php endforeach; ?>

			<h2><?php esc_html_e( 'Legacy form-field targets', 'bw-lead-ai' ); ?></h2>
			<div class="notice notice-info inline" style="margin: 12px 0;">
				<p><strong><?php esc_html_e( 'Using Gravity Forms?', 'bw-lead-ai' ); ?></strong>
				<?php esc_html_e( 'You do not need this tab. Use merge tags like {bw:source_medium} as hidden-field default values instead — they are more flexible and easier to maintain. See the Help tab for setup instructions.', 'bw-lead-ai' ); ?></p>
			</div>
			<p class="description">
				<?php esc_html_e( 'For non-Gravity-Forms setups only. These targets attach tracking data to form fields by CSS selector, id, class, or name attribute.', 'bw-lead-ai' ); ?>
			</p>
			<table class="form-table" role="presentation">
				<?php foreach ( $labels as $key => $label ) : ?>
					<?php $row = isset( $targets[ $key ] ) ? $targets[ $key ] : array( 'attr' => '', 'val' => '' ); ?>
					<tr>
						<th scope="row"><?php echo esc_html( $label ); ?></th>
						<td>
							<select name="<?php echo esc_attr( BW_LEAD_AI_OPTION ); ?>[field_targets][<?php echo esc_attr( $key ); ?>][attr]">
								<option value=""><?php esc_html_e( '(none)', 'bw-lead-ai' ); ?></option>
								<?php
								foreach ( array(
									'id'       => __( 'Field ID', 'bw-lead-ai' ),
									'class'    => __( 'CSS class', 'bw-lead-ai' ),
									'name'     => __( 'Field name', 'bw-lead-ai' ),
									'selector' => __( 'CSS selector', 'bw-lead-ai' ),
								) as $ak => $al ) {
									printf(
										'<option value="%1$s"%2$s>%3$s</option>',
										esc_attr( $ak ),
										selected( $row['attr'], $ak, false ),
										esc_html( $al )
									);
								}
								?>
							</select>
							<input type="text" name="<?php echo esc_attr( BW_LEAD_AI_OPTION ); ?>[field_targets][<?php echo esc_attr( $key ); ?>][val]" value="<?php echo esc_attr( $row['val'] ); ?>" class="regular-text">
						</td>
					</tr>
				<?php endforeach; ?>
			</table>

			<?php submit_button(); ?>
		</form>
		<?php
	}

	private function render_utm_tab() {
		wp_enqueue_script( 'bw-lead-ai-utm-builder' );
		wp_localize_script(
			'bw-lead-ai-utm-builder',
			'bwLeadAIUtm',
			array(
				'home' => home_url(),
				'rest' => rest_url( BW_Lead_AI_REST::NAMESPACE_V1 . '/links' ),
				'nonce' => wp_create_nonce( 'wp_rest' ),
			)
		);
		$options = get_option( BW_LEAD_AI_UTM_OPTION, array() );
		if ( ! is_array( $options ) ) {
			$options = array();
		}
		?>
		<form action="options.php" method="post">
			<?php settings_fields( BW_Lead_AI_Settings::OPTION_GROUP . '_utm' ); ?>
			<h2><?php esc_html_e( 'UTM Tracking Links', 'bw-lead-ai' ); ?></h2>
			<p class="description"><?php esc_html_e( 'Optional builder for outbound campaign URLs. Fill in fields; the tracked URL below updates live. Copy and use in your ad platforms.', 'bw-lead-ai' ); ?></p>
			<div class="bw-lead-ai-utm-items">
				<?php foreach ( $options as $index => $item ) :
					$item = wp_parse_args(
						is_array( $item ) ? $item : array(),
						array(
							'link_to_uri'  => home_url(),
							'utm_source'   => '',
							'utm_medium'   => '',
							'utm_campaign' => '',
							'utm_term'     => '',
							'utm_content'  => '',
							'note'         => '',
						)
					);
					?>
					<div class="bw-lead-ai-utm-item">
						<table class="form-table" role="presentation">
							<tbody>
							<?php
							$fields = array(
								'link_to_uri'  => __( 'Linked-to URL', 'bw-lead-ai' ),
								'utm_source'   => __( 'UTM Source', 'bw-lead-ai' ),
								'utm_medium'   => __( 'UTM Medium', 'bw-lead-ai' ),
								'utm_campaign' => __( 'UTM Campaign', 'bw-lead-ai' ),
								'utm_term'     => __( 'UTM Term', 'bw-lead-ai' ),
								'utm_content'  => __( 'UTM Content', 'bw-lead-ai' ),
							);
							foreach ( $fields as $fk => $fl ) :
								?>
								<tr>
									<th scope="row"><?php echo esc_html( $fl ); ?></th>
									<td><input type="text" name="<?php echo esc_attr( BW_LEAD_AI_UTM_OPTION ); ?>[<?php echo (int) $index; ?>][<?php echo esc_attr( $fk ); ?>]" value="<?php echo esc_attr( $item[ $fk ] ); ?>" class="regular-text bw-lead-ai-utm-input"></td>
								</tr>
								<?php
							endforeach;
							?>
							<tr>
								<th scope="row"><?php esc_html_e( 'Tracked URL', 'bw-lead-ai' ); ?></th>
								<td><input type="text" data-tracked-uri class="large-text" readonly> <button type="button" class="button bw-lead-ai-copy" data-target="data-tracked-uri"><?php esc_html_e( 'Copy', 'bw-lead-ai' ); ?></button></td>
							</tr>
							<tr>
								<th scope="row"><?php esc_html_e( 'Parameters only', 'bw-lead-ai' ); ?></th>
								<td><input type="text" data-parameters class="large-text" readonly> <button type="button" class="button bw-lead-ai-copy" data-target="data-parameters"><?php esc_html_e( 'Copy', 'bw-lead-ai' ); ?></button></td>
							</tr>
							<tr>
								<th scope="row"><?php esc_html_e( 'Note', 'bw-lead-ai' ); ?></th>
								<td><textarea name="<?php echo esc_attr( BW_LEAD_AI_UTM_OPTION ); ?>[<?php echo (int) $index; ?>][note]" rows="3" class="large-text"><?php echo esc_textarea( $item['note'] ); ?></textarea></td>
							</tr>
							<tr><td colspan="2"><button type="button" class="button bw-lead-ai-utm-open"><?php esc_html_e( 'Open tracked URL', 'bw-lead-ai' ); ?></button> <button type="button" class="button bw-lead-ai-utm-remove"><?php esc_html_e( 'Remove', 'bw-lead-ai' ); ?></button></td></tr>
							</tbody>
						</table>
						<hr>
					</div>
				<?php endforeach; ?>
			</div>
			<p><button type="button" class="button button-secondary bw-lead-ai-utm-add"><?php esc_html_e( 'Add tracking item', 'bw-lead-ai' ); ?></button></p>
			<?php submit_button(); ?>
		</form>
		<?php
	}

	/**
	 * Preset traffic scenarios for the Test tab's simulated-link harness.
	 *
	 * Built partly from this site's own settings — the click-ID table, non-UTM
	 * parameter aliases and custom dimensions all come from live config, so the
	 * generated links match what this install would actually recognise rather than
	 * a hardcoded ideal.
	 *
	 * Scenario shape:
	 *   group         Display grouping.
	 *   label          Scenario name.
	 *   note           One line on what it exercises.
	 *   params         Query parameters to append to the landing page.
	 *   referrer       Simulated referring URL, '' for none.
	 *   preview_only   True when the scenario depends on a referrer. A link cannot
	 *                  forge a referrer, so those rows preview the resolution but
	 *                  offer no "Open" button — opening one would just be a direct
	 *                  visit and would quietly log the wrong thing.
	 */
	private function test_scenarios() {
		$settings = BW_Lead_AI_Settings::get();
		$aliases  = BW_Lead_AI_Settings::parse_parameter_aliases( $settings['parameter_aliases'] );

		$group_full     = __( 'Fully tagged campaigns', 'bw-lead-ai' );
		$group_click    = __( 'Auto-tagging — click ID only', 'bw-lead-ai' );
		$group_minimal  = __( 'Minimal or untagged', 'bw-lead-ai' );
		$group_referrer = __( 'Arriving from another site', 'bw-lead-ai' );
		$group_edge     = __( 'Edge cases', 'bw-lead-ai' );

		$out = array();

		// --- fully tagged -------------------------------------------------
		$out[] = array(
			'group'  => $group_full,
			'label'  => __( 'Google Ads — full UTM tagging', 'bw-lead-ai' ),
			'note'   => __( 'A properly tagged paid click with every dimension present.', 'bw-lead-ai' ),
			'params' => array(
				'utm_source'   => 'google',
				'utm_medium'   => 'cpc',
				'utm_campaign' => 'spring-sale',
				'utm_term'     => 'blue widgets',
				'utm_content'  => 'ad-variant-a',
				'utm_adgroup'  => 'widgets-exact',
			),
		);
		$out[] = array(
			'group'  => $group_full,
			'label'  => __( 'Facebook Ads — full UTM tagging', 'bw-lead-ai' ),
			'note'   => __( 'Paid social tagged explicitly rather than relying on fbclid.', 'bw-lead-ai' ),
			'params' => array(
				'utm_source'   => 'facebook',
				'utm_medium'   => 'cpc',
				'utm_campaign' => 'retargeting-q3',
				'utm_content'  => 'carousel-b',
			),
		);
		$out[] = array(
			'group'  => $group_full,
			'label'  => __( 'Email newsletter', 'bw-lead-ai' ),
			'note'   => __( 'Should land in the Email channel, not Direct.', 'bw-lead-ai' ),
			'params' => array(
				'utm_source'   => 'newsletter',
				'utm_medium'   => 'email',
				'utm_campaign' => 'july-roundup',
			),
		);

		// --- one row per configured click-ID source/medium pair -----------
		$seen_pairs = array();
		foreach ( BW_Lead_AI_Settings::parse_click_ids( $settings['click_ids'] ) as $row ) {
			$pair = $row['source'] . '/' . $row['medium'];
			if ( isset( $seen_pairs[ $pair ] ) ) {
				continue;
			}
			$seen_pairs[ $pair ] = true;
			$out[]               = array(
				'group'  => $group_click,
				'label'  => sprintf(
					/* translators: 1: click-ID parameter name, 2: source/medium pair. */
					__( '%1$s only → %2$s', 'bw-lead-ai' ),
					$row['param'],
					$pair
				),
				'note'   => __( 'No UTMs at all — the source has to be inferred from the click ID.', 'bw-lead-ai' ),
				'params' => array( $row['param'] => 'bwtest-' . $row['param'] ),
			);
		}

		// --- minimal ------------------------------------------------------
		$out[] = array(
			'group'  => $group_minimal,
			'label'  => __( 'Source only — utm_source=google', 'bw-lead-ai' ),
			'note'   => __( 'Source with no medium. Exercises step 2 of the cascade.', 'bw-lead-ai' ),
			'params' => array( 'utm_source' => 'google' ),
		);
		$out[] = array(
			'group'  => $group_minimal,
			'label'  => __( 'Completely untagged URL', 'bw-lead-ai' ),
			'note'   => __( 'Only what the browser reveals. Opened from here it is a Direct visit, because your own admin is a same-site referrer.', 'bw-lead-ai' ),
			'params' => array(),
		);

		// --- referrer-driven (preview only) -------------------------------
		$referrers = array(
			array( 'https://www.google.com/search?q=blue+widgets', __( 'Google organic search', 'bw-lead-ai' ) ),
			array( 'https://www.bing.com/search?q=blue+widgets', __( 'Bing organic search', 'bw-lead-ai' ) ),
			array( 'https://www.facebook.com/', __( 'Facebook — organic, unpaid', 'bw-lead-ai' ) ),
			array( 'https://www.linkedin.com/feed/', __( 'LinkedIn — organic', 'bw-lead-ai' ) ),
			array( 'https://blog.example.net/post/', __( 'Unknown site — plain referral', 'bw-lead-ai' ) ),
		);
		foreach ( $referrers as $ref ) {
			$out[] = array(
				'group'        => $group_referrer,
				'label'        => $ref[1],
				'note'         => sprintf(
					/* translators: %s is a referring URL. */
					__( 'Untagged visit whose referrer is %s.', 'bw-lead-ai' ),
					$ref[0]
				),
				'params'       => array(),
				'referrer'     => $ref[0],
				'preview_only' => true,
			);
		}

		// --- edge cases ---------------------------------------------------
		$out[] = array(
			'group'  => $group_edge,
			'label'  => __( 'UTM and gclid together', 'bw-lead-ai' ),
			'note'   => __( 'Both present at once — the UTM values must win over click-ID inference.', 'bw-lead-ai' ),
			'params' => array(
				'utm_source' => 'facebook',
				'utm_medium' => 'social',
				'gclid'      => 'bwtest-conflict',
			),
		);

		// A non-UTM alias pair, if this site has any configured.
		$alt_source = '';
		$alt_medium = '';
		foreach ( $aliases['standard']['source'] as $alias ) {
			if ( 0 !== strpos( $alias, 'utm_' ) ) {
				$alt_source = $alias;
				break;
			}
		}
		foreach ( $aliases['standard']['medium'] as $alias ) {
			if ( 0 !== strpos( $alias, 'utm_' ) ) {
				$alt_medium = $alias;
				break;
			}
		}
		if ( '' !== $alt_source && '' !== $alt_medium ) {
			$out[] = array(
				'group'  => $group_edge,
				'label'  => sprintf(
					/* translators: 1: source alias, 2: medium alias. */
					__( 'This site\'s own aliases — %1$s / %2$s', 'bw-lead-ai' ),
					$alt_source,
					$alt_medium
				),
				'note'   => __( 'Uses your non-UTM parameter aliases instead of utm_source / utm_medium.', 'bw-lead-ai' ),
				'params' => array( $alt_source => 'partner-site', $alt_medium => 'referral' ),
			);
		}

		// One row covering every configured custom dimension at once.
		if ( ! empty( $aliases['custom'] ) ) {
			$custom_params = array();
			$custom_keys   = array();
			foreach ( $aliases['custom'] as $key => $param_list ) {
				if ( empty( $param_list ) ) {
					continue;
				}
				$custom_params[ $param_list[0] ] = 'test-' . $key;
				$custom_keys[]                   = $key;
			}
			if ( ! empty( $custom_params ) ) {
				$custom_params['utm_source'] = 'google';
				$custom_params['utm_medium'] = 'cpc';
				$out[]                       = array(
					'group'  => $group_edge,
					'label'  => sprintf(
						/* translators: %s is a comma-separated list of custom dimension keys. */
						__( 'Custom dimensions — %s', 'bw-lead-ai' ),
						implode( ', ', $custom_keys )
					),
					'note'   => __( 'A tagged visit that also carries every custom dimension you have defined.', 'bw-lead-ai' ),
					'params' => $custom_params,
				);
			}
		}

		// Normalise the optional keys so the JS never has to guess.
		foreach ( $out as $index => $scenario ) {
			$out[ $index ]['referrer']     = isset( $scenario['referrer'] ) ? $scenario['referrer'] : '';
			$out[ $index ]['preview_only'] = ! empty( $scenario['preview_only'] );
		}

		return $out;
	}

	private function render_test_tab() {
		wp_enqueue_script( 'bw-lead-ai-capture' );
		wp_enqueue_script( 'bw-lead-ai-admin-test' );
		wp_localize_script(
			'bw-lead-ai-admin-test',
			'bwLeadAITest',
			array(
				'origin'    => untrailingslashit( home_url() ),
				'scenarios' => $this->test_scenarios(),
			)
		);
		?>
		<h2><?php esc_html_e( 'Live Capture State', 'bw-lead-ai' ); ?></h2>
		<p class="description"><?php esc_html_e( 'Shows the tracking state stored in your browser right now.', 'bw-lead-ai' ); ?></p>

		<details class="bw-lead-ai-section">
			<summary><h2><?php esc_html_e( 'Simulated test links', 'bw-lead-ai' ); ?></h2></summary>
			<p class="description" style="max-width: 900px;">
				<?php esc_html_e( 'A ready-made set of incoming links covering the traffic types this plugin has to tell apart. Pick a landing page, then open any link in a new tab and come back here to see what got logged.', 'bw-lead-ai' ); ?>
			</p>
			<p class="description" style="max-width: 900px;">
				<?php
				echo wp_kses(
					__( 'The <strong>resolves to</strong> column is computed live against <em>this site\'s</em> settings — your click-ID table, parameter aliases and channel mappings — so it doubles as a check that your configuration does what you expect. These are throwaway test links; they are not saved to the UTM Builder.', 'bw-lead-ai' ),
					array( 'strong' => array(), 'em' => array() )
				);
				?>
			</p>
			<p>
				<label for="bw-lead-ai-simlinks-path"><strong><?php esc_html_e( 'Landing page', 'bw-lead-ai' ); ?></strong></label><br>
				<code class="bw-lead-ai-simlinks-origin"><?php echo esc_html( untrailingslashit( home_url() ) ); ?></code>
				<input type="text" id="bw-lead-ai-simlinks-path" value="/" placeholder="/landing-page/" class="regular-text">
			</p>
			<div id="bw-lead-ai-simlinks-out">(<?php esc_html_e( 'loading', 'bw-lead-ai' ); ?>)</div>
		</details>

		<div class="bw-lead-ai-test">
			<div class="bw-lead-ai-test-controls">
				<h3><?php esc_html_e( 'URL Simulator', 'bw-lead-ai' ); ?></h3>
				<p class="description"><?php esc_html_e( 'Type any URL to see how the plugin would classify it — without navigating or storing anything.', 'bw-lead-ai' ); ?></p>
				<p>
					<input type="text" id="bw-lead-ai-sim-url" class="large-text" placeholder="https://yoursite.com/landing?utm_source=google&amp;utm_medium=cpc&amp;utm_campaign=spring-sale">
				</p>
				<p>
					<button type="button" class="button button-primary" id="bw-lead-ai-sim-run"><?php esc_html_e( 'Simulate', 'bw-lead-ai' ); ?></button>
					<button type="button" class="button" id="bw-lead-ai-sim-open"><?php esc_html_e( 'Open in new tab', 'bw-lead-ai' ); ?></button>
				</p>
				<div id="bw-lead-ai-sim-result"></div>
			</div>

			<div class="bw-lead-ai-test-panels">
				<h3><?php esc_html_e( 'Resolved merge tags (what forms would submit)', 'bw-lead-ai' ); ?></h3>
				<div id="bw-lead-ai-resolved" class="bw-lead-ai-panel">(loading)</div>

				<h3><?php esc_html_e( 'Summary counters', 'bw-lead-ai' ); ?></h3>
				<div id="bw-lead-ai-summary" class="bw-lead-ai-panel">(loading)</div>

				<details>
					<summary><?php esc_html_e( 'Visit history (JSON)', 'bw-lead-ai' ); ?></summary>
					<pre id="bw-lead-ai-visits" class="bw-lead-ai-pre">(loading)</pre>
				</details>

				<details>
					<summary><?php esc_html_e( 'Raw localStorage', 'bw-lead-ai' ); ?></summary>
					<pre id="bw-lead-ai-raw" class="bw-lead-ai-pre">(loading)</pre>
				</details>

				<p style="margin-top: 16px;">
					<button type="button" class="button" id="bw-lead-ai-refresh"><?php esc_html_e( 'Refresh state', 'bw-lead-ai' ); ?></button>
					<button type="button" class="button button-link-delete" id="bw-lead-ai-clear"><?php esc_html_e( 'Clear all tracking storage', 'bw-lead-ai' ); ?></button>
				</p>
			</div>
		</div>
		<?php
	}

	private function render_help_tab() {
		?>
		<h2><?php esc_html_e( 'Which merge tag should I use?', 'bw-lead-ai' ); ?></h2>
		<p><?php esc_html_e( 'Choose based on what you want to send to your CRM or email platform:', 'bw-lead-ai' ); ?></p>
		<table class="widefat striped" style="max-width: 800px;">
			<thead>
				<tr>
					<th><?php esc_html_e( 'I want to...', 'bw-lead-ai' ); ?></th>
					<th><?php esc_html_e( 'Use this merge tag', 'bw-lead-ai' ); ?></th>
				</tr>
			</thead>
			<tbody>
				<tr>
					<td><?php esc_html_e( 'Send one "Lead Source" field to Salesforce / HubSpot', 'bw-lead-ai' ); ?></td>
					<td><code>{bw:source_medium}</code></td>
				</tr>
				<tr>
					<td><?php esc_html_e( 'See which campaign generated the lead', 'bw-lead-ai' ); ?></td>
					<td><code>{bw:campaign}</code></td>
				</tr>
				<tr>
					<td><?php esc_html_e( 'See which keyword triggered the ad', 'bw-lead-ai' ); ?></td>
					<td><code>{bw:term}</code></td>
				</tr>
				<tr>
					<td><?php esc_html_e( 'Know what page they first landed on', 'bw-lead-ai' ); ?></td>
					<td><code>{bw:first_page}</code></td>
				</tr>
				<tr>
					<td><?php esc_html_e( 'Know what page they submitted the form on', 'bw-lead-ai' ); ?></td>
					<td><code>{bw:submit_page}</code></td>
				</tr>
				<tr>
					<td><?php esc_html_e( 'Compare the first vs. last source for multi-touch', 'bw-lead-ai' ); ?></td>
					<td><code>{bw:first_source}</code> + <code>{bw:source}</code></td>
				</tr>
				<tr>
					<td><?php esc_html_e( 'Dump the full attribution history into a notes field', 'bw-lead-ai' ); ?></td>
					<td><code>{bw:summary}</code></td>
				</tr>
				<tr>
					<td><?php esc_html_e( 'Track a custom parameter (e.g. match type)', 'bw-lead-ai' ); ?></td>
					<td><?php esc_html_e( 'Add a row in Parameter Aliases with a non-standard label — it becomes {bw:your_key} automatically.', 'bw-lead-ai' ); ?></td>
				</tr>
			</tbody>
		</table>

		<h2 style="margin-top: 24px;"><?php esc_html_e( 'All available merge tags', 'bw-lead-ai' ); ?></h2>
		<p><?php esc_html_e( 'Drop a Hidden field into your Gravity Form and set its default value to any tag below. The plugin replaces them with live tracking data before the form submits.', 'bw-lead-ai' ); ?></p>
		<table class="widefat striped" style="max-width: 800px;">
			<thead>
				<tr>
					<th><?php esc_html_e( 'Merge tag', 'bw-lead-ai' ); ?></th>
					<th><?php esc_html_e( 'Description', 'bw-lead-ai' ); ?></th>
				</tr>
			</thead>
			<tbody>
				<tr><td><code>{bw:source}</code></td><td><?php esc_html_e( 'Last visit source (e.g. google, facebook)', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:medium}</code></td><td><?php esc_html_e( 'Last visit medium (e.g. cpc, organic, social)', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:source_medium}</code></td><td><?php esc_html_e( 'Combined "source / medium" (configurable separator)', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:channel}</code></td><td><?php esc_html_e( 'Friendly channel label for the last visit, resolved from Channel Mappings (e.g. "Google Ads", "Email", "Direct"). Falls back to raw source / medium if no rule matches.', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:campaign}</code></td><td><?php esc_html_e( 'Last visit campaign name', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:term}</code></td><td><?php esc_html_e( 'Last visit keyword / term', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:content}</code></td><td><?php esc_html_e( 'Last visit ad content / creative', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:adgroup}</code></td><td><?php esc_html_e( 'Last visit ad group', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:first_page}</code></td><td><?php esc_html_e( 'First-ever landing page URL', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:last_page}</code></td><td><?php esc_html_e( 'Most recent landing page URL', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:submit_page}</code></td><td><?php esc_html_e( 'Page URL where the form was submitted (resolved live)', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:first_source}</code></td><td><?php esc_html_e( 'First-ever visit source', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:first_medium}</code></td><td><?php esc_html_e( 'First-ever visit medium', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:first_channel}</code></td><td><?php esc_html_e( 'Friendly channel label for the first visit.', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:visits}</code></td><td><?php esc_html_e( 'Total visit count', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:pages}</code></td><td><?php esc_html_e( 'Total page view count', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:tagged_visits}</code></td><td><?php esc_html_e( 'Number of visits that had UTM/click-ID tags', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:summary}</code></td><td><?php esc_html_e( 'Full attribution summary (dates, all sources, visit history)', 'bw-lead-ai' ); ?></td></tr>
				<tr><td><code>{bw:summary_detailed}</code></td><td><?php esc_html_e( 'Like {bw:summary} but journey expands to list every page visited within each touchpoint, including form submissions', 'bw-lead-ai' ); ?></td></tr>
				<?php
				$aliases = BW_Lead_AI_Settings::parse_parameter_aliases( BW_Lead_AI_Settings::get()['parameter_aliases'] );
				if ( ! empty( $aliases['custom'] ) ) {
					foreach ( $aliases['custom'] as $ckey => $cvals ) {
						printf(
							'<tr><td><code>{bw:%1$s}</code></td><td>%2$s <code>%3$s</code></td></tr>',
							esc_html( $ckey ),
							esc_html__( 'Custom dimension, populated from URL parameters:', 'bw-lead-ai' ),
							esc_html( implode( ', ', $cvals ) )
						);
					}
				}
				?>
			</tbody>
		</table>

		<h2 style="margin-top: 24px;"><?php esc_html_e( 'Setup example: Salesforce', 'bw-lead-ai' ); ?></h2>
		<ol>
			<li><?php esc_html_e( 'Add a Hidden field to your form, set default value to {bw:source_medium}', 'bw-lead-ai' ); ?></li>
			<li><?php esc_html_e( 'Add another Hidden field, set default value to {bw:summary}', 'bw-lead-ai' ); ?></li>
			<li><?php esc_html_e( 'Map both to your Salesforce fields in the GF Salesforce add-on.', 'bw-lead-ai' ); ?></li>
		</ol>

		<h2 style="margin-top: 24px;"><?php esc_html_e( 'Non-Gravity-Forms plugins', 'bw-lead-ai' ); ?></h2>
		<p><?php esc_html_e( 'Use the Form Fields tab to target hidden fields by id, class, name, or CSS selector. This works with any form plugin that renders standard HTML inputs.', 'bw-lead-ai' ); ?></p>

		<h2 style="margin-top: 24px;"><?php esc_html_e( 'How attribution works', 'bw-lead-ai' ); ?></h2>
		<p><?php esc_html_e( 'The plugin tracks three levels of attribution:', 'bw-lead-ai' ); ?></p>
		<ul class="ul-disc" style="margin-left: 2em;">
			<li><strong><?php esc_html_e( 'First touch', 'bw-lead-ai' ); ?></strong> — <?php esc_html_e( 'the very first interaction that brought the visitor to your site ({bw:first_source}, {bw:first_medium}, {bw:first_page}). Never overwritten.', 'bw-lead-ai' ); ?></li>
			<li><strong><?php esc_html_e( 'Last touch', 'bw-lead-ai' ); ?></strong> — <?php esc_html_e( 'the most recent campaign click ({bw:source}, {bw:medium}, etc.). Updated whenever the visitor returns with new UTM parameters or a click ID.', 'bw-lead-ai' ); ?></li>
			<li><strong><?php esc_html_e( 'Full journey', 'bw-lead-ai' ); ?></strong> — <?php esc_html_e( 'all touchpoints stored in the visit history ({bw:summary}). Useful for multi-touch attribution reporting.', 'bw-lead-ai' ); ?></li>
		</ul>
		<?php
	}
}
