<?php
/**
 * Sticky Elements module. Lets the user mark any element by CSS selector as
 * sticky-on-scroll, with per-element offset, z-index, push-up target, and a
 * mobile-disable breakpoint.
 *
 * Ported from the standalone `bw-sticky-settings` plugin. Selector prefix
 * `bw-` was changed to `bw-dev-` throughout (JS config name, CSS classes,
 * dispatched events) — sites that hand-wrote CSS against `.bw-is-sticky` must
 * update to `.bw-dev-is-sticky` after migration.
 *
 * @package BW_Dev
 */

defined( 'ABSPATH' ) || exit;

class BW_Dev_Module_Sticky implements BW_Dev_Module_Interface {

	/**
	 * Defaults for a single element row. Used as the wp_parse_args base in
	 * render_element_row() and as the shape sanitize() returns when fields
	 * are missing.
	 */
	private const ELEMENT_DEFAULTS = array(
		'selector'          => '',
		'offset'            => 0,
		'margin_bottom'     => 0,
		'push_element'      => '',
		'z_index'           => 999,
		'disable_mobile'    => 1,
		'mobile_breakpoint' => 1024,
		'enabled'           => 1,
	);

	public function slug(): string {
		return 'sticky';
	}

	public function label(): string {
		return __( 'Sticky Elements', 'bw-dev' );
	}

	public function group(): string {
		return 'editor_admin';
	}

	public function default_settings(): array {
		return array( 'elements' => array() );
	}

	public function sanitize( array $data ): array {
		$out = array( 'elements' => array() );
		if ( empty( $data['elements'] ) || ! is_array( $data['elements'] ) ) {
			return $out;
		}

		foreach ( $data['elements'] as $element ) {
			if ( ! is_array( $element ) ) {
				continue;
			}
			$selector = isset( $element['selector'] ) ? sanitize_text_field( wp_unslash( (string) $element['selector'] ) ) : '';
			// Skip rows the user added but never filled in — keeps the
			// option clean and avoids loading empty rules on the frontend.
			if ( '' === $selector ) {
				continue;
			}
			$out['elements'][] = array(
				'selector'          => $selector,
				'offset'            => isset( $element['offset'] ) ? absint( $element['offset'] ) : 0,
				'margin_bottom'     => isset( $element['margin_bottom'] ) ? absint( $element['margin_bottom'] ) : 0,
				'push_element'      => isset( $element['push_element'] ) ? sanitize_text_field( wp_unslash( (string) $element['push_element'] ) ) : '',
				'z_index'           => isset( $element['z_index'] ) ? max( 1, absint( $element['z_index'] ) ) : 999,
				'disable_mobile'    => ! empty( $element['disable_mobile'] ) ? 1 : 0,
				'mobile_breakpoint' => isset( $element['mobile_breakpoint'] ) ? min( 1200, max( 320, absint( $element['mobile_breakpoint'] ) ) ) : 1024,
				'enabled'           => ! empty( $element['enabled'] ) ? 1 : 0,
			);
		}

		return $out;
	}

	public function register(): void {
		add_action( 'wp_enqueue_scripts',    array( $this, 'frontend_enqueue' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue' ) );
	}

	public function frontend_enqueue(): void {
		$elements = (array) bw_dev()->settings()->get( $this->slug(), 'elements', array() );
		$enabled  = array_values( array_filter(
			$elements,
			static function ( $el ) {
				return is_array( $el ) && ! empty( $el['enabled'] );
			}
		) );
		if ( empty( $enabled ) ) {
			return;
		}
		wp_enqueue_script(
			'bw-dev-sticky',
			BW_DEV_URL . 'assets/js/sticky.js',
			array(),
			BW_DEV_VERSION,
			true
		);
		wp_localize_script(
			'bw-dev-sticky',
			'bwDevStickyConfig',
			array(
				'elements' => $enabled,
			)
		);
	}

	public function admin_enqueue( $hook ): void {
		if ( 'settings_page_' . BW_Dev_Admin_Page::MENU_SLUG !== $hook ) {
			return;
		}
		$query = wp_unslash( $_GET ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$tab   = isset( $query['tab'] ) ? sanitize_key( (string) $query['tab'] ) : 'modules';
		if ( $this->slug() !== $tab ) {
			return;
		}
		wp_enqueue_style(
			'bw-dev-sticky-admin',
			BW_DEV_URL . 'assets/css/sticky-admin.css',
			array(),
			BW_DEV_VERSION
		);
		wp_enqueue_script(
			'bw-dev-sticky-admin',
			BW_DEV_URL . 'assets/js/sticky-admin.js',
			array( 'jquery' ),
			BW_DEV_VERSION,
			true
		);
		wp_localize_script(
			'bw-dev-sticky-admin',
			'bwDevStickyAdmin',
			array(
				'confirmRemove'   => __( 'Remove this sticky element?', 'bw-dev' ),
				'newElementLabel' => __( 'New sticky element', 'bw-dev' ),
			)
		);
	}

	public function render_tab(): void {
		$elements = (array) bw_dev()->settings()->get( $this->slug(), 'elements', array() );
		?>
		<p class="description">
			<?php esc_html_e( 'Mark any element on the page as sticky on scroll. Each row targets one CSS selector with its own offset, z-index, push-up element, and mobile-disable breakpoint.', 'bw-dev' ); ?>
		</p>

		<div class="bw-dev-sticky-wrap">
			<div id="bw-dev-sticky-elements">
				<?php foreach ( $elements as $index => $element ) {
					$this->render_element_row( (string) $index, is_array( $element ) ? $element : array() );
				} ?>
			</div>

			<p>
				<button type="button" id="bw-dev-add-element" class="button button-secondary">
					+ <?php esc_html_e( 'Add sticky element', 'bw-dev' ); ?>
				</button>
			</p>

			<script type="text/template" id="bw-dev-element-template">
				<?php $this->render_element_row( '{{INDEX}}', array() ); ?>
			</script>
		</div>
		<?php
	}

	/**
	 * Render one collapsible element row. Used both for existing rows and
	 * (via the <script type="text/template"> block) for newly-added rows.
	 *
	 * @param string $index   Numeric index, or the literal "{{INDEX}}"
	 *                        placeholder when rendering the template.
	 * @param array  $element Element config (may be empty for a fresh row).
	 */
	private function render_element_row( string $index, array $element ): void {
		$element = wp_parse_args( $element, self::ELEMENT_DEFAULTS );
		$prefix  = BW_Dev_Settings::OPTION . '[' . $this->slug() . '][elements][' . $index . ']';
		?>
		<div class="bw-dev-sticky-element" data-index="<?php echo esc_attr( $index ); ?>">
			<div class="bw-dev-sticky-element-header">
				<span class="bw-dev-sticky-element-title">
					<?php if ( '' !== $element['selector'] ) : ?>
						<code><?php echo esc_html( $element['selector'] ); ?></code>
					<?php else : ?>
						<?php esc_html_e( 'New sticky element', 'bw-dev' ); ?>
					<?php endif; ?>
				</span>
				<label class="bw-dev-sticky-enabled-toggle">
					<input type="checkbox" name="<?php echo esc_attr( $prefix ); ?>[enabled]" value="1" <?php checked( $element['enabled'], 1 ); ?> />
					<?php esc_html_e( 'Enabled', 'bw-dev' ); ?>
				</label>
				<button type="button" class="bw-dev-toggle-element button button-small">
					<?php esc_html_e( 'Edit', 'bw-dev' ); ?>
				</button>
				<button type="button" class="bw-dev-remove-element button button-small button-link-delete">
					<?php esc_html_e( 'Remove', 'bw-dev' ); ?>
				</button>
			</div>

			<div class="bw-dev-sticky-element-body" style="display:none;">
				<table class="form-table" role="presentation">
					<tr>
						<th scope="row">
							<label for="<?php echo esc_attr( $prefix ); ?>_selector"><?php esc_html_e( 'Selector', 'bw-dev' ); ?></label>
						</th>
						<td>
							<input type="text" id="<?php echo esc_attr( $prefix ); ?>_selector" name="<?php echo esc_attr( $prefix ); ?>[selector]" value="<?php echo esc_attr( $element['selector'] ); ?>" class="regular-text" placeholder="#my-element or .my-class" />
							<p class="description"><?php esc_html_e( 'CSS selector (ID or class) of the element to make sticky.', 'bw-dev' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row">
							<label for="<?php echo esc_attr( $prefix ); ?>_offset"><?php esc_html_e( 'Top offset', 'bw-dev' ); ?></label>
						</th>
						<td>
							<input type="number" id="<?php echo esc_attr( $prefix ); ?>_offset" name="<?php echo esc_attr( $prefix ); ?>[offset]" value="<?php echo esc_attr( (string) $element['offset'] ); ?>" class="small-text" min="0" /> px
							<p class="description"><?php esc_html_e( 'Distance from the top of the viewport when sticky.', 'bw-dev' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row">
							<label for="<?php echo esc_attr( $prefix ); ?>_margin_bottom"><?php esc_html_e( 'Margin bottom', 'bw-dev' ); ?></label>
						</th>
						<td>
							<input type="number" id="<?php echo esc_attr( $prefix ); ?>_margin_bottom" name="<?php echo esc_attr( $prefix ); ?>[margin_bottom]" value="<?php echo esc_attr( (string) $element['margin_bottom'] ); ?>" class="small-text" min="0" /> px
							<p class="description"><?php esc_html_e( 'Space below the sticky element when sticky.', 'bw-dev' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row">
							<label for="<?php echo esc_attr( $prefix ); ?>_push_element"><?php esc_html_e( 'Push-up element', 'bw-dev' ); ?></label>
						</th>
						<td>
							<input type="text" id="<?php echo esc_attr( $prefix ); ?>_push_element" name="<?php echo esc_attr( $prefix ); ?>[push_element]" value="<?php echo esc_attr( $element['push_element'] ); ?>" class="regular-text" placeholder="#footer or .site-footer" />
							<p class="description"><?php esc_html_e( 'Optional. Element that will push the sticky element up when reached.', 'bw-dev' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row">
							<label for="<?php echo esc_attr( $prefix ); ?>_z_index"><?php esc_html_e( 'Z-index', 'bw-dev' ); ?></label>
						</th>
						<td>
							<input type="number" id="<?php echo esc_attr( $prefix ); ?>_z_index" name="<?php echo esc_attr( $prefix ); ?>[z_index]" value="<?php echo esc_attr( (string) $element['z_index'] ); ?>" class="small-text" min="1" />
							<p class="description"><?php esc_html_e( 'Stack order of the sticky element.', 'bw-dev' ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope="row"><?php esc_html_e( 'Mobile', 'bw-dev' ); ?></th>
						<td>
							<label>
								<input type="checkbox" name="<?php echo esc_attr( $prefix ); ?>[disable_mobile]" value="1" <?php checked( $element['disable_mobile'], 1 ); ?> />
								<?php esc_html_e( 'Disable on mobile devices', 'bw-dev' ); ?>
							</label>
							<br /><br />
							<label>
								<?php esc_html_e( 'Mobile breakpoint:', 'bw-dev' ); ?>
								<input type="number" name="<?php echo esc_attr( $prefix ); ?>[mobile_breakpoint]" value="<?php echo esc_attr( (string) $element['mobile_breakpoint'] ); ?>" class="small-text" min="320" max="1200" /> px
							</label>
							<p class="description"><?php esc_html_e( 'Sticky behavior disabled below this screen width.', 'bw-dev' ); ?></p>
						</td>
					</tr>
				</table>
			</div>
		</div>
		<?php
	}

	public function uninstall(): void {
		// No-op. Root option drop handles persisted state.
	}
}
