<?php
/**
 * Bulk Suggestion page.
 *
 * Scans many published posts at once and presents every internal-link
 * opportunity in a single reviewable table — the power-user counterpart to the
 * in-editor panel. Rendered in the Helm design system.
 *
 * Architecture:
 *   - The page header has a "Scan" button; an empty-state card invites the
 *     first scan.
 *   - Scanning is chunked over AJAX (default 10 posts per request) with a
 *     progress bar, so it never blocks or times out on large sites.
 *   - Each chunk reuses the SAME SuggestionEngine as the editor panel.
 *   - Results stream into a card table grouped by source post. Each row links
 *     to edit the source and offers "Dismiss" (records to DismissedSuggestions).
 *
 * Read + dismiss only — this page does not auto-insert links (that stays a
 * deliberate, per-link action in the editor). It's a discovery/triage tool.
 *
 * SECURITY: gated by `edit_others_posts`; AJAX nonce-checked.
 *
 * @package Platform\Helm\Modules\Link_Suggester
 */

declare( strict_types = 1 );

namespace Platform\Helm\Modules\Link_Suggester;

use Platform\Helm\Core\Ui;

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

final class BulkSuggestPage {

	public const PAGE_SLUG   = 'platform-helm-bulk-suggest';
	public const PARENT_SLUG = 'platform-helm';

	public const SCAN_ACTION    = 'platform_helm_bulk_suggest_scan';
	public const DISMISS_ACTION = 'platform_helm_bulk_dismiss';
	public const NONCE          = 'platform_helm_bulk_suggest_nonce';

	private const CAPABILITY = 'edit_others_posts';
	private const CHUNK      = 10;

	private SuggestionEngine $engine;
	private DismissedSuggestions $dismissed;

	public function __construct() {
		$this->engine    = new SuggestionEngine();
		$this->dismissed = new DismissedSuggestions();
	}

	public function register_hooks(): void {
		add_action( 'admin_menu', [ $this, 'register_menu' ], 41 );
		add_action( 'wp_ajax_' . self::SCAN_ACTION, [ $this, 'ajax_scan' ] );
		add_action( 'wp_ajax_' . self::DISMISS_ACTION, [ $this, 'ajax_dismiss' ] );
	}

	public function register_menu(): void {
		if ( ! current_user_can( self::CAPABILITY ) ) {
			return;
		}
		add_submenu_page(
			self::PARENT_SLUG,
			__( 'Bulk Link Suggestions', 'platform-helm' ),
			__( 'Bulk Suggestions', 'platform-helm' ),
			self::CAPABILITY,
			self::PAGE_SLUG,
			[ $this, 'render' ]
		);
	}

	/**
	 * IDs of published posts of the configured target types, to scan.
	 *
	 * @return int[]
	 */
	private function scannable_ids(): array {
		$settings = SuggesterSettings::get();
		$types    = $settings['target_post_types'];
		if ( empty( $types ) ) {
			return [];
		}
		$q = new \WP_Query( [
			'post_type'      => $types,
			'post_status'    => 'publish',
			'posts_per_page' => 1000,
			'fields'         => 'ids',
			'orderby'        => 'date',
			'order'          => 'DESC',
			'no_found_rows'  => true,
		] );
		return array_map( 'intval', $q->posts );
	}

	public function render(): void {
		if ( ! current_user_can( self::CAPABILITY ) ) {
			wp_die( esc_html__( 'You do not have permission to access this page.', 'platform-helm' ) );
		}
		$ids   = $this->scannable_ids();
		$nonce = wp_create_nonce( self::NONCE );

		$start_action = '<button type="button" id="phelm-bulk-start" class="btn btn-primary"><i class="ti ti-player-play" aria-hidden="true"></i> '
			. esc_html( sprintf( /* translators: %d: number of posts */ __( 'Scan %d posts', 'platform-helm' ), count( $ids ) ) )
			. '</button>';

		Ui::open_page(
			__( 'Bulk Suggestions', 'platform-helm' ),
			__( 'Scan all your published posts and review every internal-link opportunity in one place — the same engine as the editor panel. Dismissing a pairing hides it everywhere.', 'platform-helm' ),
			$start_action
		);
		?>
		<div id="phelm-bulk-progress" class="progress-card" style="display:none;margin-bottom:16px">
			<p class="progress-title"><i class="ti ti-loader" aria-hidden="true"></i> <?php esc_html_e( 'Scanning posts…', 'platform-helm' ); ?></p>
			<div class="progress-track"><div class="progress-fill" id="phelm-bulk-fill" style="width:0"></div></div>
			<div class="progress-meta"><span id="phelm-bulk-progress-text"></span></div>
		</div>

		<div id="phelm-bulk-results" class="card" style="display:none">
			<div class="card-header">
				<div class="card-title"><i class="ti ti-sparkles" style="color:var(--accent-hover)" aria-hidden="true"></i> <?php esc_html_e( 'Link Opportunities', 'platform-helm' ); ?> <span class="badge badge-info" id="phelm-bulk-count">0</span></div>
				<span class="text-faint"><?php esc_html_e( 'Review and dismiss — apply links in the editor', 'platform-helm' ); ?></span>
			</div>
			<div class="table-wrap">
				<table>
					<thead><tr>
						<th><?php esc_html_e( 'Source Post', 'platform-helm' ); ?></th>
						<th><?php esc_html_e( 'Anchor Phrase', 'platform-helm' ); ?></th>
						<th><?php esc_html_e( 'Links To', 'platform-helm' ); ?></th>
						<th class="right"><?php esc_html_e( 'Actions', 'platform-helm' ); ?></th>
					</tr></thead>
					<tbody id="phelm-bulk-tbody"></tbody>
				</table>
			</div>
		</div>

		<div id="phelm-bulk-empty" class="card mt-16" style="border-style:dashed;background:transparent">
			<div class="empty-state">
				<div class="empty-icon" style="background:var(--accent-tint);color:var(--accent-hover)"><i class="ti ti-stack-2" aria-hidden="true"></i></div>
				<p class="empty-title" id="phelm-bulk-empty-title"><?php esc_html_e( 'No scan run yet', 'platform-helm' ); ?></p>
				<p class="empty-desc" id="phelm-bulk-empty-desc"><?php esc_html_e( 'Run a scan to discover internal-link opportunities across all your posts and pages. Usually takes under two minutes.', 'platform-helm' ); ?></p>
			</div>
		</div>
		<?php
		Ui::close_page();
		?>
		<script>
		(function(){
			var ids     = <?php echo wp_json_encode( array_values( $ids ) ); ?>;
			var ajaxUrl = <?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>;
			var nonce   = <?php echo wp_json_encode( $nonce ); ?>;
			var CHUNK   = <?php echo (int) self::CHUNK; ?>;

			var startBtn   = document.getElementById('phelm-bulk-start');
			var progress   = document.getElementById('phelm-bulk-progress');
			var fill       = document.getElementById('phelm-bulk-fill');
			var ptext      = document.getElementById('phelm-bulk-progress-text');
			var results    = document.getElementById('phelm-bulk-results');
			var tbody      = document.getElementById('phelm-bulk-tbody');
			var countBadge = document.getElementById('phelm-bulk-count');
			var emptyCard  = document.getElementById('phelm-bulk-empty');
			var emptyTitle = document.getElementById('phelm-bulk-empty-title');
			var emptyDesc  = document.getElementById('phelm-bulk-empty-desc');

			var L = {
				done:      <?php echo wp_json_encode( __( 'Scan complete.', 'platform-helm' ) ); ?>,
				edit:      <?php echo wp_json_encode( __( 'Edit post', 'platform-helm' ) ); ?>,
				dismiss:   <?php echo wp_json_encode( __( 'Dismiss', 'platform-helm' ) ); ?>,
				noneTitle: <?php echo wp_json_encode( __( 'No opportunities found', 'platform-helm' ) ); ?>,
				noneDesc:  <?php echo wp_json_encode( __( 'Your internal linking is thorough — no new suggestions across your posts.', 'platform-helm' ) ); ?>
			};

			function esc(s){ var d=document.createElement('div'); d.textContent = s==null?'':s; return d.innerHTML; }

			function addRows(items){
				items.forEach(function(it){
					var tr = document.createElement('tr');
					tr.innerHTML =
						'<td><p class="url-cell">'+esc(it.source_title)+'</p></td>'+
						'<td><span style="background:var(--accent-tint);color:var(--accent);padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600">'+esc(it.phrase)+'</span>'+
							(it.is_external?' <span class="badge badge-warning">ext</span>':'')+
							(it.snippet?'<p class="meta-line">'+esc(it.snippet)+'</p>':'')+'</td>'+
						'<td><p style="font-family:var(--font-mono);font-size:11px;color:var(--text-secondary);margin:0">'+esc(it.target_title)+'</p></td>'+
						'<td class="right"></td>';
					var actions = tr.lastChild;

					var edit = document.createElement('a');
					edit.href = it.source_edit; edit.className = 'btn btn-ghost btn-xs'; edit.textContent = L.edit;
					actions.appendChild(edit);

					if (it.target_id > 0){
						var dismiss = document.createElement('a');
						dismiss.href = '#'; dismiss.className = 'btn btn-ghost btn-xs'; dismiss.style.marginLeft = '6px'; dismiss.textContent = L.dismiss;
						dismiss.addEventListener('click', function(e){
							e.preventDefault();
							tr.style.opacity = '.4';
							var body = new URLSearchParams();
							body.append('action', <?php echo wp_json_encode( self::DISMISS_ACTION ); ?>);
							body.append('nonce', nonce);
							body.append('source_id', it.source_id);
							body.append('target_id', it.target_id);
							fetch(ajaxUrl,{method:'POST',credentials:'same-origin',body:body})
								.then(function(r){return r.json();})
								.then(function(res){ if(res&&res.success){ tr.parentNode.removeChild(tr); } else { tr.style.opacity=''; } })
								.catch(function(){ tr.style.opacity=''; });
						});
						actions.appendChild(dismiss);
					}
					tbody.appendChild(tr);
				});
			}

			if (startBtn) startBtn.addEventListener('click', function(){
				if (!ids.length){ emptyTitle.textContent = L.noneTitle; emptyDesc.textContent = L.noneDesc; return; }
				startBtn.disabled = true;
				emptyCard.style.display = 'none';
				progress.style.display = 'block';
				results.style.display = 'block';
				tbody.innerHTML = '';

				var index = 0, totalFound = 0, scanned = 0;

				function nextChunk(){
					var slice = ids.slice(index, index + CHUNK);
					if (!slice.length){
						ptext.textContent = L.done;
						fill.style.width = '100%';
						if (totalFound === 0){
							results.style.display = 'none';
							emptyCard.style.display = 'block';
							emptyTitle.textContent = L.noneTitle;
							emptyDesc.textContent = L.noneDesc;
						}
						startBtn.disabled = false;
						return;
					}
					var body = new URLSearchParams();
					body.append('action', <?php echo wp_json_encode( self::SCAN_ACTION ); ?>);
					body.append('nonce', nonce);
					body.append('ids', slice.join(','));

					fetch(ajaxUrl,{method:'POST',credentials:'same-origin',body:body})
						.then(function(r){return r.json();})
						.then(function(res){
							if (res && res.success && res.data){
								addRows(res.data.items || []);
								totalFound += (res.data.items || []).length;
								countBadge.textContent = totalFound;
							}
							index += CHUNK; scanned = Math.min(index, ids.length);
							fill.style.width = Math.round((scanned/ids.length)*100) + '%';
							ptext.textContent = scanned + ' / ' + ids.length;
							nextChunk();
						})
						.catch(function(){
							index += CHUNK; scanned = Math.min(index, ids.length);
							nextChunk();
						});
				}
				nextChunk();
			});
		})();
		</script>
		<?php
	}

	/**
	 * Scan a chunk of post IDs and return their suggestions, flattened with
	 * source info attached.
	 */
	public function ajax_scan(): void {
		check_ajax_referer( self::NONCE, 'nonce' );
		if ( ! current_user_can( self::CAPABILITY ) ) {
			wp_send_json_error( [ 'message' => __( 'Permission denied.', 'platform-helm' ) ], 403 );
		}

		$ids_raw = isset( $_POST['ids'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['ids'] ) ) : '';
		$ids     = array_filter( array_map( 'intval', explode( ',', $ids_raw ) ) );

		$items = [];
		foreach ( $ids as $post_id ) {
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
				continue;
			}
			$content     = (string) get_post_field( 'post_content', $post_id );
			$suggestions = $this->engine->suggest( $post_id, $content );
			if ( empty( $suggestions ) ) {
				continue;
			}
			$source_title = (string) ( get_the_title( $post_id ) ?: __( '(no title)', 'platform-helm' ) );
			$source_edit  = (string) get_edit_post_link( $post_id, 'raw' );
			foreach ( $suggestions as $s ) {
				$items[] = [
					'source_id'    => $post_id,
					'source_title' => $source_title,
					'source_edit'  => $source_edit,
					'phrase'       => (string) $s['phrase'],
					'target_id'    => (int) $s['target_id'],
					'target_title' => (string) $s['target_title'],
					'snippet'      => (string) ( $s['snippet'] ?? '' ),
					'is_external'  => ! empty( $s['is_external'] ),
				];
			}
		}

		wp_send_json_success( [ 'items' => $items ] );
	}

	public function ajax_dismiss(): void {
		check_ajax_referer( self::NONCE, 'nonce' );
		$source_id = isset( $_POST['source_id'] ) ? (int) $_POST['source_id'] : 0;
		$target_id = isset( $_POST['target_id'] ) ? (int) $_POST['target_id'] : 0;
		if ( $source_id <= 0 || ! current_user_can( 'edit_post', $source_id ) ) {
			wp_send_json_error( [ 'message' => __( 'Permission denied.', 'platform-helm' ) ], 403 );
		}
		$this->dismissed->dismiss( $source_id, $target_id );
		wp_send_json_success( [ 'dismissed' => true ] );
	}
}
