<?php
/**
 * Robots.txt Manager module.
 *
 * Lets the site owner append custom rules to WordPress's virtual robots.txt
 * via the `robots_txt` filter. Composes cleanly with Yoast and other SEO
 * plugins — each filter callback runs in order and our additions are
 * appended at the end of the document.
 *
 * Caveat: WordPress's `robots_txt` filter is bypassed if a physical
 * `robots.txt` file exists at ABSPATH. The settings tab detects that case
 * and warns the user — we can't write to ABSPATH from PHP reliably (and
 * shouldn't, on most hosts).
 *
 * Intentionally simple: just a textarea + a live preview. No AI bot
 * allow/block list — the sites running BW Dev want to BE indexed by AI
 * crawlers, not block them.
 *
 * @package BW_Dev
 */

defined( 'ABSPATH' ) || exit;

class BW_Dev_Module_Robots_Txt implements BW_Dev_Module_Interface {

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

	public function label(): string {
		return __( 'Robots.txt Manager', 'bw-dev' );
	}

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

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

	public function sanitize( array $data ): array {
		$raw = isset( $data['custom_rules'] ) ? (string) $data['custom_rules'] : '';
		// Allow line breaks; sanitize_textarea_field strips tags and normalizes
		// line endings while preserving the multi-line structure of robots.txt.
		$clean = sanitize_textarea_field( wp_unslash( $raw ) );
		return array( 'custom_rules' => $clean );
	}

	public function register(): void {
		add_filter( 'robots_txt', array( $this, 'append_custom_rules' ), 20, 2 );
	}

	/**
	 * Append the saved custom rules to whatever robots.txt content has been
	 * built up by core + other plugins. Late priority (20) so we land below
	 * Yoast's Sitemap line and any other contributors.
	 *
	 * @param string $output Current robots.txt body.
	 * @param string $public Value of the `blog_public` option (string '0' or '1').
	 */
	public function append_custom_rules( $output, $public ) {
		unset( $public );
		$rules = (string) bw_dev()->settings()->get( $this->slug(), 'custom_rules', '' );
		$rules = trim( $rules );
		if ( '' === $rules ) {
			return $output;
		}
		$output = rtrim( (string) $output, "\n" );
		return $output . "\n\n# Added by BW Dev → Robots.txt Manager\n" . $rules . "\n";
	}

	/* ---------------------------------------------------------------------
	 * Settings tab
	 * ------------------------------------------------------------------- */

	/**
	 * Build the live robots.txt preview by running the same filter chain WP
	 * uses when serving /robots.txt — so the preview reflects core defaults,
	 * Yoast/etc additions, and our own appended rules.
	 */
	private function preview(): string {
		$public = (string) get_option( 'blog_public' );
		if ( '0' === $public ) {
			$base = "User-agent: *\nDisallow: /\n";
		} else {
			$base = "User-agent: *\nDisallow: /wp-admin/\nAllow: /wp-admin/admin-ajax.php\n";
		}
		return (string) apply_filters( 'robots_txt', $base, $public );
	}

	private function physical_file_path(): string {
		return rtrim( ABSPATH, '/\\' ) . '/robots.txt';
	}

	public function render_tab(): void {
		$current      = (string) bw_dev()->settings()->get( $this->slug(), 'custom_rules', '' );
		$field_name   = BW_Dev_Settings::OPTION . '[' . $this->slug() . '][custom_rules]';
		$preview      = $this->preview();
		$physical     = $this->physical_file_path();
		$has_physical = file_exists( $physical );
		$robots_url   = home_url( '/robots.txt' );
		?>
		<p class="description">
			<?php esc_html_e( 'Append custom rules to WordPress\'s virtual robots.txt. Composes cleanly with Yoast and other SEO plugins — each filter contributes; our rules are appended at the end.', 'bw-dev' ); ?>
		</p>

		<?php if ( $has_physical ) : ?>
			<div style="background:#fff;border-left:4px solid #d63638;padding:12px 16px;margin:14px 0;max-width:720px;">
				<strong><?php esc_html_e( 'Physical robots.txt detected', 'bw-dev' ); ?></strong>
				<p style="margin:6px 0 0;">
					<?php
					printf(
						/* translators: %s: file path */
						esc_html__( 'A physical file exists at %s. WordPress\'s robots_txt filter is BYPASSED when a physical file is present, so the rules below will not take effect. Either delete the physical file (so WP serves the virtual robots.txt again), or paste the rules below directly into that file.', 'bw-dev' ),
						'<code>' . esc_html( $physical ) . '</code>'
					);
					?>
				</p>
			</div>
		<?php endif; ?>

		<table class="form-table" role="presentation">
			<tbody>
				<tr>
					<th scope="row">
						<label for="bw-dev-robots-txt-rules"><?php esc_html_e( 'Custom rules', 'bw-dev' ); ?></label>
					</th>
					<td>
						<textarea id="bw-dev-robots-txt-rules" name="<?php echo esc_attr( $field_name ); ?>" rows="10" class="large-text code" placeholder="User-agent: SomeBot&#10;Disallow: /private/"><?php echo esc_textarea( $current ); ?></textarea>
						<p class="description">
							<?php esc_html_e( 'Standard robots.txt syntax — one directive per line. Examples: "User-agent: BotName", "Disallow: /path/", "Allow: /path/", "Sitemap: https://...". Yoast already injects the sitemap line, so you usually don\'t need to add one.', 'bw-dev' ); ?>
						</p>
					</td>
				</tr>
			</tbody>
		</table>

		<h3><?php esc_html_e( 'Live preview', 'bw-dev' ); ?></h3>
		<p class="description">
			<?php
			printf(
				/* translators: %s: link to live robots.txt */
				esc_html__( 'This is what %s currently serves (after WP defaults + every plugin\'s contribution + your custom rules below):', 'bw-dev' ),
				'<a href="' . esc_url( $robots_url ) . '" target="_blank" rel="noopener"><code>' . esc_html( $robots_url ) . '</code></a>'
			);
			?>
		</p>
		<pre style="background:#f6f7f7;padding:12px 16px;border-left:4px solid #2271b1;max-width:720px;overflow:auto;font-size:12px;line-height:1.5;"><?php echo esc_html( $preview ); ?></pre>
		<?php
	}

	public function uninstall(): void {
		// Settings live under bw_dev_settings — root option drop covers them.
	}
}
