<?php
/**
 * Color probe session handling and AJAX endpoints.
 *
 * @package The_Studio_Player
 */

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

/**
 * Get the transient lifetime for color probe sessions.
 *
 * @return int Session lifetime in seconds.
 */
function thespl_color_probe_session_ttl() {
	return 10 * MINUTE_IN_SECONDS;
}

/**
 * Build the transient key for a color probe session.
 *
 * @param string $session_id Session identifier.
 * @return string Transient key.
 */
function thespl_color_probe_transient_key( $session_id ) {
	$session_id = is_string( $session_id ) ? sanitize_key( $session_id ) : '';
	return 'thespl_probe_' . $session_id;
}

/**
 * Hash a probe token for secure storage.
 *
 * @param string $token Raw token value.
 * @return string Token hash.
 */
function thespl_color_probe_token_hash( $token ) {
	$token = is_string( $token ) ? $token : '';
	return hash_hmac( 'sha256', $token, wp_salt( 'auth' ) );
}

/**
 * Verify a submitted token against the stored session hash.
 *
 * @param array  $session Session payload.
 * @param string $token Incoming raw token.
 * @return bool True when token is valid.
 */
function thespl_color_probe_verify_token( $session, $token ) {
	if ( ! is_array( $session ) || ! isset( $session['token_hash'] ) || ! is_string( $session['token_hash'] ) ) {
		return false;
	}
	$incoming = thespl_color_probe_token_hash( $token );
	return '' !== $incoming && hash_equals( $session['token_hash'], $incoming );
}

/**
 * Load a color probe session from transient storage.
 *
 * @param string $session_id Session identifier.
 * @return array Session payload or empty array.
 */
function thespl_color_probe_get_session( $session_id ) {
	$key = thespl_color_probe_transient_key( $session_id );
	$val = get_transient( $key );
	return is_array( $val ) ? $val : array();
}

/**
 * Store a color probe session in transient storage.
 *
 * @param string $session_id Session identifier.
 * @param array  $session Session payload.
 * @return void
 */
function thespl_color_probe_set_session( $session_id, $session ) {
	$key = thespl_color_probe_transient_key( $session_id );
	set_transient( $key, $session, thespl_color_probe_session_ttl() );
}

/**
 * Delete a color probe session from transient storage.
 *
 * @param string $session_id Session identifier.
 * @return void
 */
function thespl_color_probe_delete_session( $session_id ) {
	$key = thespl_color_probe_transient_key( $session_id );
	delete_transient( $key );
}

/**
 * Resolve the active playlist post ID from the admin context.
 *
 * @return int Playlist post ID or zero.
 */
function thespl_color_probe_get_post_id_from_admin() {
	$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
	if ( ! $screen || ! isset( $screen->post_type ) || 'thespl_playlist' !== $screen->post_type ) {
		return 0;
	}

	global $post;
	if ( $post instanceof WP_Post && 'thespl_playlist' === $post->post_type && current_user_can( 'edit_post', $post->ID ) ) {
		return (int) $post->ID;
	}

	$post_id       = function_exists( 'thespl_get_requested_admin_post_id' ) ? thespl_get_requested_admin_post_id() : 0;
	$playlist_post = $post_id > 0 ? get_post( $post_id ) : null;
	if ( ! $playlist_post instanceof WP_Post || 'thespl_playlist' !== $playlist_post->post_type ) {
		return 0;
	}
	if ( ! current_user_can( 'edit_post', $playlist_post->ID ) ) {
		return 0;
	}

	return (int) $playlist_post->ID;
}

/**
 * Normalize and validate a same-site target URL for probing.
 *
 * @param string $target_url_raw Raw target URL input.
 * @return string Normalized URL or empty string when invalid.
 */
function thespl_color_probe_normalize_target_url( $target_url_raw ) {
	$raw = is_string( $target_url_raw ) ? trim( $target_url_raw ) : '';
	if ( '' === $raw ) {
		return '';
	}
	if ( 0 === strpos( $raw, '/' ) ) {
		$raw = home_url( $raw );
	}
	$parsed = wp_parse_url( $raw );
	if ( ! is_array( $parsed ) || empty( $parsed['host'] ) ) {
		$raw    = home_url( '/' . ltrim( $raw, '/' ) );
		$parsed = wp_parse_url( $raw );
	}
	if ( ! is_array( $parsed ) || empty( $parsed['host'] ) ) {
		return '';
	}
	$home = wp_parse_url( home_url() );
	if ( ! is_array( $home ) || empty( $home['host'] ) ) {
		return '';
	}
	$target_host = strtolower( (string) $parsed['host'] );
	$home_host   = strtolower( (string) $home['host'] );
	if ( $target_host !== $home_host ) {
		return '';
	}
	$normalized = esc_url_raw( $raw );
	if ( '' === $normalized ) {
		return '';
	}
	$normalized = strtok( $normalized, '#' );
	return is_string( $normalized ) ? $normalized : '';
}

/**
 * Get the list of supported palette algorithms.
 *
 * @return string[] Allowed algorithm keys.
 */
function thespl_color_probe_get_allowed_algorithms() {
	return array( 'auto', 'surface_shift', 'vivid_contrast' );
}

/**
 * Normalize an algorithm key to a supported fallback-safe value.
 *
 * @param string $raw Raw algorithm key.
 * @return string Normalized algorithm key.
 */
function thespl_color_probe_normalize_algorithm( $raw ) {
	$algorithm = is_string( $raw ) ? sanitize_key( $raw ) : '';
	if ( '' === $algorithm ) {
		return 'auto';
	}
	$allowed = thespl_color_probe_get_allowed_algorithms();
	if ( in_array( $algorithm, $allowed, true ) ) {
		return $algorithm;
	}
	return 'auto';
}

/**
 * Enqueue admin assets for the color probe workflow.
 *
 * @param string $hook Current admin page hook.
 * @return void
 */
function thespl_color_probe_enqueue_admin_assets( $hook ) {
	if ( ! is_string( $hook ) || ( 'post.php' !== $hook && 'post-new.php' !== $hook ) ) {
		return;
	}
	$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
	if ( ! $screen || ! isset( $screen->post_type ) || 'thespl_playlist' !== $screen->post_type ) {
		return;
	}
	$post_id = thespl_color_probe_get_post_id_from_admin();
	$deps    = array();
	if ( wp_script_is( 'thespl-admin-design', 'registered' ) || wp_script_is( 'thespl-admin-design', 'enqueued' ) ) {
		$deps[] = 'thespl-admin-design';
	}
	wp_enqueue_script(
		'thespl-admin-color-probe',
		THESPL_PLUGIN_URL . 'assets/js/admin-color-probe.js',
		$deps,
		(string) THESPL_VERSION,
		true
	);
	wp_localize_script(
		'thespl-admin-color-probe',
		'thesplColorProbeAdmin',
		array(
			'ajaxUrl'    => admin_url( 'admin-ajax.php' ),
			'nonce'      => wp_create_nonce( 'thespl_color_probe_admin' ),
			'postId'     => $post_id,
			'homeUrl'    => home_url( '/' ),
			'homeHost'   => wp_parse_url( home_url(), PHP_URL_HOST ),
			'autoApply'  => true,
			'algorithm'  => 'auto',
			'algorithms' => array(
				'auto'           => __( 'Algorithm 1 · Balanced', 'the-studio-player' ),
				'surface_shift'  => __( 'Algorithm 2 · Surface Shift', 'the-studio-player' ),
				'vivid_contrast' => __( 'Algorithm 3 · Vivid Contrast', 'the-studio-player' ),
			),
			'i18n'       => array(
				'sectionTitle'       => __( 'Live DOM color analysis (Beta)', 'the-studio-player' ),
				'urlLabel'           => __( 'Target page URL', 'the-studio-player' ),
				'algorithmLabel'     => __( 'Palette algorithm', 'the-studio-player' ),
				'urlPlaceholder'     => __( 'https://your-site.example/your-player-page/', 'the-studio-player' ),
				'runButton'          => __( 'Analyze target page', 'the-studio-player' ),
				'running'            => __( 'Starting analysis session…', 'the-studio-player' ),
				'waiting'            => __( 'Waiting for frontend result…', 'the-studio-player' ),
				'popupBlocked'       => __( 'Could not open the target page. Please allow popups and try again.', 'the-studio-player' ),
				'invalidUrl'         => __( 'Please enter a URL from this site.', 'the-studio-player' ),
				'failedStart'        => __( 'Failed to start analysis.', 'the-studio-player' ),
				'failedStatus'       => __( 'Failed to read analysis status.', 'the-studio-player' ),
				'completed'          => __( 'Colors imported. You can now generate a palette from these values.', 'the-studio-player' ),
				'expired'            => __( 'Analysis session expired. Please retry.', 'the-studio-player' ),
				'failed'             => __( 'Analysis failed. Please retry or enter colors manually.', 'the-studio-player' ),
				'statusPending'      => __( 'Session created. Waiting for frontend…', 'the-studio-player' ),
				'statusRunning'      => __( 'Frontend analysis is running…', 'the-studio-player' ),
				'statusInsufficient' => __( 'Not enough visible color signals found on this page.', 'the-studio-player' ),
			),
		)
	);
}
add_action( 'admin_enqueue_scripts', 'thespl_color_probe_enqueue_admin_assets' );

/**
 * Register frontend color probe query vars.
 *
 * @param array $vars Public query vars.
 * @return array
 */
function thespl_color_probe_add_query_vars( $vars ) {
	$vars[] = 'thespl_color_probe';
	$vars[] = 'thespl_probe_session';
	$vars[] = 'thespl_probe_token';
	$vars[] = 'thespl_probe_pid';
	$vars[] = 'thespl_probe_nonce';
	return $vars;
}
add_filter( 'query_vars', 'thespl_color_probe_add_query_vars' );

/**
 * Read the frontend color probe nonce from the request.
 *
 * @return string Probe nonce or empty string.
 */
function thespl_color_probe_get_frontend_probe_nonce() {
	$probe_nonce = get_query_var( 'thespl_probe_nonce' );

	return is_scalar( $probe_nonce ) ? sanitize_text_field( (string) $probe_nonce ) : '';
}

/**
 * Read the frontend color probe enabled flag from the request.
 *
 * @return string Probe enabled flag or empty string.
 */
function thespl_color_probe_get_frontend_probe_flag() {
	$probe_flag = get_query_var( 'thespl_color_probe' );

	return is_scalar( $probe_flag ) ? sanitize_text_field( (string) $probe_flag ) : '';
}

/**
 * Read the frontend color probe session ID from the request.
 *
 * @return string Probe session ID or empty string.
 */
function thespl_color_probe_get_frontend_probe_session_id() {
	$session_id = get_query_var( 'thespl_probe_session' );

	return is_scalar( $session_id ) ? sanitize_key( (string) $session_id ) : '';
}

/**
 * Read the frontend color probe token from the request.
 *
 * @return string Probe token or empty string.
 */
function thespl_color_probe_get_frontend_probe_token() {
	$token = get_query_var( 'thespl_probe_token' );

	return is_scalar( $token ) ? sanitize_text_field( (string) $token ) : '';
}

/**
 * Verify the frontend color probe nonce before reading probe state.
 *
 * @return string Verified nonce or empty string.
 */
function thespl_color_probe_verify_frontend_probe_nonce() {
	$probe_nonce = thespl_color_probe_get_frontend_probe_nonce();
	if ( '' === $probe_nonce ) {
		return '';
	}

	// This probe runs through a frontend query-var flow, not a classic wp-admin form submit.
	// Use a direct nonce verification here and keep admin-only referer checks for actual admin requests.
	if ( ! wp_verify_nonce( $probe_nonce, 'thespl_color_probe_frontend' ) ) {
		return '';
	}

	return $probe_nonce;
}

/**
 * Read validated frontend probe request parameters after the nonce gate passed.
 *
 * @param string $probe_nonce Verified frontend probe nonce.
 * @return array{
 *  nonce:string,
 *  enabled:bool,
 *  session_id:string,
 *  token:string
 * }
 */
function thespl_color_probe_get_verified_frontend_request_context( $probe_nonce ) {
	$probe_nonce = is_string( $probe_nonce ) ? $probe_nonce : '';
	if ( '' === $probe_nonce ) {
		return array(
			'nonce'      => '',
			'enabled'    => false,
			'session_id' => '',
			'token'      => '',
		);
	}

	$probe_flag = thespl_color_probe_get_frontend_probe_flag();
	if ( '1' !== $probe_flag ) {
		return array(
			'nonce'      => $probe_nonce,
			'enabled'    => false,
			'session_id' => '',
			'token'      => '',
		);
	}

	$session_id = thespl_color_probe_get_frontend_probe_session_id();
	$token      = thespl_color_probe_get_frontend_probe_token();
	if ( '' === $session_id || '' === $token ) {
		return array(
			'nonce'      => $probe_nonce,
			'enabled'    => false,
			'session_id' => '',
			'token'      => '',
		);
	}

	return array(
		'nonce'      => $probe_nonce,
		'enabled'    => true,
		'session_id' => $session_id,
		'token'      => $token,
	);
}

add_action(
	'wp_ajax_thespl_color_probe_start',
	function () {
		check_ajax_referer( 'thespl_color_probe_admin', 'nonce' );
		$playlist_id = isset( $_POST['playlist_id'] ) ? absint( wp_unslash( $_POST['playlist_id'] ) ) : 0;
		if ( $playlist_id <= 0 || ! thespl_current_user_can_policy( 'playlist_edit_own', array( 'object_id' => $playlist_id ) ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		$target_url_raw = isset( $_POST['target_url'] ) && is_string( $_POST['target_url'] ) ? sanitize_text_field( wp_unslash( $_POST['target_url'] ) ) : '';
		$target_url     = thespl_color_probe_normalize_target_url( $target_url_raw );
		$algorithm_raw  = isset( $_POST['algorithm'] ) && is_string( $_POST['algorithm'] ) ? sanitize_key( wp_unslash( $_POST['algorithm'] ) ) : '';
		$algorithm      = thespl_color_probe_normalize_algorithm( $algorithm_raw );
		if ( '' === $target_url ) {
			wp_send_json_error( array( 'message' => 'invalid_url' ), 400 );
		}
		$session_id = str_replace( '-', '', wp_generate_uuid4() );
		$token      = wp_generate_password( 32, false, false );
		$path       = wp_parse_url( $target_url, PHP_URL_PATH );
		$query      = wp_parse_url( $target_url, PHP_URL_QUERY );
		$return_url = get_edit_post_link( $playlist_id, '' );
		$return_url = is_string( $return_url ) ? esc_url_raw( $return_url ) : '';
		if ( '' === $return_url ) {
			$return_url = admin_url( 'post.php?post=' . $playlist_id . '&action=edit' );
		}
		$session = array(
			'owner_user_id' => get_current_user_id(),
			'playlist_id'   => $playlist_id,
			'target_url'    => $target_url,
			'target_path'   => is_string( $path ) ? $path : '/',
			'target_query'  => is_string( $query ) ? $query : '',
			'status'        => 'pending',
			'algorithm'     => $algorithm,
			'token_hash'    => thespl_color_probe_token_hash( $token ),
			'result'        => array(),
			'error_code'    => '',
			'return_url'    => $return_url,
			'created_at'    => time(),
			'expires_at'    => time() + thespl_color_probe_session_ttl(),
		);
		thespl_color_probe_set_session( $session_id, $session );
		$frontend_nonce = wp_create_nonce( 'thespl_color_probe_frontend' );
		$open_url       = add_query_arg(
			array(
				'thespl_color_probe'   => '1',
				'thespl_probe_session' => $session_id,
				'thespl_probe_token'   => $token,
				'thespl_probe_pid'     => $playlist_id,
				'thespl_probe_nonce'   => $frontend_nonce,
			),
			$target_url
		);
		wp_send_json_success(
			array(
				'session_id' => $session_id,
				'token'      => $token,
				'open_url'   => esc_url_raw( $open_url ),
			)
		);
	}
);

add_action(
	'wp_ajax_thespl_color_probe_status',
	function () {
		check_ajax_referer( 'thespl_color_probe_admin', 'nonce' );
		if ( ! thespl_current_user_can_policy( 'plugin_access' ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		$session_id = isset( $_POST['session_id'] ) ? sanitize_key( wp_unslash( (string) $_POST['session_id'] ) ) : '';
		$token      = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['token'] ) ) : '';
		if ( '' === $session_id || '' === $token ) {
			wp_send_json_error( array( 'message' => 'invalid_session' ), 400 );
		}
		$session = thespl_color_probe_get_session( $session_id );
		if ( empty( $session ) ) {
			wp_send_json_success(
				array(
					'status' => 'expired',
				)
			);
		}
		if ( ! isset( $session['owner_user_id'] ) || get_current_user_id() !== (int) $session['owner_user_id'] ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		if ( ! thespl_color_probe_verify_token( $session, $token ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		$playlist_id = isset( $session['playlist_id'] ) ? absint( $session['playlist_id'] ) : 0;
		if ( $playlist_id <= 0 || ! thespl_current_user_can_policy( 'playlist_edit_own', array( 'object_id' => $playlist_id ) ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		if ( isset( $session['expires_at'] ) && time() > (int) $session['expires_at'] ) {
			thespl_color_probe_delete_session( $session_id );
			wp_send_json_success(
				array(
					'status' => 'expired',
				)
			);
		}
		$status = isset( $session['status'] ) ? (string) $session['status'] : 'pending';
		$data   = array(
			'status' => $status,
		);
		if ( 'completed' === $status && isset( $session['result'] ) && is_array( $session['result'] ) ) {
			$data['result'] = $session['result'];
		}
		if ( 'failed' === $status && isset( $session['error_code'] ) && is_string( $session['error_code'] ) ) {
			$data['error_code'] = $session['error_code'];
		}
		wp_send_json_success( $data );
	}
);

add_action(
	'wp_enqueue_scripts',
	function () {
		if ( ! is_user_logged_in() ) {
			return;
		}
		$probe_nonce = thespl_color_probe_verify_frontend_probe_nonce();
		if ( '' === $probe_nonce ) {
			return;
		}
		$request_context = thespl_color_probe_get_verified_frontend_request_context( $probe_nonce );
		if ( empty( $request_context['enabled'] ) || true !== $request_context['enabled'] ) {
			return;
		}
		$session_id = $request_context['session_id'];
		$token      = $request_context['token'];
		$session    = thespl_color_probe_get_session( $session_id );
		if ( empty( $session ) ) {
			return;
		}
		if ( ! isset( $session['owner_user_id'] ) || get_current_user_id() !== (int) $session['owner_user_id'] ) {
			return;
		}
		if ( ! thespl_color_probe_verify_token( $session, $token ) ) {
			return;
		}
		if ( isset( $session['expires_at'] ) && time() > (int) $session['expires_at'] ) {
			thespl_color_probe_delete_session( $session_id );
			return;
		}
		$session['status'] = 'running';
		thespl_color_probe_set_session( $session_id, $session );
		wp_enqueue_script(
			'thespl-frontend-color-probe',
			THESPL_PLUGIN_URL . 'assets/js/frontend-color-probe.js',
			array(),
			(string) THESPL_VERSION,
			true
		);
		wp_localize_script(
			'thespl-frontend-color-probe',
			'thesplColorProbeFrontend',
			array(
				'ajaxUrl'    => admin_url( 'admin-ajax.php' ),
				'nonce'      => wp_create_nonce( 'thespl_color_probe_submit' ),
				'sessionId'  => $session_id,
				'token'      => $token,
				'returnUrl'  => isset( $session['return_url'] ) ? esc_url_raw( (string) $session['return_url'] ) : '',
				'playlistId' => isset( $session['playlist_id'] ) ? (int) $session['playlist_id'] : 0,
				'algorithm'  => thespl_color_probe_normalize_algorithm( isset( $session['algorithm'] ) ? (string) $session['algorithm'] : 'auto' ),
			)
		);
	}
);

add_action(
	'wp_ajax_thespl_color_probe_submit',
	function () {
		check_ajax_referer( 'thespl_color_probe_submit', 'nonce' );
		if ( ! thespl_current_user_can_policy( 'plugin_access' ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		$session_id = isset( $_POST['session_id'] ) ? sanitize_key( wp_unslash( (string) $_POST['session_id'] ) ) : '';
		$token      = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['token'] ) ) : '';
		if ( '' === $session_id || '' === $token ) {
			wp_send_json_error( array( 'message' => 'invalid_session' ), 400 );
		}
		$session = thespl_color_probe_get_session( $session_id );
		if ( empty( $session ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		if ( ! isset( $session['owner_user_id'] ) || get_current_user_id() !== (int) $session['owner_user_id'] ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		if ( ! thespl_color_probe_verify_token( $session, $token ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		$playlist_id = isset( $session['playlist_id'] ) ? absint( $session['playlist_id'] ) : 0;
		if ( $playlist_id <= 0 || ! thespl_current_user_can_policy( 'playlist_edit_own', array( 'object_id' => $playlist_id ) ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
		if ( isset( $session['expires_at'] ) && time() > (int) $session['expires_at'] ) {
			thespl_color_probe_delete_session( $session_id );
			wp_send_json_error( array( 'message' => 'expired' ), 410 );
		}
		$background = isset( $_POST['background'] ) ? sanitize_hex_color( wp_unslash( (string) $_POST['background'] ) ) : '';
		$text_color = isset( $_POST['text_color'] ) ? sanitize_hex_color( wp_unslash( (string) $_POST['text_color'] ) ) : '';
		$accent_1   = isset( $_POST['accent_1'] ) ? sanitize_hex_color( wp_unslash( (string) $_POST['accent_1'] ) ) : '';
		$accent_2   = isset( $_POST['accent_2'] ) ? sanitize_hex_color( wp_unslash( (string) $_POST['accent_2'] ) ) : '';
		if ( '' === $accent_2 ) {
			$accent_2 = $accent_1;
		}
		if ( '' === $background || '' === $text_color || '' === $accent_1 || '' === $accent_2 ) {
			$session['status']     = 'failed';
			$session['error_code'] = 'insufficient_signal';
			thespl_color_probe_set_session( $session_id, $session );
			wp_send_json_error( array( 'message' => 'insufficient_signal' ), 422 );
		}
		$session['status']     = 'completed';
		$session['error_code'] = '';
		$session['result']     = array(
			'background' => $background,
			'text_color' => $text_color,
			'accent_1'   => $accent_1,
			'accent_2'   => $accent_2,
		);
		thespl_color_probe_set_session( $session_id, $session );
		wp_send_json_success(
			array(
				'status' => 'completed',
			)
		);
	}
);
