<?php
/**
 * AjaxController — AJAX endpoints for Link Assassin.
 *
 * Endpoints (all require manage_options + nonce):
 *   - platform_helm_la_init:    Build candidate list, store in transient, return job_id + total
 *   - platform_helm_la_batch:   Process next N posts in the job
 *   - platform_helm_la_scan:    Dry-run preview, returns matching posts
 *   - platform_helm_la_restore: Restore a single backup
 *   - platform_helm_la_delete_backup: Drop a backup without restoring
 *
 * @package Platform\Helm\Modules\LinkAssassin
 */

declare( strict_types = 1 );

namespace Platform\Helm\Modules\LinkAssassin;

use Platform\Helm\Support\ElementorDataProcessor;
use Platform\Helm\Support\HtmlLinkStripper;

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

final class AjaxController {

	public const NONCE_ACTION = 'platform_helm_la';

	public function __construct(
		private PostScanner $scanner,
		private BatchProcessor $batch,
		private BackupManager $backups
	) {}

	public function register_hooks(): void {
		add_action( 'wp_ajax_platform_helm_la_init',          [ $this, 'ajax_init' ] );
		add_action( 'wp_ajax_platform_helm_la_batch',         [ $this, 'ajax_batch' ] );
		add_action( 'wp_ajax_platform_helm_la_scan',          [ $this, 'ajax_scan' ] );
		add_action( 'wp_ajax_platform_helm_la_restore',       [ $this, 'ajax_restore' ] );
		add_action( 'wp_ajax_platform_helm_la_delete_backup', [ $this, 'ajax_delete_backup' ] );
	}

	/* ----------------------------------------------------------------- */

	public function ajax_init(): void {
		$this->verify();

		$urls_input = isset( $_POST['urls'] ) ? sanitize_textarea_field( wp_unslash( (string) $_POST['urls'] ) ) : '';
		$urls       = $this->scanner->parse_urls_input( $urls_input );
		if ( empty( $urls ) ) {
			wp_send_json_error( [ 'message' => __( 'Please provide at least one valid URL.', 'platform-helm' ) ], 400 );
		}

		$post_ids = $this->scanner->find_candidate_post_ids( $urls );

		$job_id  = $this->generate_job_id();
		$payload = [
			'urls'          => $urls,
			'post_ids'      => $post_ids,
			'cursor'        => 0,
			'created_at'    => time(),
			'totals'        => [
				'posts_modified' => 0,
				'links_removed'  => 0,
				'posts_errored'  => 0,
			],
			'elementor_dirty' => false,
		];
		set_transient( LinkAssassinModule::JOB_TRANSIENT . $job_id, $payload, LinkAssassinModule::JOB_TTL_SECONDS );

		wp_send_json_success( [
			'job_id'      => $job_id,
			'total_posts' => count( $post_ids ),
			'batch_size'  => LinkAssassinModule::BATCH_SIZE,
			'urls'        => $urls,
		] );
	}

	public function ajax_batch(): void {
		$this->verify();

		$job_id = isset( $_POST['job_id'] ) ? sanitize_key( wp_unslash( (string) $_POST['job_id'] ) ) : '';
		if ( '' === $job_id ) {
			wp_send_json_error( [ 'message' => __( 'Missing job id.', 'platform-helm' ) ], 400 );
		}

		$key  = LinkAssassinModule::JOB_TRANSIENT . $job_id;
		$data = get_transient( $key );
		if ( ! is_array( $data ) || ! isset( $data['post_ids'], $data['urls'], $data['cursor'] ) ) {
			wp_send_json_error( [ 'message' => __( 'Job expired or not found. Please restart.', 'platform-helm' ) ], 410 );
		}

		$cursor    = (int) $data['cursor'];
		$post_ids  = (array) $data['post_ids'];
		$urls      = (array) $data['urls'];
		$slice     = array_slice( $post_ids, $cursor, LinkAssassinModule::BATCH_SIZE );
		$total     = count( $post_ids );

		if ( function_exists( 'set_time_limit' ) ) {
			@set_time_limit( 120 );
		}

		$result = $this->batch->process_batch( $slice, $urls );

		$new_cursor = $cursor + count( $slice );
		$data['cursor']                  = $new_cursor;
		$data['totals']['posts_modified'] += (int) $result['posts_modified'];
		$data['totals']['links_removed']  += (int) $result['links_removed'];
		$data['totals']['posts_errored']  += count( $result['errors'] );
		$data['elementor_dirty']          = $data['elementor_dirty'] || $result['elementor_dirty'];

		$done = ( $new_cursor >= $total );

		if ( $done ) {
			if ( ! empty( $data['elementor_dirty'] ) ) {
				$this->backups->maybe_clear_elementor_cache();
			}
			delete_transient( $key );
		} else {
			set_transient( $key, $data, LinkAssassinModule::JOB_TTL_SECONDS );
		}

		wp_send_json_success( [
			'job_id'         => $job_id,
			'cursor'         => $new_cursor,
			'total'          => $total,
			'done'           => $done,
			'batch_modified' => (int) $result['posts_modified'],
			'batch_removed'  => (int) $result['links_removed'],
			'batch_errors'   => $result['errors'],
			'totals'         => $data['totals'],
		] );
	}

	public function ajax_scan(): void {
		$this->verify();

		$urls_input = isset( $_POST['urls'] ) ? sanitize_textarea_field( wp_unslash( (string) $_POST['urls'] ) ) : '';
		$urls       = $this->scanner->parse_urls_input( $urls_input );
		if ( empty( $urls ) ) {
			wp_send_json_error( [ 'message' => __( 'Please provide at least one valid URL.', 'platform-helm' ) ], 400 );
		}

		$post_ids = $this->scanner->find_candidate_post_ids( $urls );
		$rows     = [];
		$stripper = new HtmlLinkStripper();
		$elementor = new ElementorDataProcessor();

		if ( function_exists( 'set_time_limit' ) ) {
			@set_time_limit( 120 );
		}

		wp_suspend_cache_addition( true );
		foreach ( $post_ids as $post_id ) {
			$post = get_post( (int) $post_id );
			if ( ! $post ) {
				continue;
			}
			[ , $cc, $u1 ] = $stripper->strip( (string) $post->post_content, $urls, true );
			$em = get_post_meta( $post->ID, '_elementor_data', true );
			$ec = 0;
			$u2 = [];
			if ( is_string( $em ) && '' !== $em ) {
				[ , $ec, $u2 ] = $elementor->process(
					$em,
					fn( string $html ): array => $stripper->strip( $html, $urls, true ),
					true
				);
			}
			$total = $cc + $ec;
			if ( $total > 0 ) {
				$rows[] = [
					'id'              => (int) $post->ID,
					'title'           => (string) $post->post_title,
					'type'            => (string) $post->post_type,
					'content_count'   => $cc,
					'elementor_count' => $ec,
					'urls'            => array_values( array_unique( array_merge( $u1, $u2 ) ) ),
				];
			}
		}
		wp_suspend_cache_addition( false );

		wp_send_json_success( [
			'rows'              => $rows,
			'total_posts'       => count( $rows ),
			'total_occurrences' => array_sum( array_map( fn( $r ) => $r['content_count'] + $r['elementor_count'], $rows ) ),
			'urls'              => $urls,
		] );
	}

	public function ajax_restore(): void {
		$this->verify();
		$post_id  = isset( $_POST['post_id'] )  ? absint( $_POST['post_id'] )  : 0;
		$meta_key = isset( $_POST['meta_key'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['meta_key'] ) ) : '';
		if ( $post_id <= 0 || '' === $meta_key ) {
			wp_send_json_error( [ 'message' => __( 'Invalid restore parameters.', 'platform-helm' ) ], 400 );
		}
		$ok = $this->backups->restore( $post_id, $meta_key );
		if ( ! $ok ) {
			wp_send_json_error( [ 'message' => __( 'Restore failed (backup missing or corrupted).', 'platform-helm' ) ], 500 );
		}
		wp_send_json_success( [ 'post_id' => $post_id, 'meta_key' => $meta_key ] );
	}

	public function ajax_delete_backup(): void {
		$this->verify();
		$post_id  = isset( $_POST['post_id'] )  ? absint( $_POST['post_id'] )  : 0;
		$meta_key = isset( $_POST['meta_key'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['meta_key'] ) ) : '';
		if ( $post_id <= 0 || '' === $meta_key ) {
			wp_send_json_error( [ 'message' => __( 'Invalid parameters.', 'platform-helm' ) ], 400 );
		}
		$this->backups->delete( $post_id, $meta_key );
		wp_send_json_success( [ 'post_id' => $post_id, 'meta_key' => $meta_key ] );
	}

	/* ----------------------------------------------------------------- */

	private function verify(): void {
		check_ajax_referer( self::NONCE_ACTION, 'nonce' );
		\Platform\Helm\Core\Authorization::require_master_admin_ajax();
	}

	private function generate_job_id(): string {
		return wp_generate_password( 16, false, false );
	}
}
