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

/**
 * Gravity Forms custom field integration.
 *
 * Registers the "BW Lead Data" field type and its form-editor UI. Everything in
 * here is admin-side: the field itself renders a `{bw:*}` merge tag into a
 * hidden input, and the existing capture script resolves it before submit. No
 * new front-end JavaScript is involved.
 *
 * The GF_Field subclass lives in a separate file because it extends GF_Field,
 * which does not exist until Gravity Forms has loaded.
 */
class BW_Lead_AI_GF_Field {

	/** GF field type slug. */
	const TYPE = 'bw_lead_ai_datapoint';

	/** Field property holding the chosen data point. */
	const PROP = 'bwDataPoint';

	/** Used when a field has no stored data point, or its stored one is invalid. */
	const DEFAULT_POINT = 'source_medium';

	/** Data points whose value spans multiple lines — rendered as a textarea. */
	const MULTILINE_POINTS = array( 'summary', 'summary_detailed' );

	/** The editor setting's CSS class, referenced by the field's settings list. */
	const SETTING_CLASS = 'bw_lead_ai_datapoint_setting';

	private static $instance = null;

	/** Guards against registering the field type twice. */
	private $field_registered = false;

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

	public function register() {
		// The field type can only be registered once GF_Field exists, which GF
		// signals with `gform_loaded`. GF fires that from its own `plugins_loaded`
		// callback at the same priority as ours, so whether it has already fired
		// by the time we boot depends on plugin load order — handle both cases.
		if ( did_action( 'gform_loaded' ) ) {
			$this->register_field();
		} else {
			add_action( 'gform_loaded', array( $this, 'register_field' ), 5 );
		}

		add_action( 'gform_field_standard_settings', array( $this, 'render_setting' ), 10, 2 );
		add_action( 'gform_editor_js', array( $this, 'editor_js' ) );
		add_filter( 'gform_tooltips', array( $this, 'tooltips' ) );
	}

	public function register_field() {
		if ( $this->field_registered ) {
			return;
		}
		if ( ! class_exists( 'GF_Fields' ) || ! class_exists( 'GF_Field' ) ) {
			return;
		}
		require_once BW_LEAD_AI_DIR . 'includes/class-bw-lead-ai-gf-field-datapoint.php';
		if ( ! class_exists( 'BW_Lead_AI_GF_Field_Datapoint' ) ) {
			return;
		}
		GF_Fields::register( new BW_Lead_AI_GF_Field_Datapoint() );
		$this->field_registered = true;
	}

	/**
	 * The grouped list of selectable data points.
	 *
	 * Built-ins are grouped by what they describe (last touch / first touch /
	 * counts / summary). Custom dimensions declared in Parameter Aliases are
	 * appended as their own group, so a client who adds `match_type` gets it in
	 * the dropdown without any code change.
	 *
	 * @return array [ group_key => [ 'label' => string, 'points' => [ key => label ] ] ]
	 */
	public static function data_point_groups() {
		$groups = array(
			'latest'  => array(
				'label'  => __( 'Latest visit', 'bw-lead-ai' ),
				'points' => array(
					'source'        => __( 'Source', 'bw-lead-ai' ),
					'medium'        => __( 'Medium', 'bw-lead-ai' ),
					'source_medium' => __( 'Source / Medium', 'bw-lead-ai' ),
					'channel'       => __( 'Channel', 'bw-lead-ai' ),
					'campaign'      => __( 'Campaign', 'bw-lead-ai' ),
					'term'          => __( 'Term / keyword', 'bw-lead-ai' ),
					'content'       => __( 'Content / creative', 'bw-lead-ai' ),
					'adgroup'       => __( 'Ad group', 'bw-lead-ai' ),
					'last_page'     => __( 'Landing page', 'bw-lead-ai' ),
					'submit_page'   => __( 'Submit page', 'bw-lead-ai' ),
				),
			),
			'first'   => array(
				'label'  => __( 'First visit', 'bw-lead-ai' ),
				'points' => array(
					'first_source'  => __( 'First source', 'bw-lead-ai' ),
					'first_medium'  => __( 'First medium', 'bw-lead-ai' ),
					'first_channel' => __( 'First channel', 'bw-lead-ai' ),
					'first_page'    => __( 'First landing page', 'bw-lead-ai' ),
				),
			),
			'counts'  => array(
				'label'  => __( 'Counts', 'bw-lead-ai' ),
				'points' => array(
					'visits'        => __( 'Visits', 'bw-lead-ai' ),
					'pages'         => __( 'Pages viewed', 'bw-lead-ai' ),
					'tagged_visits' => __( 'Tagged visits', 'bw-lead-ai' ),
				),
			),
			'summary' => array(
				'label'  => __( 'Summary', 'bw-lead-ai' ),
				'points' => array(
					'summary'          => __( 'Summary', 'bw-lead-ai' ),
					'summary_detailed' => __( 'Summary (detailed)', 'bw-lead-ai' ),
				),
			),
		);

		$settings = BW_Lead_AI_Settings::get();

		// Analytics points only appear once GA client ID capture is switched on.
		if ( ! empty( $settings['capture_ga_client_id'] ) ) {
			$groups['analytics'] = array(
				'label'  => __( 'Analytics', 'bw-lead-ai' ),
				'points' => array(
					'ga_client_id' => __( 'GA client ID', 'bw-lead-ai' ),
				),
			);
		}

		// Interaction points only appear once interaction tracking is switched
		// on, so the dropdown stays short for sites that don't use it.
		$event_types = BW_Lead_AI_Settings::parse_event_types( $settings['event_types'] );
		if ( ! empty( $event_types ) ) {
			$points = array(
				'events'      => __( 'Interactions (total count)', 'bw-lead-ai' ),
				'events_list' => __( 'Interactions (breakdown)', 'bw-lead-ai' ),
			);
			foreach ( $event_types as $event_type ) {
				$points[ 'event.' . $event_type ] = sprintf(
					/* translators: %s is an interaction type label, e.g. "Video plays". */
					__( '%s (count)', 'bw-lead-ai' ),
					self::event_type_label( $event_type )
				);
			}
			$groups['events'] = array(
				'label'  => __( 'Interactions', 'bw-lead-ai' ),
				'points' => $points,
			);
		}

		$aliases = BW_Lead_AI_Settings::parse_parameter_aliases( $settings['parameter_aliases'] );
		$custom  = array();
		foreach ( array_keys( $aliases['custom'] ) as $key ) {
			$custom[ $key ] = $key;
		}
		if ( ! empty( $custom ) ) {
			$groups['custom'] = array(
				'label'  => __( 'Custom dimensions', 'bw-lead-ai' ),
				'points' => $custom,
			);
		}

		return $groups;
	}

	/**
	 * Display label for an interaction event type. Shared by the field dropdown
	 * and the Events settings tab so the wording stays consistent.
	 */
	public static function event_type_label( $type ) {
		$labels = array(
			'video'      => __( 'Video plays', 'bw-lead-ai' ),
			'download'   => __( 'File downloads', 'bw-lead-ai' ),
			'phone'      => __( 'Phone clicks', 'bw-lead-ai' ),
			'email'      => __( 'Email clicks', 'bw-lead-ai' ),
			'social'     => __( 'Social clicks', 'bw-lead-ai' ),
			'outbound'   => __( 'Outbound clicks', 'bw-lead-ai' ),
			'custom'     => __( 'Custom actions', 'bw-lead-ai' ),
			'scroll'     => __( 'Scroll depth', 'bw-lead-ai' ),
			'form_start' => __( 'Form starts', 'bw-lead-ai' ),
		);
		return isset( $labels[ $type ] ) ? $labels[ $type ] : $type;
	}

	/**
	 * Whether $point is a data point this plugin can currently resolve.
	 */
	public static function is_valid_point( $point ) {
		foreach ( self::data_point_groups() as $group ) {
			if ( isset( $group['points'][ $point ] ) ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Human-readable label for a data point, falling back to the raw key.
	 */
	public static function label_for( $point ) {
		foreach ( self::data_point_groups() as $group ) {
			if ( isset( $group['points'][ $point ] ) ) {
				return $group['points'][ $point ];
			}
		}
		return $point;
	}

	/**
	 * Whether this data point's value spans multiple lines (textarea, not input).
	 */
	public static function is_multiline( $point ) {
		return in_array( $point, self::MULTILINE_POINTS, true );
	}

	/**
	 * The read-only text shown in the form editor's field preview. Mirrored by
	 * the editor JS so the preview updates the moment the dropdown changes —
	 * keep the two in sync if this format ever changes.
	 */
	public static function preview_text( $point ) {
		return self::label_for( $point ) . '  —  {bw:' . $point . '}';
	}

	/**
	 * Whether the current request should render BW Lead Data fields visibly.
	 *
	 * Requires the plugin's global Debug mode setting AND the debug capability.
	 * Defaults to `manage_options` rather than "any logged-in user" so tracking
	 * internals aren't exposed to subscribers on membership sites. Widen it with
	 * the `bw_lead_ai_debug_capability` filter if a site genuinely needs that.
	 */
	public static function debug_visible() {
		$settings = BW_Lead_AI_Settings::get();
		if ( empty( $settings['debug'] ) ) {
			return false;
		}
		$capability = apply_filters( 'bw_lead_ai_debug_capability', 'manage_options' );
		return current_user_can( $capability );
	}

	/**
	 * Render the data point picker in the form editor's field settings panel.
	 */
	public function render_setting( $position, $form_id ) {
		if ( 25 !== (int) $position ) {
			return;
		}
		?>
		<li class="<?php echo esc_attr( self::SETTING_CLASS ); ?> field_setting">
			<label for="bw_lead_ai_datapoint_select" class="section_label">
				<?php esc_html_e( 'Data point', 'bw-lead-ai' ); ?>
				<?php gform_tooltip( self::SETTING_CLASS ); ?>
			</label>
			<select id="bw_lead_ai_datapoint_select">
				<?php foreach ( self::data_point_groups() as $group ) : ?>
					<optgroup label="<?php echo esc_attr( $group['label'] ); ?>">
						<?php foreach ( $group['points'] as $key => $label ) : ?>
							<option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $label ); ?></option>
						<?php endforeach; ?>
					</optgroup>
				<?php endforeach; ?>
			</select>
			<p class="description">
				<?php esc_html_e( 'Which piece of attribution data this field captures. It is submitted with the entry but stays hidden from the visitor.', 'bw-lead-ai' ); ?>
			</p>
		</li>
		<?php
	}

	public function tooltips( $tooltips ) {
		$tooltips[ self::SETTING_CLASS ] = '<h6>' . esc_html__( 'Data point', 'bw-lead-ai' ) . '</h6>'
			. esc_html__( 'Choose what this field records — for example Source / Medium for "google / cpc", or Summary for the full attribution readout. The value is resolved in the visitor\'s browser at submit time.', 'bw-lead-ai' );
		return $tooltips;
	}

	/**
	 * Wire the picker into the editor: load the field's stored value when a
	 * field is selected, and persist changes back onto the field.
	 */
	public function editor_js() {
		$type    = wp_json_encode( self::TYPE );
		$prop    = wp_json_encode( self::PROP );
		$default = wp_json_encode( self::DEFAULT_POINT );
		?>
		<script type="text/javascript">
		( function ( $ ) {
			var BW_TYPE    = <?php echo $type; // phpcs:ignore WordPress.Security.EscapeOutput -- wp_json_encode output. ?>;
			var BW_PROP    = <?php echo $prop; // phpcs:ignore WordPress.Security.EscapeOutput -- wp_json_encode output. ?>;
			var BW_DEFAULT = <?php echo $default; // phpcs:ignore WordPress.Security.EscapeOutput -- wp_json_encode output. ?>;

			// Tracked so the change handler knows which field's preview to refresh.
			var currentFieldId = null;

			$( document ).on( 'gform_load_field_settings', function ( event, field ) {
				if ( ! field || field.type !== BW_TYPE ) {
					currentFieldId = null;
					return;
				}
				currentFieldId = field.id;
				var current = field[ BW_PROP ] || BW_DEFAULT;
				$( '#bw_lead_ai_datapoint_select' ).val( current );
				// Persist the fallback so a newly dropped field saves with a value
				// instead of relying on the PHP-side default forever.
				if ( ! field[ BW_PROP ] ) {
					SetFieldProperty( BW_PROP, current );
				}
			} );

			$( document ).on( 'change', '#bw_lead_ai_datapoint_select', function () {
				var value = $( this ).val();
				SetFieldProperty( BW_PROP, value );

				// GF does not re-render a custom field's preview on a property
				// change, so update the preview text in place. Format must match
				// BW_Lead_AI_GF_Field::preview_text() on the PHP side.
				if ( null !== currentFieldId ) {
					var label = $( this ).find( 'option:selected' ).text();
					$( '#field_' + currentFieldId + ' .bw-lead-ai-preview' )
						.val( label + '  —  {bw:' + value + '}' );
				}
			} );
		} )( jQuery );
		</script>
		<?php
	}
}
