<?php
/**
 * Rules tab - the main find/replace rules editor.
 *
 * Owns its own POST handler + render. Pro fields are accepted on submit but
 * only have effect when a license is active (the rule engine checks
 * ESRN_Freemius_Gate::is_pro() before applying them).
 *
 * @package EasySearchReplace
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/* ---------------------------------------------------------------------------
 * Submit handler
 * ------------------------------------------------------------------------- */

/**
 * Handle the Rules tab form submission.
 *
 * Security:
 *  - capability check is enforced by the dispatcher (manage_options).
 *  - nonce is verified here.
 *  - every scalar is unslashed + sanitized before persisting.
 *
 * @return array|null Sanitized options array to save, or null if no submit.
 */
function esrn_rules_handle_submit() {
	// phpcs:disable WordPress.Security.NonceVerification.Missing -- verified directly below.
	if ( ! isset( $_POST['esrn_search_replace_submit'] ) ) {
		return null;
	}

	if ( ! current_user_can( 'manage_options' ) ) {
		return null;
	}

	check_admin_referer( 'esrn_search_replace_options' );

	$raw_search   = isset( $_POST['esrn_search_replace_search'] )      && is_array( $_POST['esrn_search_replace_search'] )      ? (array) $_POST['esrn_search_replace_search']      : array();
	$raw_replace  = isset( $_POST['esrn_search_replace_replace'] )     && is_array( $_POST['esrn_search_replace_replace'] )     ? (array) $_POST['esrn_search_replace_replace']     : array();
	$raw_selector = isset( $_POST['esrn_search_replace_selector'] )    && is_array( $_POST['esrn_search_replace_selector'] )    ? (array) $_POST['esrn_search_replace_selector']    : array();
	$raw_posttype = isset( $_POST['esrn_search_replace_post_type'] )   && is_array( $_POST['esrn_search_replace_post_type'] )   ? (array) $_POST['esrn_search_replace_post_type']   : array();
	$raw_postids  = isset( $_POST['esrn_search_replace_post_ids'] )    && is_array( $_POST['esrn_search_replace_post_ids'] )    ? (array) $_POST['esrn_search_replace_post_ids']    : array();
	$raw_urls     = isset( $_POST['esrn_search_replace_urls'] )        && is_array( $_POST['esrn_search_replace_urls'] )        ? (array) $_POST['esrn_search_replace_urls']        : array();
	$raw_ignore   = isset( $_POST['esrn_search_replace_ignore_case'] ) && is_array( $_POST['esrn_search_replace_ignore_case'] ) ? (array) $_POST['esrn_search_replace_ignore_case'] : array();
	$raw_enabled  = isset( $_POST['esrn_search_replace_enabled'] )     && is_array( $_POST['esrn_search_replace_enabled'] )     ? (array) $_POST['esrn_search_replace_enabled']     : array();

	// Pro fields - accepted regardless of license, gated at runtime.
	$raw_regex        = isset( $_POST['esrn_search_replace_is_regex'] )           && is_array( $_POST['esrn_search_replace_is_regex'] )           ? (array) $_POST['esrn_search_replace_is_regex']           : array();
	$raw_between      = isset( $_POST['esrn_search_replace_between_active'] )     && is_array( $_POST['esrn_search_replace_between_active'] )     ? (array) $_POST['esrn_search_replace_between_active']     : array();
	$raw_between_s    = isset( $_POST['esrn_search_replace_between_start'] )      && is_array( $_POST['esrn_search_replace_between_start'] )      ? (array) $_POST['esrn_search_replace_between_start']      : array();
	$raw_between_e    = isset( $_POST['esrn_search_replace_between_end'] )        && is_array( $_POST['esrn_search_replace_between_end'] )        ? (array) $_POST['esrn_search_replace_between_end']        : array();
	$raw_excl_pt      = isset( $_POST['esrn_search_replace_exclude_post_type'] )  && is_array( $_POST['esrn_search_replace_exclude_post_type'] )  ? (array) $_POST['esrn_search_replace_exclude_post_type']  : array();
	$raw_excl_pid     = isset( $_POST['esrn_search_replace_exclude_post_ids'] )   && is_array( $_POST['esrn_search_replace_exclude_post_ids'] )   ? (array) $_POST['esrn_search_replace_exclude_post_ids']   : array();
	$raw_excl_urls    = isset( $_POST['esrn_search_replace_exclude_urls'] )       && is_array( $_POST['esrn_search_replace_exclude_urls'] )       ? (array) $_POST['esrn_search_replace_exclude_urls']       : array();
	$raw_sched        = isset( $_POST['esrn_search_replace_schedule'] )           && is_array( $_POST['esrn_search_replace_schedule'] )           ? (array) $_POST['esrn_search_replace_schedule']           : array();
	$raw_roles        = isset( $_POST['esrn_search_replace_roles'] )              && is_array( $_POST['esrn_search_replace_roles'] )              ? (array) $_POST['esrn_search_replace_roles']              : array();
	$raw_logged_in    = isset( $_POST['esrn_search_replace_logged_in'] )          && is_array( $_POST['esrn_search_replace_logged_in'] )          ? (array) $_POST['esrn_search_replace_logged_in']          : array();
	$raw_device       = isset( $_POST['esrn_search_replace_device'] )             && is_array( $_POST['esrn_search_replace_device'] )             ? (array) $_POST['esrn_search_replace_device']             : array();
	// phpcs:enable WordPress.Security.NonceVerification.Missing

	$options = array( 'fields' => array() );
	$count   = count( $raw_search );

	for ( $i = 0; $i < $count; $i++ ) {
		// Find / Replace: arbitrary text/HTML by design - only unslashed.
		$search  = isset( $raw_search[ $i ] )  ? (string) wp_unslash( $raw_search[ $i ] )  : '';
		$replace = isset( $raw_replace[ $i ] ) ? (string) wp_unslash( $raw_replace[ $i ] ) : '';

		if ( '' === $search ) {
			continue; // Skip empty rules.
		}

		$selector = isset( $raw_selector[ $i ] ) ? sanitize_text_field( wp_unslash( $raw_selector[ $i ] ) ) : '';

		$post_types        = esrn_sanitize_slug_list( isset( $raw_posttype[ $i ] ) ? $raw_posttype[ $i ] : array() );
		$exclude_post_types = esrn_sanitize_slug_list( isset( $raw_excl_pt[ $i ] ) ? $raw_excl_pt[ $i ] : array() );

		$post_ids         = esrn_sanitize_id_csv( isset( $raw_postids[ $i ] )  ? (string) wp_unslash( $raw_postids[ $i ] )  : '' );
		$exclude_post_ids = esrn_sanitize_id_csv( isset( $raw_excl_pid[ $i ] ) ? (string) wp_unslash( $raw_excl_pid[ $i ] ) : '' );

		$urls         = esrn_sanitize_url_list( isset( $raw_urls[ $i ] )      ? (string) wp_unslash( $raw_urls[ $i ] )      : '' );
		$exclude_urls = esrn_sanitize_url_list( isset( $raw_excl_urls[ $i ] ) ? (string) wp_unslash( $raw_excl_urls[ $i ] ) : '' );

		$ignore_case = ( isset( $raw_ignore[ $i ] )  && $raw_ignore[ $i ] )  ? 1 : 0;
		$enabled     = ( isset( $raw_enabled[ $i ] ) && $raw_enabled[ $i ] ) ? 1 : 0;
		$is_regex    = ( isset( $raw_regex[ $i ] )   && $raw_regex[ $i ] )   ? 1 : 0;

		// "Match between" mode is mutually exclusive with regex - if both
		// are submitted, between wins because it's the simpler/safer choice.
		$between_active = ( isset( $raw_between[ $i ] ) && $raw_between[ $i ] ) ? 1 : 0;
		$between_start  = isset( $raw_between_s[ $i ] ) ? (string) wp_unslash( $raw_between_s[ $i ] ) : '';
		$between_end    = isset( $raw_between_e[ $i ] ) ? (string) wp_unslash( $raw_between_e[ $i ] ) : '';
		if ( $between_active ) {
			$is_regex = 0;
		}

		// Schedule: structured sub-array.
		$schedule = esrn_sanitize_schedule( isset( $raw_sched[ $i ] ) && is_array( $raw_sched[ $i ] ) ? $raw_sched[ $i ] : array() );

		// Roles: array of role slugs.
		$roles = array();
		if ( isset( $raw_roles[ $i ] ) && is_array( $raw_roles[ $i ] ) ) {
			foreach ( $raw_roles[ $i ] as $role ) {
				$role = sanitize_key( wp_unslash( $role ) );
				if ( '' !== $role ) {
					$roles[] = $role;
				}
			}
			$roles = array_values( array_unique( $roles ) );
		}

		$logged_in = isset( $raw_logged_in[ $i ] ) ? sanitize_key( wp_unslash( $raw_logged_in[ $i ] ) ) : 'any';
		if ( ! in_array( $logged_in, array( 'any', 'in', 'out' ), true ) ) {
			$logged_in = 'any';
		}

		$device = isset( $raw_device[ $i ] ) ? sanitize_key( wp_unslash( $raw_device[ $i ] ) ) : 'any';
		if ( ! in_array( $device, array( 'any', 'desktop', 'mobile', 'tablet' ), true ) ) {
			$device = 'any';
		}

		$options['fields'][] = array(
			'enabled'            => $enabled,
			'search'             => $search,
			'replace'             => $replace,
			'ignore_case'        => $ignore_case,
			'post_types'         => $post_types,
			'post_ids'           => $post_ids,
			'urls'               => $urls,
			'is_regex'           => $is_regex,
			'between_active'     => $between_active,
			'between_start'      => $between_start,
			'between_end'        => $between_end,
			'selector'           => $selector,
			'schedule'           => $schedule,
			'roles'              => $roles,
			'logged_in'          => $logged_in,
			'device'             => $device,
			'exclude_post_types' => $exclude_post_types,
			'exclude_post_ids'   => $exclude_post_ids,
			'exclude_urls'       => $exclude_urls,
		);
	}

	return $options;
}

/* ---------------------------------------------------------------------------
 * Shared sanitization helpers
 * ------------------------------------------------------------------------- */

/**
 * Sanitize a list of slugs (post type names, role names, etc.).
 *
 * @param mixed $raw Raw POST value.
 * @return array
 */
function esrn_sanitize_slug_list( $raw ) {
	if ( ! is_array( $raw ) ) {
		return array();
	}
	$out = array();
	foreach ( $raw as $slug ) {
		$slug = sanitize_key( wp_unslash( $slug ) );
		if ( '' !== $slug ) {
			$out[] = $slug;
		}
	}
	return array_values( array_unique( $out ) );
}

/**
 * Sanitize a comma-separated list of integer IDs, returning a clean CSV string.
 *
 * @param string $raw Raw POST value (pre-unslashed).
 * @return string
 */
function esrn_sanitize_id_csv( $raw ) {
	$raw = (string) $raw;
	if ( '' === $raw ) {
		return '';
	}
	$ids = array_filter( array_map( 'intval', array_map( 'trim', explode( ',', $raw ) ) ) );
	return implode( ',', $ids );
}

/**
 * Sanitize a newline-separated list of URLs / URL patterns.
 *
 * @param string $raw Raw POST value.
 * @return string
 */
function esrn_sanitize_url_list( $raw ) {
	$raw = (string) $raw;
	if ( '' === $raw ) {
		return '';
	}
	$lines = array_filter( array_map( 'trim', explode( "\n", $raw ) ) );
	$clean = array();
	foreach ( $lines as $line ) {
		if ( preg_match( '#^https?://#i', $line ) ) {
			$candidate = esc_url_raw( $line );
		} else {
			$candidate = sanitize_text_field( $line );
		}
		if ( '' !== $candidate ) {
			$clean[] = $candidate;
		}
	}
	return implode( "\n", $clean );
}

/**
 * Sanitize the schedule sub-array.
 *
 * @param array $raw Raw schedule data.
 * @return array
 */
function esrn_sanitize_schedule( $raw ) {
	$out = array(
		'enabled'    => ( ! empty( $raw['enabled'] ) ) ? 1 : 0,
		'start_date' => isset( $raw['start_date'] ) ? sanitize_text_field( wp_unslash( $raw['start_date'] ) ) : '',
		'end_date'   => isset( $raw['end_date'] )   ? sanitize_text_field( wp_unslash( $raw['end_date'] ) )   : '',
		'start_time' => isset( $raw['start_time'] ) ? sanitize_text_field( wp_unslash( $raw['start_time'] ) ) : '',
		'end_time'   => isset( $raw['end_time'] )   ? sanitize_text_field( wp_unslash( $raw['end_time'] ) )   : '',
		'days'       => array(),
	);

	// Basic date format validation (YYYY-MM-DD) - leave empty if malformed.
	foreach ( array( 'start_date', 'end_date' ) as $k ) {
		if ( '' !== $out[ $k ] && ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $out[ $k ] ) ) {
			$out[ $k ] = '';
		}
	}
	foreach ( array( 'start_time', 'end_time' ) as $k ) {
		if ( '' !== $out[ $k ] && ! preg_match( '/^\d{2}:\d{2}$/', $out[ $k ] ) ) {
			$out[ $k ] = '';
		}
	}

	if ( isset( $raw['days'] ) && is_array( $raw['days'] ) ) {
		foreach ( $raw['days'] as $d ) {
			$d = (int) $d;
			if ( $d >= 0 && $d <= 6 ) {
				$out['days'][] = $d;
			}
		}
		$out['days'] = array_values( array_unique( $out['days'] ) );
	}

	return $out;
}

/* ---------------------------------------------------------------------------
 * Render
 * ------------------------------------------------------------------------- */

/**
 * Render the Rules tab.
 */
function esrn_render_tab_rules() {
	// Handle submit before render so saved values show immediately.
	$submitted = esrn_rules_handle_submit();
	if ( null !== $submitted ) {
		if ( empty( $submitted['fields'] ) ) {
			delete_option( ESRN_OPTION_KEY );
		} elseif ( null === get_option( ESRN_OPTION_KEY, null ) ) {
			add_option( ESRN_OPTION_KEY, $submitted, '', 'no' );
		} else {
			update_option( ESRN_OPTION_KEY, $submitted );
		}
		esrn_flash( 'success', __( 'Rules saved successfully.', 'easy-search-replace' ) );
	}

	$options = get_option( ESRN_OPTION_KEY );
	$fields  = ( isset( $options['fields'] ) && is_array( $options['fields'] ) ) ? $options['fields'] : array();

	if ( empty( $fields ) ) {
		$fields = array( ESRN_Rule_Schema::defaults() );
	}

	$public_post_types = get_post_types( array( 'public' => true ), 'objects' );
	$roles             = wp_roles()->roles;
	?>

	<form method="post" action="" class="esr-form">
		<?php wp_nonce_field( 'esrn_search_replace_options' ); ?>

		<div class="esr-layout">
			<div class="esr-main">
				<div class="esr-rules">
					<?php foreach ( $fields as $index => $field ) : ?>
						<?php esrn_render_rule_card( (int) $index, ESRN_Rule_Schema::normalize( $field ), $public_post_types, $roles ); ?>
					<?php endforeach; ?>
				</div>

				<div class="esr-rules-actions">
					<button type="button" id="esr-add-field" class="esr-btn esr-btn-secondary">
						<span class="dashicons dashicons-plus-alt2" aria-hidden="true"></span>
						<?php esc_html_e( 'Add rule', 'easy-search-replace' ); ?>
					</button>
					<button type="submit" name="esrn_search_replace_submit" class="esr-btn esr-btn-primary">
						<span class="dashicons dashicons-yes" aria-hidden="true"></span>
						<?php esc_html_e( 'Save changes', 'easy-search-replace' ); ?>
					</button>
				</div>
			</div>

			<aside class="esr-side">
				<div class="esr-side-card">
					<h3>
						<span class="dashicons dashicons-lightbulb" aria-hidden="true"></span>
						<?php esc_html_e( 'How it works', 'easy-search-replace' ); ?>
					</h3>
					<ol class="esr-side-steps">
						<li>
							<span class="esr-step-n">1</span>
							<div>
								<strong><?php esc_html_e( 'Type what to find.', 'easy-search-replace' ); ?></strong>
								<p><?php esc_html_e( 'Exact text, phrase, or HTML goes in the "Find" box.', 'easy-search-replace' ); ?></p>
								<code>Powered by WordPress</code>
							</div>
						</li>
						<li>
							<span class="esr-step-n">2</span>
							<div>
								<strong><?php esc_html_e( 'Type what it becomes.', 'easy-search-replace' ); ?></strong>
								<p><?php esc_html_e( 'New text in "Replace with". Leave empty to remove.', 'easy-search-replace' ); ?></p>
								<code>Built by My Agency</code>
							</div>
						</li>
						<li>
							<span class="esr-step-n">3</span>
							<div>
								<strong><?php esc_html_e( 'Narrow it down.', 'easy-search-replace' ); ?></strong>
								<p><?php esc_html_e( 'Expand "Where to apply" to target specific post types, IDs, or URLs.', 'easy-search-replace' ); ?></p>
							</div>
						</li>
						<li>
							<span class="esr-step-n">4</span>
							<div>
								<strong><?php esc_html_e( 'Ignore case.', 'easy-search-replace' ); ?></strong>
								<p><?php esc_html_e( 'Toggle case-insensitive matching if capitalization varies.', 'easy-search-replace' ); ?></p>
							</div>
						</li>
					</ol>
					<p class="esr-side-tip">
						<strong><?php esc_html_e( 'Tip:', 'easy-search-replace' ); ?></strong>
						<?php esc_html_e( 'Replacements happen at render time, so disabling a rule restores content instantly.', 'easy-search-replace' ); ?>
					</p>
					<a href="https://fluxpress.io/docs/easy-search-replace/" target="_blank" rel="noopener noreferrer" class="esr-btn esr-btn-docs">
						<span class="dashicons dashicons-book" aria-hidden="true"></span>
						<?php esc_html_e( 'View Documentation', 'easy-search-replace' ); ?>
					</a>
				</div>
			</aside>
		</div>
	</form>

	<?php
}
