<?php
/**
 * Scan AJAX handlers.
 *
 * @package CoffeeWP_Media_Cleanup
 */

namespace CWPM\Ajax;

use CWPM\Core\Db;
use CWPM\Core\Logger;
use CWPM\Media\Scanner;
use CWPM\Utils\TimeoutGuard;

defined( 'ABSPATH' ) || exit;

// phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce verified centrally in BaseHandler::verify_request().

class ScanHandler extends BaseHandler {

	/**
	 * Constructor -- register AJAX actions.
	 */
	public function __construct() {
		add_action( 'wp_ajax_cwpm_scan_init', array( $this, 'scan_init' ) );
		add_action( 'wp_ajax_cwpm_scan_batch', array( $this, 'scan_batch' ) );
		add_action( 'wp_ajax_cwpm_scan_broken', array( $this, 'scan_broken' ) );
		add_action( 'wp_ajax_cwpm_scan_abort', array( $this, 'scan_abort' ) );
		add_action( 'wp_ajax_cwpm_scan_complete', array( $this, 'scan_complete' ) );
		add_action( 'wp_ajax_cwpm_scan_results_page', array( $this, 'scan_results_page' ) );
		add_action( 'wp_ajax_cwpm_scan_results_ids', array( $this, 'scan_results_ids' ) );
	}

	/**
	 * Initialise a scan: collect folder structure and file counts.
	 */
	public function scan_init() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		$force_new = isset( $_POST['force_new'] ) && '1' === $_POST['force_new'];
		$meta      = Db::get_scan_meta();

		if ( $force_new ) {
			Scanner::flush_cache();
		}

		$data = Scanner::init_scan();

		if (
			! $force_new
			&& $meta
			&& in_array( $meta['status'], array( 'in_progress', 'interrupted' ), true )
		) {

			$data['resume']          = true;
			$data['scan_id']         = $meta['scan_id'];
			$data['last_file_index'] = $meta['last_file_index'];
			$data['scanned_files']   = $meta['scanned_files'];
		} else {

			$scan_id         = Db::start_scan( $data['total'] );
			$data['resume']  = false;
			$data['scan_id'] = $scan_id;
		}

		Logger::info( 'Scan started. Total files to scan: ' . $data['total'] );

		wp_send_json_success( $data );
	}

	/**
	 * Abort the current scan by marking it as interrupted.
	 */
	public function scan_abort() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		$meta = Db::get_scan_meta();
		if ( $meta ) {
			$meta['status'] = 'interrupted';
			Db::save_scan_meta( $meta );
		}

		Logger::info( 'Scan aborted by user.' );

		wp_send_json_success();
	}

	/**
	 * Mark the current scan as complete.
	 */
	public function scan_complete() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		Db::complete_scan();

		$meta    = Db::get_scan_meta();
		$scanned = $meta ? $meta['scanned_files'] : 0;
		Logger::info( 'Scan completed. Scanned ' . $scanned . ' files.' );

		wp_send_json_success();
	}

	/**
	 * Process a batch of files during a scan.
	 */
	public function scan_batch() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- per-item sanitized in foreach loop below.
		$files = isset( $_POST['files'] ) ? $_POST['files'] : array();

		if ( ! is_array( $files ) || empty( $files ) ) {
			wp_send_json_error( 'No files provided', 400 );
		}

		$clean_files = array();
		foreach ( $files as $item ) {
			if ( empty( $item['year'] ) || empty( $item['month'] ) || empty( $item['file'] ) ) {
				continue;
			}
			$clean_files[] = array(
				'year'  => sanitize_text_field( $item['year'] ),
				'month' => sanitize_text_field( $item['month'] ),
				'file'  => sanitize_file_name( $item['file'] ),
			);
		}

		if ( empty( $clean_files ) ) {
			wp_send_json_error( 'Invalid file data', 400 );
		}

		$guard   = new TimeoutGuard();
		$results = Scanner::scan_batch( $clean_files, $guard );

		$meta = Db::get_scan_meta();
		if ( $meta && ! empty( $meta['scan_id'] ) ) {
			Db::insert_results( $meta['scan_id'], $results );

			$new_scanned = (int) $meta['scanned_files'] + count( $results );
			$new_index   = (int) $meta['last_file_index'] + count( $clean_files );
			Db::update_scan_progress( $new_scanned, $new_index );
		}

		$response = array( 'files' => $results );

		$processed = $guard->get_items_processed();
		if ( $processed < count( $clean_files ) ) {
			$response['timeout']   = true;
			$response['remaining'] = array_slice( $clean_files, $processed );
		}

		wp_send_json_success( $response );
	}

	/**
	 * Scan for broken media (DB entries with missing files on disk).
	 */
	public function scan_broken() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		$results = Scanner::scan_broken_media();

		$meta = Db::get_scan_meta();
		if ( $meta && ! empty( $meta['scan_id'] ) ) {
			Db::insert_results( $meta['scan_id'], $results );
		}

		wp_send_json_success( array( 'files' => $results ) );
	}

	/**
	 * Return a paginated, filterable page of scan results from the database.
	 *
	 * Expects POST:
	 *   page          — int, page number (default 1).
	 *   per_page      — int, results per page (default 200).
	 *   status_filter — string, filter key (default '').
	 *   search        — string, file name search term (default '').
	 */
	public function scan_results_page() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		$args = array(
			'page'          => isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1,
			'per_page'      => isset( $_POST['per_page'] ) ? absint( $_POST['per_page'] ) : 200,
			'status_filter' => isset( $_POST['status_filter'] ) ? sanitize_text_field( wp_unslash( $_POST['status_filter'] ) ) : '',
			'search'        => isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '',
		);

		$data = Db::query_results( $args );
		wp_send_json_success( $data );
	}

	/**
	 * Return lightweight result IDs for all matching scan results.
	 *
	 * Used by the "Select All" flow so the frontend can build clean
	 * targets across all pages without loading full result data.
	 *
	 * Expects POST:
	 *   status_filter — string (default '').
	 *   search        — string (default '').
	 */
	public function scan_results_ids() {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		$this->verify_request();

		$args = array(
			'status_filter' => isset( $_POST['status_filter'] ) ? sanitize_text_field( wp_unslash( $_POST['status_filter'] ) ) : '',
			'search'        => isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '',
		);

		$results = Db::query_results_ids( $args );
		wp_send_json_success( array( 'results' => $results ) );
	}
}
