<?php
/**
 * Live reload — enqueues the polling JS for logged-in users and registers
 * the REST endpoint the JS hits.
 *
 * Frontend pages: auto-navigate when the customer's own user makes a change.
 * Admin pages: toast notification with an action button.
 *
 * The script is only enqueued when there are recent changes — quiet sites
 * pay zero polling cost.
 */

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

class PCD_PC_Live_Reload {

	public static function init(): void {
		add_action( 'rest_api_init', [ __CLASS__, 'register_routes' ] );
		add_action( 'wp_enqueue_scripts', [ __CLASS__, 'enqueue' ] );
		add_action( 'admin_enqueue_scripts', [ __CLASS__, 'enqueue' ] );
		// Catch writes from any WP REST client, not just our tools.
		add_filter( 'rest_post_dispatch', [ __CLASS__, 'track_rest_writes' ], 10, 3 );
	}

	/**
	 * Detect successful write requests against WP's REST API and mirror them
	 * into our change tracker so live reload fires regardless of which AI
	 * client triggered them. Skips our own /pressconduit/v1/* routes
	 * (those tools record their own change entries via the dispatcher).
	 */
	public static function track_rest_writes( $response, $server, $request ) {
		if ( ! ( $response instanceof WP_REST_Response ) ) {
			return $response;
		}
		$method = $request->get_method();
		$status = $response->get_status();
		if ( in_array( $method, [ 'GET', 'OPTIONS', 'HEAD' ], true ) || $status >= 400 ) {
			return $response;
		}
		$route = $request->get_route();
		// Our own tool calls already record changes via PCD_PC_Tools — skip.
		if ( 0 === strpos( $route, '/pressconduit/v1/' ) ) {
			return $response;
		}
		if ( false !== strpos( $route, '/autosaves' ) ) {
			return $response;
		}

		// Match any registered post type's REST base.
		$post_types = get_post_types( [ 'show_in_rest' => true ], 'objects' );
		$matched_pt = null;
		$post_id    = 0;
		foreach ( $post_types as $pt ) {
			$base = $pt->rest_base ?: $pt->name;
			if ( preg_match( '#/wp/v2/' . preg_quote( $base, '#' ) . '(?:/(\d+))?#', $route, $m ) ) {
				$matched_pt = $pt;
				$post_id    = ! empty( $m[1] ) ? (int) $m[1] : 0;
				break;
			}
		}

		if ( ! $matched_pt ) {
			return $response;
		}

		$data = $response->get_data();
		if ( ! $post_id && ! empty( $data['id'] ) ) {
			$post_id = (int) $data['id'];
		}

		$singular = $matched_pt->labels->singular_name ?: 'Item';
		if ( 201 === $status ) {
			$summary = sprintf( '%s created', $singular );
		} elseif ( 'DELETE' === $method ) {
			$summary = sprintf( '%s deleted', $singular );
		} else {
			$summary = sprintf( '%s updated', $singular );
		}
		if ( $post_id && 'attachment' !== $matched_pt->name ) {
			$title = '';
			if ( ! empty( $data['title']['rendered'] ) ) {
				$title = wp_strip_all_tags( $data['title']['rendered'] );
			} elseif ( ! empty( $data['title']['raw'] ) ) {
				$title = $data['title']['raw'];
			} elseif ( $post_id ) {
				$title = get_the_title( $post_id );
			}
			if ( $title ) {
				$summary .= ': ' . $title;
			}
		}

		$url       = $post_id ? get_permalink( $post_id ) : '';
		$admin_url = $post_id ? admin_url( "post.php?post={$post_id}&action=edit" ) : '';

		PCD_PC_Change_Tracker::mark( [
			'summary'      => $summary,
			'action_label' => 'View',
			'url'          => $url,
			'admin_url'    => $admin_url,
			'post_id'      => $post_id,
		] );

		return $response;
	}

	public static function register_routes(): void {
		register_rest_route( 'pressconduit/v1', '/recent-changes', [
			'methods'             => 'GET',
			'callback'            => [ __CLASS__, 'handle_recent_changes' ],
			'permission_callback' => function () {
				return current_user_can( 'manage_options' );
			},
			'args' => [
				'since' => [ 'type' => 'number', 'default' => 0 ],
			],
		] );
	}

	public static function handle_recent_changes( WP_REST_Request $request ) {
		$since = (float) $request->get_param( 'since' );
		return [
			'changes' => PCD_PC_Change_Tracker::get_since( $since ),
		];
	}

	private static function should_load(): bool {
		if ( ! is_user_logged_in() ) {
			return false;
		}
		// Only inject when something is in the buffer — quiet sites stay quiet.
		return PCD_PC_Change_Tracker::has_recent();
	}

	public static function enqueue(): void {
		if ( ! self::should_load() ) {
			return;
		}

		$css = PCD_PC_DIR . 'assets/live-reload.css';
		$js  = PCD_PC_DIR . 'assets/live-reload.js';

		if ( file_exists( $css ) ) {
			wp_enqueue_style(
				'pcd-pc-live-reload',
				PCD_PC_URL . 'assets/live-reload.css',
				[],
				(string) filemtime( $css )
			);
		}

		if ( file_exists( $js ) ) {
			wp_enqueue_script(
				'pcd-pc-live-reload',
				PCD_PC_URL . 'assets/live-reload.js',
				[],
				(string) filemtime( $js ),
				true
			);

			$post_id = 0;
			if ( is_admin() ) {
				$post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			}

			wp_localize_script( 'pcd-pc-live-reload', 'pcdDcLiveReload', [
				'endpoint' => esc_url( rest_url( 'pressconduit/v1/recent-changes' ) ),
				'nonce'    => wp_create_nonce( 'wp_rest' ),
				'isAdmin'  => is_admin() ? '1' : '',
				'postId'   => (string) $post_id,
				'userId'   => (string) get_current_user_id(),
			] );
		}
	}
}
