<?php
/**
 * IndexNow Manager
 *
 * Orchestrates IndexNow functionality: auto-submit on post publish/update,
 * manual submit via admin row/bulk actions, API key file serving, and
 * admin AJAX for manual URL submission.
 *
 * @package SEOvaultSEO\IndexNow
 */

namespace SEOvaultSEO\IndexNow;

use SEOvaultSEO\Core\Options;
use SEOvaultSEO\Utils\Logger;

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

/**
 * IndexNow manager class.
 */
class IndexNowManager {

	/**
	 * Track submitted post IDs to prevent double submission.
	 *
	 * @var array
	 */
	private $submitted = array();

	/**
	 * Store previous post status for transition detection.
	 *
	 * @var array
	 */
	private $previous_post_status = array();

	/**
	 * Store previous post permalinks for trash handling.
	 *
	 * @var array
	 */
	private $previous_post_permalinks = array();

	/**
	 * Singleton instance.
	 *
	 * @var IndexNowManager|null
	 */
	private static $instance = null;

	/**
	 * Get singleton instance.
	 *
	 * @return IndexNowManager
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Initialize hooks.
	 *
	 * @return void
	 */
	public function init() {
		if ( ! \SEOvaultSEO\Compatibility\Detector::should_output_seo() ) {
			return;
		}

		// AJAX handlers stay available so settings UI can submit/clear logs.
		add_action( 'wp_ajax_seovaultseo_indexnow_submit', array( $this, 'ajax_submit' ) );
		add_action( 'wp_ajax_seovaultseo_indexnow_clear_log', array( $this, 'ajax_clear_log' ) );

		if ( ! Options::is_indexnow_enabled() ) {
			return;
		}

		$api = IndexNowApi::get_instance();

		if ( '' === Options::get_indexnow_key() ) {
			$key = $api->generate_key();
			Options::set_indexnow_key( $key );
			$api->write_key_file( $key );
		}

		$post_types = Options::get_indexnow_post_types();
		if ( ! empty( $post_types ) ) {
			add_filter( 'wp_insert_post_data', array( $this, 'before_save_post' ), 10, 4 );

			foreach ( $post_types as $post_type ) {
				add_action( 'save_post_' . $post_type, array( $this, 'save_post' ), 10, 3 );
				add_filter( 'bulk_actions-edit-' . $post_type, array( $this, 'add_bulk_action' ) );
				add_filter( 'handle_bulk_actions-edit-' . $post_type, array( $this, 'handle_bulk_action' ), 10, 3 );
			}
		}

		add_filter( 'post_row_actions', array( $this, 'add_row_action' ), 10, 2 );
		add_filter( 'page_row_actions', array( $this, 'add_row_action' ), 10, 2 );
		add_action( 'admin_init', array( $this, 'handle_row_action' ) );

		add_action( 'wp', array( $this, 'serve_key_file' ) );
	}

	/**
	 * Store previous post status and permalink before saving.
	 *
	 * @param array $data     Post data.
	 * @param array $postarr  Raw post data.
	 * @param array $unsanitized_postarr Unsanitized post data.
	 * @param bool  $update   Whether this is an update.
	 * @return array
	 */
	public function before_save_post( $data, $postarr, $unsanitized_postarr, $update = false ) {
		if ( ! $update ) {
			return $data;
		}

		$this->previous_post_status[ $postarr['ID'] ]     = get_post_status( $postarr['ID'] );
		$this->previous_post_permalinks[ $postarr['ID'] ] = str_replace( '__trashed', '', get_permalink( $postarr['ID'] ) );

		return $data;
	}

	/**
	 * Auto-submit URL to IndexNow when a watched post type is published or trashed.
	 *
	 * @param int      $post_id Post ID.
	 * @param \WP_Post $post    Post object.
	 * @return void
	 */
	public function save_post( $post_id, $post ) {
		if ( in_array( $post_id, $this->submitted, true ) ) {
			return;
		}

		if ( ! in_array( $post->post_status, array( 'publish', 'trash' ), true ) ) {
			return;
		}

		if ( 'trash' === $post->post_status ) {
			$prev = isset( $this->previous_post_status[ $post_id ] ) ? $this->previous_post_status[ $post_id ] : '';
			if ( 'publish' !== $prev ) {
				return;
			}
		}

		if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
			return;
		}

		if ( ! $this->is_post_indexable( $post_id ) ) {
			return;
		}

		$url = get_permalink( $post );
		if ( 'trash' === $post->post_status ) {
			$url = isset( $this->previous_post_permalinks[ $post_id ] ) ? $this->previous_post_permalinks[ $post_id ] : $url;
		}

		$url = apply_filters( 'seovaultseo_indexnow_submit_url', $url, $post );
		if ( ! $url ) {
			return;
		}

		$api = IndexNowApi::get_instance();
		if ( ! $api->is_throttled( $url ) ) {
			$api->submit( $url, false );
			Logger::log(
				'IndexNow save_post: auto-submitted',
				array(
					'post_id' => (int) $post_id,
					'status'  => $post->post_status,
				)
			);
		} else {
			Logger::log( 'IndexNow save_post: throttled, skipping', array( 'post_id' => (int) $post_id ) );
		}

		$this->submitted[] = $post_id;
	}

	/**
	 * Check if a post is indexable (not noindex).
	 *
	 * @param int $post_id Post ID.
	 * @return bool
	 */
	private function is_post_indexable( $post_id ) {
		return ! \SEOvaultSEO\Core\PostMeta::is_noindex( $post_id );
	}

	/**
	 * Add bulk action to post list tables.
	 *
	 * @param array $actions Existing bulk actions.
	 * @return array
	 */
	public function add_bulk_action( $actions ) {
		$actions['seovaultseo_indexnow'] = __( 'Submit to IndexNow', 'seovault-ai' );
		return $actions;
	}

	/**
	 * Handle bulk action submission.
	 *
	 * @param string $redirect   Redirect URL.
	 * @param string $doaction   Action being performed.
	 * @param array  $object_ids Post IDs.
	 * @return string
	 */
	public function handle_bulk_action( $redirect, $doaction, $object_ids ) {
		if ( 'seovaultseo_indexnow' !== $doaction || empty( $object_ids ) ) {
			return $redirect;
		}

		if ( ! current_user_can( 'manage_options' ) ) {
			Logger::error( 'IndexNow bulk_action: capability check failed' );
			return $redirect;
		}

		Logger::log( 'IndexNow bulk_action: entered', array( 'post_count' => count( $object_ids ) ) );

		$urls = array();
		foreach ( $object_ids as $object_id ) {
			$post = get_post( $object_id );
			if ( $post && 'publish' === $post->post_status ) {
				$urls[] = get_permalink( $object_id );
			}
		}

		if ( ! empty( $urls ) ) {
			$api       = IndexNowApi::get_instance();
			$submitted = $api->submit( $urls, true );
			$count     = count( $urls );
			$this->add_admin_notice( $submitted, $count );
			Logger::log(
				'IndexNow bulk_action: submitted',
				array(
					'url_count'     => $count,
					'success'       => $submitted,
					'response_code' => $api->get_response_code(),
				)
			);
		} else {
			Logger::log( 'IndexNow bulk_action: no publishable URLs found' );
		}

		return add_query_arg( 'seovaultseo_indexnow_done', count( $urls ), $redirect );
	}

	/**
	 * Add row action link to post list tables.
	 *
	 * @param array    $actions Existing actions.
	 * @param \WP_Post $post    Post object.
	 * @return array
	 */
	public function add_row_action( $actions, $post ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return $actions;
		}

		if ( 'publish' !== $post->post_status ) {
			return $actions;
		}

		if ( ! Options::is_indexnow_enabled() ) {
			return $actions;
		}

		$post_types = Options::get_indexnow_post_types();
		if ( ! in_array( $post->post_type, $post_types, true ) ) {
			return $actions;
		}

		$link = wp_nonce_url(
			add_query_arg(
				array(
					'action'        => 'seovaultseo_indexnow_submit_post_' . $post->ID,
					'index_post_id' => $post->ID,
				)
			),
			'seovaultseo_indexnow_submit_post_' . $post->ID
		);

		$actions['seovaultseo_indexnow'] = '<a href="' . esc_url( $link ) . '">' . esc_html__( 'Submit to IndexNow', 'seovault-ai' ) . '</a>';

		return $actions;
	}

	/**
	 * Handle row action link click.
	 *
	 * @return void
	 */
	public function handle_row_action() {
		$action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : '';
		if ( 'seovaultseo_indexnow_submit_post_' !== substr( $action, 0, 33 ) ) {
			return;
		}

		$post_id = isset( $_GET['index_post_id'] ) ? absint( wp_unslash( $_GET['index_post_id'] ) ) : 0;
		if ( ! $post_id ) {
			return;
		}
		if ( $action !== 'seovaultseo_indexnow_submit_post_' . $post_id ) {
			return;
		}

		if ( ! current_user_can( 'manage_options' ) ) {
			Logger::error( 'IndexNow row_action: capability check failed', array( 'post_id' => $post_id ) );
			return;
		}

		$nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';
		if ( '' === $nonce || ! wp_verify_nonce( $nonce, 'seovaultseo_indexnow_submit_post_' . $post_id ) ) {
			Logger::error( 'IndexNow row_action: nonce verification failed', array( 'post_id' => $post_id ) );
			return;
		}

		Logger::log( 'IndexNow row_action: submitting', array( 'post_id' => $post_id ) );

		$api       = IndexNowApi::get_instance();
		$submitted = $api->submit( get_permalink( $post_id ), true );
		$this->add_admin_notice( $submitted, 1 );

		Logger::log(
			'IndexNow row_action: result',
			array(
				'post_id'       => $post_id,
				'success'       => $submitted,
				'response_code' => $api->get_response_code(),
			)
		);

		wp_safe_redirect( remove_query_arg( array( 'action', 'index_post_id', '_wpnonce' ) ) );
		exit;
	}

	/**
	 * Serve the IndexNow key file when the key URL is requested.
	 *
	 * @return void
	 */
	public function serve_key_file() {
		global $wp;

		$key = Options::get_indexnow_key();
		if ( '' === $key ) {
			return;
		}

		$key_location = trailingslashit( home_url() ) . $key . '.txt';
		$current_url  = home_url( $wp->request );

		if ( $key_location === $current_url ) {
			header( 'Content-Type: text/plain' );
			header( 'X-Robots-Tag: noindex' );
			status_header( 200 );
			echo esc_html( $key );
			exit;
		}
	}

	/**
	 * AJAX handler for manual URL submission from the admin console.
	 *
	 * @return void
	 */
	public function ajax_submit() {
		check_ajax_referer( 'seovaultseo_indexnow_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			Logger::error( 'AJAX indexnow_submit: capability check failed' );
			wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'seovault-ai' ) ), 403 );
		}

		$urls_raw = isset( $_POST['urls'] ) && is_string( $_POST['urls'] )
			? wp_strip_all_tags( wp_unslash( $_POST['urls'] ), true )
			: '';
		if ( '' === trim( $urls_raw ) ) {
			Logger::error( 'AJAX indexnow_submit: no URLs provided' );
			wp_send_json_error( array( 'message' => __( 'No URLs provided.', 'seovault-ai' ) ), 400 );
		}

		$urls = array_filter( array_map( 'trim', explode( "\n", $urls_raw ) ) );
		$urls = array_filter( array_map( 'esc_url_raw', $urls ) );

		if ( empty( $urls ) ) {
			Logger::error( 'AJAX indexnow_submit: no valid URLs provided' );
			wp_send_json_error( array( 'message' => __( 'No valid URLs provided.', 'seovault-ai' ) ), 400 );
		}

		Logger::log( 'AJAX indexnow_submit: submitting', array( 'url_count' => count( $urls ) ) );

		$api       = IndexNowApi::get_instance();
		$submitted = $api->submit( $urls, true );

		if ( $submitted ) {
			Logger::log(
				'AJAX indexnow_submit: success',
				array(
					'url_count'     => count( $urls ),
					'response_code' => $api->get_response_code(),
				)
			);
			wp_send_json_success(
				array(
					'message' => sprintf(
					/* translators: %d: number of URLs */
						_n( '%d URL submitted to IndexNow.', '%d URLs submitted to IndexNow.', count( $urls ), 'seovault-ai' ),
						count( $urls )
					),
					'code'    => $api->get_response_code(),
				)
			);
		} else {
			Logger::error(
				'AJAX indexnow_submit: failed',
				array(
					'url_count'     => count( $urls ),
					'response_code' => $api->get_response_code(),
					'error'         => $api->get_error(),
				)
			);
			wp_send_json_error(
				array(
					'message' => $api->get_error(),
					'code'    => $api->get_response_code(),
				),
				200
			);
		}
	}

	/**
	 * AJAX handler for clearing the submission log.
	 *
	 * @return void
	 */
	public function ajax_clear_log() {
		check_ajax_referer( 'seovaultseo_indexnow_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			Logger::error( 'AJAX indexnow_clear_log: capability check failed' );
			wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'seovault-ai' ) ), 403 );
		}

		Logger::log( 'AJAX indexnow_clear_log: clearing log' );
		IndexNowApi::get_instance()->clear_log();
		Logger::log( 'AJAX indexnow_clear_log: success' );
		wp_send_json_success( array( 'message' => __( 'Log cleared.', 'seovault-ai' ) ) );
	}

	/**
	 * Add an admin notice after a manual submission.
	 *
	 * @param bool $success Whether the submission was successful.
	 * @param int  $count   Number of URLs submitted.
	 * @return void
	 */
	private function add_admin_notice( $success, $count ) {
		if ( $success ) {
			$message = sprintf(
				/* translators: %s: number of URLs */
				_n( '%s URL submitted to IndexNow.', '%s URLs submitted to IndexNow.', $count, 'seovault-ai' ),
				$count
			);
			add_settings_error( 'seovault_seo_settings', 'indexnow_submitted', $message, 'updated' );
		} else {
			add_settings_error( 'seovault_seo_settings', 'indexnow_error', __( 'Error submitting URL(s) to IndexNow.', 'seovault-ai' ), 'error' );
		}
	}
}
