<?php
/**
 * GSC Index Coverage Page.
 *
 * Tools → Index Coverage: runs URL Inspection across published posts in
 * quota-safe batches and lists the URLs Google is NOT cleanly indexing, with
 * the reason (e.g. "Crawled - currently not indexed"). This diagnoses the
 * page-level reach loss the SEO audit suspected. Read-only — requesting
 * indexing is still a manual click in the GSC UI.
 *
 * @package Platform\Helm\Modules\GSC
 */

declare( strict_types = 1 );

namespace Platform\Helm\Modules\GSC;

use Platform\Helm\Core\Assets;
use Platform\Helm\Core\Authorization;

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

final class GscIndexPage {

	public const PAGE_SLUG   = 'platform-helm-index-coverage';
	public const PARENT_SLUG = 'platform-helm';
	public const NONCE       = 'platform_helm_gsc_index';
	public const AJAX_BATCH  = 'platform_helm_gsc_index_batch';

	public function __construct(
		private Assets $assets,
		private GscIndexStore $store,
		private GscIndexChecker $checker
	) {}

	public function register_hooks(): void {
		add_action( 'admin_menu', [ $this, 'register_menu' ], 49 );
		add_action( 'wp_ajax_' . self::AJAX_BATCH, [ $this, 'ajax_batch' ] );
		add_action( 'admin_post_platform_helm_gsc_index_export', [ $this, 'export_csv' ] );
	}

	public function register_menu(): void {
		if ( ! Authorization::is_master_admin() ) {
			return;
		}
		$hook = add_submenu_page(
			self::PARENT_SLUG,
			__( 'Index Coverage', 'platform-helm' ),
			__( 'Index Coverage', 'platform-helm' ),
			'manage_options',
			self::PAGE_SLUG,
			[ $this, 'render' ]
		);
		if ( $hook ) {
			add_action( 'load-' . $hook, [ $this, 'enqueue' ] );
		}
	}

	public function enqueue(): void {
		$this->assets->enqueue_gsc_index( [
			'ajaxUrl' => admin_url( 'admin-ajax.php' ),
			'nonce'   => wp_create_nonce( self::NONCE ),
			'action'  => self::AJAX_BATCH,
		] );
	}

	public function ajax_batch(): void {
		check_ajax_referer( self::NONCE, 'nonce' );
		Authorization::require_master_admin_ajax();
		$result = $this->checker->check_batch();
		if ( ! $result['ok'] ) {
			wp_send_json_error( [ 'message' => $result['message'], 'remaining' => $result['remaining'] ], 400 );
		}
		wp_send_json_success( $result );
	}

	public function export_csv(): void {
		if ( ! Authorization::is_master_admin() ) {
			wp_die( esc_html__( 'Not allowed.', 'platform-helm' ), '', [ 'response' => 403 ] );
		}
		check_admin_referer( self::NONCE . '_export' );
		$filter = isset( $_GET['ifilter'] ) ? sanitize_key( wp_unslash( $_GET['ifilter'] ) ) : 'issues';
		$filter = in_array( $filter, [ 'issues', 'pass', 'all' ], true ) ? $filter : 'issues';
		$rows   = $this->store->rows( $filter, 100000 );

		nocache_headers();
		header( 'Content-Type: text/csv; charset=utf-8' );
		header( 'Content-Disposition: attachment; filename=index-coverage-' . $filter . '-' . gmdate( 'Ymd' ) . '.csv' );
		$out = fopen( 'php://output', 'w' );
		fputcsv( $out, [ 'Post ID', 'Title', 'URL', 'Verdict', 'Coverage state', 'Indexing state', 'Robots', 'Google canonical', 'Last crawl', 'Checked at' ] );
		foreach ( $rows as $r ) {
			fputcsv( $out, [
				(int) $r['post_id'],
				get_the_title( (int) $r['post_id'] ),
				(string) $r['url'],
				(string) $r['verdict'],
				(string) $r['coverage'],
				(string) $r['indexing_state'],
				(string) $r['robots_state'],
				(string) $r['google_canonical'],
				(string) ( $r['last_crawl'] ?? '' ),
				(string) ( $r['checked_at'] ?? '' ),
			] );
		}
		fclose( $out );
		exit;
	}

	private function verdict_label( string $verdict, string $coverage ): string {
		if ( 'PASS' === $verdict ) {
			return __( 'Indexed', 'platform-helm' );
		}
		return '' !== $coverage ? $coverage : ( '' !== $verdict ? $verdict : __( 'Unknown', 'platform-helm' ) );
	}

	public function render(): void {
		Authorization::require_master_admin();

		$connected = GscCredentials::is_connected();
		$checked   = $this->store->count_checked();
		$passing   = $this->store->count_passing();
		$issues    = max( 0, $checked - $passing );
		$total     = (int) wp_count_posts( 'post' )->publish;
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$filter    = isset( $_GET['ifilter'] ) ? sanitize_key( wp_unslash( $_GET['ifilter'] ) ) : 'issues';
		$filter    = in_array( $filter, [ 'issues', 'pass', 'all' ], true ) ? $filter : 'issues';
		$rows      = $this->store->rows( $filter, 1000 );
		$base      = admin_url( 'admin.php?page=' . self::PAGE_SLUG );
		$export    = wp_nonce_url( admin_url( 'admin-post.php?action=platform_helm_gsc_index_export&ifilter=' . $filter ), self::NONCE . '_export' );
		?>
		<div class="wrap ph-gsc">
			<h1><?php esc_html_e( 'Index Coverage', 'platform-helm' ); ?></h1>
			<p class="ph-gsc-lead">
				<?php esc_html_e( 'Asks Google, post by post, whether each URL is actually indexed — and if not, why. This is the diagnosis for the reach loss in the audit: a page Google has dropped earns zero clicks no matter how good it is.', 'platform-helm' ); ?>
			</p>

			<?php if ( ! $connected ) : ?>
				<div class="notice notice-warning inline"><p>
					<?php
					printf(
						/* translators: %s: settings URL */
						wp_kses_post( __( 'Connect to Search Console first on the <a href="%s">Search Console</a> screen.', 'platform-helm' ) ),
						esc_url( admin_url( 'admin.php?page=platform-helm-gsc' ) )
					);
					?>
				</p></div>
			<?php else : ?>
				<div class="ph-gsc-card">
					<div class="ph-gsc-row" style="gap:24px; flex-wrap:wrap;">
						<div><strong style="font-size:22px;"><?php echo esc_html( number_format_i18n( $checked ) ); ?></strong><br /><?php esc_html_e( 'posts checked', 'platform-helm' ); ?></div>
						<div style="color:#1a7f37;"><strong style="font-size:22px;"><?php echo esc_html( number_format_i18n( $passing ) ); ?></strong><br /><?php esc_html_e( 'indexed (PASS)', 'platform-helm' ); ?></div>
						<div style="color:#b32d2e;"><strong style="font-size:22px;"><?php echo esc_html( number_format_i18n( max( 0, $checked - $passing ) ) ); ?></strong><br /><?php esc_html_e( 'with issues', 'platform-helm' ); ?></div>
						<div style="color:#646970;"><strong style="font-size:22px;"><?php echo esc_html( number_format_i18n( max( 0, $total - $checked ) ) ); ?></strong><br /><?php esc_html_e( 'not yet checked', 'platform-helm' ); ?></div>
					</div>
					<div class="ph-gsc-actions" style="margin-top:14px;">
						<button type="button" class="button button-primary" id="ph-gsc-index-run"><?php esc_html_e( 'Check / continue', 'platform-helm' ); ?></button>
						<button type="button" class="button" id="ph-gsc-index-stop" style="display:none;"><?php esc_html_e( 'Stop', 'platform-helm' ); ?></button>
						<span class="ph-gsc-msg" id="ph-gsc-index-msg" aria-live="polite"></span>
					</div>
					<p class="ph-gsc-hint"><?php esc_html_e( 'Runs in small batches and pauses automatically near Google’s daily limit (~2,000 URLs). On a large site, leave it running or come back across a couple of days — progress is saved.', 'platform-helm' ); ?></p>
				</div>

				<div class="ph-gsc-card">
					<div class="ph-gsc-row" style="justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px;">
						<h2 style="margin:0;"><?php esc_html_e( 'Coverage report', 'platform-helm' ); ?></h2>
						<a class="button" href="<?php echo esc_url( $export ); ?>"><?php esc_html_e( 'Export CSV', 'platform-helm' ); ?></a>
					</div>

					<p class="ph-gsc-filters" style="margin:12px 0 4px;">
						<?php
						$tabs = [
							'issues' => sprintf( __( 'With issues (%s)', 'platform-helm' ), number_format_i18n( $issues ) ),
							'pass'   => sprintf( __( 'Indexed (%s)', 'platform-helm' ), number_format_i18n( $passing ) ),
							'all'    => sprintf( __( 'All checked (%s)', 'platform-helm' ), number_format_i18n( $checked ) ),
						];
						$links = [];
						foreach ( $tabs as $key => $label ) {
							$url     = esc_url( add_query_arg( 'ifilter', $key, $base ) );
							$style   = $filter === $key ? 'font-weight:700; color:#9C1C1F; text-decoration:underline;' : '';
							$links[] = '<a href="' . $url . '" style="' . $style . '">' . esc_html( $label ) . '</a>';
						}
						echo implode( ' &nbsp;·&nbsp; ', $links ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
						?>
					</p>

					<?php if ( empty( $rows ) ) : ?>
						<p><?php echo $checked > 0 ? esc_html__( 'Nothing in this view yet.', 'platform-helm' ) : esc_html__( 'Run a check to populate this list.', 'platform-helm' ); ?></p>
					<?php else : ?>
						<table class="widefat striped ph-gsc-table">
							<thead><tr>
								<th><?php esc_html_e( 'Post', 'platform-helm' ); ?></th>
								<th><?php esc_html_e( 'Status', 'platform-helm' ); ?></th>
								<th><?php esc_html_e( 'Last crawl', 'platform-helm' ); ?></th>
								<th></th>
							</tr></thead>
							<tbody>
								<?php foreach ( $rows as $p ) : ?>
									<tr>
										<td>
											<a href="<?php echo esc_url( (string) get_edit_post_link( (int) $p['post_id'] ) ); ?>"><?php echo esc_html( get_the_title( (int) $p['post_id'] ) ?: (string) $p['url'] ); ?></a>
											<div style="font-size:11px; color:#646970;"><?php echo esc_html( (string) $p['url'] ); ?></div>
										</td>
										<td><?php echo esc_html( $this->verdict_label( (string) $p['verdict'], (string) $p['coverage'] ) ); ?></td>
										<td><?php echo esc_html( ! empty( $p['last_crawl'] ) ? wp_date( 'j M Y', strtotime( (string) $p['last_crawl'] ) ?: time() ) : '—' ); ?></td>
										<td><a class="button button-small" target="_blank" rel="noopener" href="<?php echo esc_url( (string) get_permalink( (int) $p['post_id'] ) ); ?>"><?php esc_html_e( 'View', 'platform-helm' ); ?></a></td>
									</tr>
								<?php endforeach; ?>
							</tbody>
						</table>
						<p class="ph-gsc-hint"><?php esc_html_e( 'Showing up to 1,000 rows on screen — use Export CSV for the complete filtered list. Workflow: filter to “With issues”, export, then in Search Console → URL Inspection request indexing for the ones you actually want indexed. Pages you do not want indexed can stay as-is.', 'platform-helm' ); ?></p>
					<?php endif; ?>
				</div>
			<?php endif; ?>
		</div>
		<?php
	}
}
