<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( 'MYAIO_Manual_Change_Tracker' ) ) {

	class MYAIO_Manual_Change_Tracker {

		// === CONFIG ===
		const WEBHOOK_URL = 'https://app.myaio.com/api/webhooks/projects/manual-change';
		const TOKEN       = 'mc_9F2xQ7A8WkM3RZyE4bL6D1H0pN5SJCtU';

		// Debounce window: collect many changes, send once.
		const DEBOUNCE_SECONDS = 120;

		// Internal keys.
		const TRANSIENT_PENDING  = 'myaio_manual_change_pending';
		const TRANSIENT_SENDING  = 'myaio_manual_change_sending';
		const TRANSIENT_SUPPRESS = 'myaio_manual_change_suppress';
		const CRON_HOOK          = 'myaio_send_manual_change_webhook';

		// Logging.
		const LOG_FILENAME = 'myaio-manual-change-tracker.log';

		// Per-request state (to compress noisy builders like Elementor).
		private static $request_post_save_ids = array();
		private static $request_meta_counts   = array(); // post_id => count.
		private static $request_option_counts = 0;

		// Ensure we only send once per request.
		private static $needs_shutdown_send = false;
		private static $sent_this_request   = false;

		public static function init() {
			add_action( self::CRON_HOOK, array( __CLASS__, 'send_webhook' ) );
			add_action( 'shutdown', array( __CLASS__, 'maybe_send_on_shutdown' ), 0 );

			// Posts / Pages / CPTs.
			add_action( 'save_post', array( __CLASS__, 'on_save_post' ), 10, 3 );
			add_action( 'before_delete_post', array( __CLASS__, 'on_post_delete' ), 10, 1 );
			add_action( 'trashed_post', array( __CLASS__, 'on_post_delete' ), 10, 1 );
			add_action( 'untrashed_post', array( __CLASS__, 'on_post_delete' ), 10, 1 );
			add_action( 'transition_post_status', array( __CLASS__, 'on_transition_post_status' ), 10, 3 );

			// Post Meta.
			add_action( 'added_post_meta', array( __CLASS__, 'on_postmeta_change' ), 10, 4 );
			add_action( 'updated_post_meta', array( __CLASS__, 'on_postmeta_change' ), 10, 4 );
			add_action( 'deleted_post_meta', array( __CLASS__, 'on_postmeta_change' ), 10, 4 );

			// Terms / Taxonomies.
			add_action( 'created_term', array( __CLASS__, 'on_term_change' ), 10, 3 );
			add_action( 'edited_term', array( __CLASS__, 'on_term_change' ), 10, 3 );
			add_action( 'delete_term', array( __CLASS__, 'on_term_delete' ), 10, 4 );

			// Comments.
			add_action( 'comment_post', array( __CLASS__, 'on_comment_change' ), 10, 3 );
			add_action( 'edit_comment', array( __CLASS__, 'on_comment_change' ), 10, 2 );
			add_action( 'deleted_comment', array( __CLASS__, 'on_comment_delete' ), 10, 1 );
			add_action( 'trashed_comment', array( __CLASS__, 'on_comment_delete' ), 10, 1 );
			add_action( 'untrashed_comment', array( __CLASS__, 'on_comment_delete' ), 10, 1 );

			// Media / Attachments.
			add_action( 'add_attachment', array( __CLASS__, 'on_site_change' ) );
			add_action( 'edit_attachment', array( __CLASS__, 'on_site_change' ) );
			add_action( 'delete_attachment', array( __CLASS__, 'on_site_change' ) );

			// Options / Settings.
			add_action( 'updated_option', array( __CLASS__, 'on_option_update' ), 10, 3 );
			add_action( 'added_option', array( __CLASS__, 'on_option_add' ), 10, 2 );
			add_action( 'deleted_option', array( __CLASS__, 'on_option_delete' ), 10, 1 );

			// Theme / Plugins / Updates.
			add_action( 'switch_theme', array( __CLASS__, 'on_site_change' ) );
			add_action( 'activated_plugin', array( __CLASS__, 'on_plugin_change' ), 10, 2 );
			add_action( 'deactivated_plugin', array( __CLASS__, 'on_plugin_change' ), 10, 2 );
			add_action( 'upgrader_process_complete', array( __CLASS__, 'on_upgrader_complete' ), 10, 2 );

			// Menus / Widgets / Customizer.
			add_action( 'wp_update_nav_menu', array( __CLASS__, 'on_site_change' ) );
			add_action( 'delete_nav_menu', array( __CLASS__, 'on_site_change' ) );
			add_action( 'customize_save_after', array( __CLASS__, 'on_site_change' ) );
			add_action( 'updated_widget', array( __CLASS__, 'on_site_change' ) );
			add_action( 'deleted_widget', array( __CLASS__, 'on_site_change' ) );
		}

		// ====== Public suppression helpers (call from MYAIO internal operations) ======
		public static function suppress_for_request() {
			set_transient( self::TRANSIENT_SUPPRESS, 1, 60 );
		}

		public static function unsuppress_for_request() {
			delete_transient( self::TRANSIENT_SUPPRESS );
		}

		/**
		 * Read a GET param without touching $_GET directly (avoids nonce warnings in sniffs).
		 *
		 * @param string $key
		 * @return string
		 */
		private static function get_get_param( $key ) {
			$key = (string) $key;

			// phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.filter_input -- Needed to avoid direct superglobal usage (PluginCheck nonce sniff).
			$val = filter_input( INPUT_GET, $key, FILTER_UNSAFE_RAW );

			if ( null === $val || false === $val ) {
				return '';
			}

			return sanitize_text_field( wp_unslash( (string) $val ) );
		}

		/**
		 * Read REQUEST_METHOD safely (avoid direct unsanitized superglobal usage).
		 *
		 * @return string
		 */
		private static function get_request_method() {
			// phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.filter_input -- Same reason as get_get_param().
			$val = filter_input( INPUT_SERVER, 'REQUEST_METHOD', FILTER_UNSAFE_RAW );
			if ( null === $val || false === $val ) {
				return 'GET';
			}
			$val = strtoupper( sanitize_text_field( wp_unslash( (string) $val ) ) );
			return $val ? $val : 'GET';
		}

		/**
		 * Read REQUEST_URI safely (avoid direct unsanitized superglobal usage).
		 *
		 * @return string
		 */
		private static function get_request_uri() {
			// phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.filter_input -- Same reason as get_get_param().
			$val = filter_input( INPUT_SERVER, 'REQUEST_URI', FILTER_UNSAFE_RAW );
			if ( null === $val || false === $val ) {
				return '';
			}
			return sanitize_text_field( wp_unslash( (string) $val ) );
		}

		// ========= Change handlers =========

		public static function on_save_post( $post_id, $post, $update ) {
			if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
				return;
			}
			if ( is_object( $post ) && isset( $post->post_type ) && self::is_our_post_type( $post->post_type ) ) {
				return;
			}

			self::$request_post_save_ids[ (int) $post_id ] = true;

			$title = '';
			if ( is_object( $post ) && isset( $post->post_title ) ) {
				$title = sanitize_text_field( (string) $post->post_title );
			} else {
				$title = sanitize_text_field( (string) get_the_title( $post_id ) );
			}

			$msg = $title ? ( 'Content updated: ' . $title ) : ( 'Content updated (post ID ' . (int) $post_id . ')' );
			self::record_change( $msg );
		}

		public static function on_transition_post_status( $new_status, $old_status, $post ) {
			if ( ! is_object( $post ) || empty( $post->ID ) ) {
				return;
			}
			if ( wp_is_post_autosave( $post->ID ) || wp_is_post_revision( $post->ID ) ) {
				return;
			}
			if ( isset( $post->post_type ) && self::is_our_post_type( $post->post_type ) ) {
				return;
			}
			if ( (string) $new_status === (string) $old_status ) {
				return;
			}

			$title = isset( $post->post_title ) ? sanitize_text_field( (string) $post->post_title ) : '';
			$msg   = $title
				? sprintf( 'Post status changed: %s (%s → %s)', $title, (string) $old_status, (string) $new_status )
				: sprintf( 'Post status changed (post ID %d): %s → %s', (int) $post->ID, (string) $old_status, (string) $new_status );

			self::record_change( $msg );
		}

		public static function on_post_delete( $post_id ) {
			$post_id = (int) $post_id;
			if ( $post_id <= 0 ) {
				return;
			}
			if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
				return;
			}

			$title = sanitize_text_field( (string) get_the_title( $post_id ) );
			$msg   = $title ? ( 'Post deleted/trashed: ' . $title ) : ( 'Post deleted/trashed (post ID ' . $post_id . ')' );
			self::record_change( $msg );
		}

		public static function on_postmeta_change( $meta_id, $object_id, $meta_key, $_meta_value ) {
			$post_id  = (int) $object_id;
			$meta_key = (string) $meta_key;

			if ( self::is_ours_key( $meta_key ) ) {
				return;
			}
			if ( self::is_noisy_meta_key( $meta_key ) ) {
				return;
			}

			if ( ! empty( self::$request_post_save_ids[ $post_id ] ) ) {
				if ( ! isset( self::$request_meta_counts[ $post_id ] ) ) {
					self::$request_meta_counts[ $post_id ] = 0;
				}
				self::$request_meta_counts[ $post_id ]++;
				return;
			}

			self::record_change( 'Post meta updated (post ID ' . $post_id . ')' );
		}

		public static function on_term_change( $term_id, $tt_id, $taxonomy ) {
			self::record_change( 'Taxonomy updated: ' . sanitize_text_field( (string) $taxonomy ) );
		}

		public static function on_term_delete( $term, $tt_id, $taxonomy, $deleted_term ) {
			self::record_change( 'Taxonomy deleted: ' . sanitize_text_field( (string) $taxonomy ) );
		}

		public static function on_comment_change( $comment_id, $approved = null ) {
			self::record_change( 'Comment updated (ID ' . (int) $comment_id . ')' );
		}

		public static function on_comment_delete( $comment_id ) {
			self::record_change( 'Comment deleted (ID ' . (int) $comment_id . ')' );
		}

		public static function on_option_update( $option, $old_value, $value ) {
			$option = (string) $option;

			if ( self::is_ours_key( $option ) ) {
				return;
			}
			if ( self::is_noisy_option( $option ) ) {
				return;
			}
			if ( ! empty( self::$request_post_save_ids ) ) {
				return;
			}
			if ( maybe_serialize( $old_value ) === maybe_serialize( $value ) ) {
				return;
			}

			self::$request_option_counts++;
			self::record_change( 'Settings updated' );
		}

		public static function on_option_add( $option, $value ) {
			$option = (string) $option;
			if ( self::is_ours_key( $option ) ) {
				return;
			}
			if ( self::is_noisy_option( $option ) ) {
				return;
			}
			if ( ! empty( self::$request_post_save_ids ) ) {
				return;
			}

			self::$request_option_counts++;
			self::record_change( 'Settings updated' );
		}

		public static function on_option_delete( $option ) {
			$option = (string) $option;
			if ( self::is_ours_key( $option ) ) {
				return;
			}
			if ( self::is_noisy_option( $option ) ) {
				return;
			}

			self::$request_option_counts++;
			self::record_change( 'Settings updated' );
		}

		public static function on_plugin_change( $plugin, $network_wide ) {
			self::record_change( 'Plugin state changed' );
		}

		public static function on_upgrader_complete( $upgrader, $hook_extra ) {
			self::record_change( 'Core/theme/plugin update completed' );
		}

		public static function on_site_change() {
			self::record_change( 'Site configuration updated' );
		}

		// ========= Core aggregator =========

		private static function record_change( $message ) {
			$message = trim( (string) $message );
			if ( '' === $message ) {
				return;
			}

			$suppress_reason = '';
			if ( self::is_suppressed_context( $suppress_reason ) ) {
				self::log_event(
					'suppressed_change',
					array(
						'message' => $message,
						'reason'  => $suppress_reason,
					)
				);
				return;
			}

			self::inject_builder_summaries();

			$pending = get_transient( self::TRANSIENT_PENDING );
			$changes = array();

			if ( is_array( $pending ) && isset( $pending['changes'] ) && is_array( $pending['changes'] ) ) {
				$changes = $pending['changes'];
			}

			if ( ! in_array( $message, $changes, true ) ) {
				$changes[] = $message;
			}

			set_transient(
				self::TRANSIENT_PENDING,
				array(
					't'       => time(),
					'changes' => $changes,
				),
				self::DEBOUNCE_SECONDS
			);

			self::log_event(
				'change_detected',
				array(
					'message'       => $message,
					'changes_count' => count( $changes ),
				)
			);

			if ( self::is_ajax_or_rest_request() ) {
				self::$needs_shutdown_send = true;
			}

			if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
				wp_schedule_single_event( time() + 10, self::CRON_HOOK );
			}
		}

		private static function inject_builder_summaries() {
			if ( empty( self::$request_meta_counts ) ) {
				return;
			}

			foreach ( self::$request_meta_counts as $post_id => $count ) {
				$post_id = (int) $post_id;
				$count   = (int) $count;
				if ( $post_id <= 0 || $count <= 0 ) {
					continue;
				}

				$title = sanitize_text_field( (string) get_the_title( $post_id ) );
				$msg   = $title
					? sprintf( 'Builder meta updated: %s (keys updated: %d)', $title, $count )
					: sprintf( 'Builder meta updated (post ID %d): keys updated: %d', $post_id, $count );

				$pending = get_transient( self::TRANSIENT_PENDING );
				$changes = array();
				if ( is_array( $pending ) && isset( $pending['changes'] ) && is_array( $pending['changes'] ) ) {
					$changes = $pending['changes'];
				}

				if ( ! in_array( $msg, $changes, true ) ) {
					$changes[] = $msg;
					set_transient(
						self::TRANSIENT_PENDING,
						array(
							't'       => time(),
							'changes' => $changes,
						),
						self::DEBOUNCE_SECONDS
					);
				}
			}

			self::$request_meta_counts = array();
		}

		public static function maybe_send_on_shutdown() {
			if ( self::$sent_this_request ) {
				return;
			}
			if ( ! self::$needs_shutdown_send ) {
				return;
			}

			$pending = get_transient( self::TRANSIENT_PENDING );
			if ( ! is_array( $pending ) || empty( $pending['changes'] ) ) {
				return;
			}

			self::$sent_this_request = true;
			self::log_event( 'shutdown_send_attempt', array( 'trigger' => 'ajax_or_rest_shutdown' ) );
			self::send_webhook();
		}

		public static function send_webhook() {
			if ( get_transient( self::TRANSIENT_SENDING ) ) {
				return;
			}
			set_transient( self::TRANSIENT_SENDING, 1, 60 );

			self::inject_builder_summaries();

			$pending = get_transient( self::TRANSIENT_PENDING );
			delete_transient( self::TRANSIENT_PENDING );

			$verified = (bool) get_option( 'myaio_sac_api_verified', null );
			if ( false === $verified ) {
				$verified = (bool) get_option( 'sac_api_verified', false );
			}
			if ( ! $verified ) {
				self::log_event( 'send_skipped', array( 'reason' => 'api_verified_false' ) );
				delete_transient( self::TRANSIENT_SENDING );
				return;
			}

			$website_id = (string) get_option( 'myaio_sac_api_key', '' );
			if ( '' === $website_id ) {
				$website_id = (string) get_option( 'sac_api_key', '' );
			}
			if ( '' === $website_id ) {
				self::log_event( 'send_skipped', array( 'reason' => 'missing_website_id' ) );
				delete_transient( self::TRANSIENT_SENDING );
				return;
			}

			$changes = array();
			if ( is_array( $pending ) && isset( $pending['changes'] ) && is_array( $pending['changes'] ) ) {
				$changes = $pending['changes'];
			}
			if ( empty( $changes ) ) {
				self::log_event( 'send_skipped', array( 'reason' => 'no_changes' ) );
				delete_transient( self::TRANSIENT_SENDING );
				return;
			}

			$change_str = implode( '; ', array_values( array_unique( $changes ) ) );

			$payload = array(
				'token'      => self::TOKEN,
				'website_id' => $website_id,
				'change'     => $change_str,
			);

			self::log_event( 'outgoing_webhook', array( 'payload' => $payload ) );

			$response = wp_remote_post(
				self::WEBHOOK_URL,
				array(
					'timeout' => 20,
					'headers' => array( 'Content-Type' => 'application/json' ),
					'body'    => wp_json_encode( $payload ),
				)
			);

			if ( is_wp_error( $response ) ) {
				self::log_event(
					'webhook_error',
					array(
						'error' => $response->get_error_message(),
					)
				);
				delete_transient( self::TRANSIENT_SENDING );
				return;
			}

			self::log_event(
				'webhook_response',
				array(
					'http_code' => (int) wp_remote_retrieve_response_code( $response ),
					'body'      => (string) wp_remote_retrieve_body( $response ),
				)
			);

			delete_transient( self::TRANSIENT_SENDING );
		}

		// ========= Suppression / helpers =========

		private static function is_suppressed_context( &$reason_out = '' ) {
			$reason_out = '';

			if ( get_transient( self::TRANSIENT_SUPPRESS ) ) {
				$reason_out = 'explicit_transient_suppress';
				return true;
			}
			if ( defined( 'MYAIO_MANUAL_CHANGE_SUPPRESS' ) && MYAIO_MANUAL_CHANGE_SUPPRESS ) {
				$reason_out = 'explicit_constant_suppress';
				return true;
			}

			// Request method (safe getter).
			$method = self::get_request_method();

			// Context flags.
			$is_ajax = function_exists( 'wp_doing_ajax' ) ? wp_doing_ajax() : ( defined( 'DOING_AJAX' ) && DOING_AJAX );
			$is_rest = ( defined( 'REST_REQUEST' ) && REST_REQUEST );
			$is_cli  = ( defined( 'WP_CLI' ) && WP_CLI );

			$is_edit_ctx    = is_admin() || $is_ajax || $is_rest || $is_cli;
			$is_frontend_ro = ! $is_edit_ctx;

			if ( $is_frontend_ro ) {
				if ( 'GET' === $method || 'HEAD' === $method ) {
					$reason_out = 'frontend_view_request';
					return true;
				}
				if ( ! is_user_logged_in() || ! ( current_user_can( 'edit_posts' ) || current_user_can( 'manage_options' ) ) ) {
					$reason_out = 'frontend_non_editor_request';
					return true;
				}
			}

			// Request URI (safe getter).
			$uri = self::get_request_uri();

			// Suppress MYAIO REST endpoints.
			if ( $is_rest && '' !== $uri ) {
				if (
					false !== stripos( $uri, '/wp-json/myaio' ) ||
					false !== stripos( $uri, '/wp-json/sac' ) ||
					false !== stripos( $uri, '/myaio/' ) ||
					false !== stripos( $uri, '/sac/' )
				) {
					$reason_out = 'myaio_rest_request';
					return true;
				}
			}

			/**
			 * IMPORTANT:
			 * Do not read $_POST / $_REQUEST here; nonce sniffs treat that as "processing form data".
			 * For heuristics, use filter_input-based GET helpers only.
			 */

			// AJAX action heuristic (GET only).
			if ( $is_ajax ) {
				$action = self::get_get_param( 'action' );
				if ( $action && ( 0 === stripos( $action, 'myaio_' ) || 0 === stripos( $action, 'sac_' ) ) ) {
					$reason_out = 'myaio_ajax_action_get';
					return true;
				}
			}

			// Admin action heuristic (GET only).
			if ( is_admin() ) {
				$action = self::get_get_param( 'action' );
				if ( $action && ( 0 === stripos( $action, 'myaio_' ) || 0 === stripos( $action, 'sac_' ) ) ) {
					$reason_out = 'myaio_admin_action_get';
					return true;
				}
			}

			// MYAIO admin page heuristic (GET only).
			if ( is_admin() ) {
				$page = self::get_get_param( 'page' );
				if ( $page && ( false !== stripos( $page, 'myaio' ) || false !== stripos( $page, 'sac' ) ) ) {
					$reason_out = 'myaio_admin_page_get';
					return true;
				}
			}

			// Referer check using WP API (no direct $_SERVER['HTTP_REFERER']).
			if ( is_admin() ) {
				$ref = wp_get_referer();
				if ( is_string( $ref ) && '' !== $ref ) {
					$ref = sanitize_text_field( $ref );
					if ( false !== stripos( $ref, 'page=myaio' ) || false !== stripos( $ref, 'page=sac' ) ) {
						$reason_out = 'myaio_referer';
						return true;
					}
				}
			}

			// Plugin activation heuristic (GET only).
			if ( is_admin() ) {
				$action = self::get_get_param( 'action' );
				if ( 'activate' === $action || 'activate-selected' === $action ) {
					$plugin = self::get_get_param( 'plugin' );
					if ( $plugin && false !== stripos( $plugin, 'myaio' ) ) {
						$reason_out = 'myaio_plugin_activation';
						return true;
					}
				}
			}

			return false;
		}

		private static function is_ajax_or_rest_request() {
			return ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST );
		}

		private static function is_ours_key( $key ) {
			return ( is_string( $key ) && ( 0 === stripos( $key, 'sac_' ) || 0 === stripos( $key, 'myaio_' ) ) );
		}

		private static function is_noisy_option( $option ) {
			if ( ! is_string( $option ) || '' === $option ) {
				return false;
			}

			if ( 0 === stripos( $option, '_transient_' ) || 0 === stripos( $option, '_site_transient_' ) ) {
				return true;
			}
			if ( 'cron' === $option ) {
				return true;
			}

			if ( 'rewrite_rules' === $option ) {
				return false;
			}
			if ( false !== stripos( $option, '_edit_lock' ) || false !== stripos( $option, '_edit_last' ) ) {
				return true;
			}

			return false;
		}

		private static function is_noisy_meta_key( $meta_key ) {
			if ( ! is_string( $meta_key ) || '' === $meta_key ) {
				return false;
			}

			if ( '_edit_lock' === $meta_key || '_edit_last' === $meta_key ) {
				return true;
			}
			if ( 0 === stripos( $meta_key, '_oembed_' ) ) {
				return true;
			}

			return false;
		}

		private static function is_our_post_type( $post_type ) {
			$post_type = (string) $post_type;
			if ( '' === $post_type ) {
				return false;
			}
			return ( false !== stripos( $post_type, 'myaio' ) || false !== stripos( $post_type, 'sac' ) );
		}

		private static function log_event( $event, array $data = array() ) {
			$entry = array(
				'ts'      => gmdate( 'c' ),
				'event'   => (string) $event,
				'uri'     => self::get_request_uri(),
				'method'  => self::get_request_method(),
				'user_id' => function_exists( 'get_current_user_id' ) ? (int) get_current_user_id() : 0,
				'ajax'    => ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ? 1 : 0,
				'rest'    => ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ? 1 : 0,
				'data'    => $data,
			);

			$line = wp_json_encode( $entry );
			if ( ! $line ) {
				return;
			}

			$uploads  = wp_upload_dir();
			$log_path = trailingslashit( $uploads['basedir'] ) . self::LOG_FILENAME;

			// phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_file_put_contents -- Local plugin log file.
			@file_put_contents( $log_path, $line . PHP_EOL, FILE_APPEND | LOCK_EX );
		}
	}

	add_action( 'init', array( 'MYAIO_Manual_Change_Tracker', 'init' ) );
}