<?php
/**
 * SSP REST API endpoints
 *
 * Handles all SSP-related REST endpoints for activation, sync,
 * testing, resync (inbound from SSP portal), and status.
 *
 * @since 1.57.0
 */

namespace SweetCode\Pixel_Manager\Admin;

use SweetCode\Pixel_Manager\Helpers;
use SweetCode\Pixel_Manager\Logger;
use SweetCode\Pixel_Manager\Options;
use SweetCode\Pixel_Manager\SSP_Sync;

defined('ABSPATH') || exit; // Exit if accessed directly

class SSP_REST {

	protected static $rest_namespace = 'pmw/v1';
	protected static $log_source     = 'pmw';

	private static $instance;

	public static function get_instance() {
		if (is_null(self::$instance)) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	public function __construct() {
		add_action('rest_api_init', [ $this, 'register_routes' ]);
	}

	public function can_current_user_edit_options() {
		return Environment::can_current_user_edit_options();
	}

	/**
	 * Register all SSP REST routes.
	 *
	 * @since 1.57.0
	 */
	public function register_routes() {

		// Activate SSP — validate token, fetch domain info, register callback, push config
		register_rest_route(self::$rest_namespace, '/ssp/activate', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'handle_activate' ],
			'permission_callback' => [ $this, 'can_current_user_edit_options' ],
		]);

		// Manual sync — push config to SSP
		register_rest_route(self::$rest_namespace, '/ssp/sync', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'handle_sync' ],
			'permission_callback' => [ $this, 'can_current_user_edit_options' ],
		]);

		// Test connection — check if proxy is reachable
		register_rest_route(self::$rest_namespace, '/ssp/test', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'handle_test' ],
			'permission_callback' => [ $this, 'can_current_user_edit_options' ],
		]);

		// Get current SSP status
		register_rest_route(self::$rest_namespace, '/ssp/status', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'handle_status' ],
			'permission_callback' => [ $this, 'can_current_user_edit_options' ],
		]);

		// Deactivate SSP (deprecated alias for disconnect)
		register_rest_route(self::$rest_namespace, '/ssp/deactivate', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'handle_disconnect' ],
			'permission_callback' => [ $this, 'can_current_user_edit_options' ],
		]);

		// Enable SSP (soft toggle, preserves connection state)
		register_rest_route(self::$rest_namespace, '/ssp/enable', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'handle_enable' ],
			'permission_callback' => [ $this, 'can_current_user_edit_options' ],
		]);

		// Disable SSP (soft toggle, preserves connection state)
		register_rest_route(self::$rest_namespace, '/ssp/disable', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'handle_disable' ],
			'permission_callback' => [ $this, 'can_current_user_edit_options' ],
		]);

		// Disconnect SSP — fully wipe all connection data
		register_rest_route(self::$rest_namespace, '/ssp/disconnect', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'handle_disconnect' ],
			'permission_callback' => [ $this, 'can_current_user_edit_options' ],
		]);

		// Save SSP setting (proxy_failure_behavior)
		register_rest_route(self::$rest_namespace, '/ssp/save-setting', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'handle_save_setting' ],
			'permission_callback' => [ $this, 'can_current_user_edit_options' ],
		]);

		// Inbound resync trigger — called by SSP portal
		register_rest_route(self::$rest_namespace, '/ssp/resync', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'handle_resync' ],
			'permission_callback' => '__return_true', // Public — auth via token header
		]);
	}

	/**
	 * Handle SSP activation.
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response|\WP_Error
	 * @since 1.57.0
	 */
	public function handle_activate( $request ) {

		$data       = Helpers::generic_sanitization($request->get_json_params());
		$sync_token = isset($data['sync_token']) ? trim($data['sync_token']) : '';

		if (empty($sync_token)) {
			return new \WP_Error(
				'ssp_missing_token',
				'Sync token is required.',
				[ 'status' => 400 ]
			);
		}

		// Validate token format: hex string, 32-64 chars
		if (!preg_match('/^[a-f0-9]{32,64}$/i', $sync_token)) {
			return new \WP_Error(
				'ssp_invalid_token',
				'Invalid sync token format. Expected a hex string.',
				[ 'status' => 400 ]
			);
		}

		$result = SSP_Sync::activate($sync_token);

		if (is_wp_error($result)) {
			return $result;
		}

		return new \WP_REST_Response([
			'success'     => true,
			'message'     => 'SSP activated successfully.',
			'domain_info' => $result,
			'status'      => SSP_Sync::get_status(),
		], 200);
	}

	/**
	 * Handle manual config sync.
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response|\WP_Error
	 * @since 1.57.0
	 */
	public function handle_sync( $request ) {

		if (!Options::is_ssp_active() && empty(Options::get_ssp_sync_token())) {
			return new \WP_Error(
				'ssp_not_configured',
				'SSP is not configured. Please activate first.',
				[ 'status' => 400 ]
			);
		}

		$result = SSP_Sync::push_config_to_ssp();

		if (is_wp_error($result)) {
			return $result;
		}

		return new \WP_REST_Response([
			'success' => true,
			'message' => 'Configuration synced successfully.',
			'status'  => SSP_Sync::get_status(),
		], 200);
	}

	/**
	 * Handle connection test.
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response|\WP_Error
	 * @since 1.57.0
	 */
	public function handle_test( $request ) {

		$result = SSP_Sync::test_connection();

		if (is_wp_error($result)) {
			return new \WP_REST_Response([
				'success' => false,
				'message' => 'Connection test failed: ' . $result->get_error_message(),
			], 200);
		}

		return new \WP_REST_Response([
			'success' => $result['reachable'],
			'message' => $result['reachable']
				? 'Proxy is reachable at ' . $result['hostname']
				: 'Proxy is not reachable.',
			'details' => $result,
		], 200);
	}

	/**
	 * Handle status request.
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response
	 * @since 1.57.0
	 */
	public function handle_status( $request ) {

		return new \WP_REST_Response([
			'success' => true,
			'status'  => SSP_Sync::get_status(),
		], 200);
	}

	/**
	 * Handle SSP deactivation (deprecated alias for disconnect).
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response
	 * @deprecated 1.57.0 Use /ssp/disconnect instead.
	 * @since 1.57.0
	 */
	public function handle_deactivate( $request ) {
		return $this->handle_disconnect( $request );
	}

	/**
	 * Handle SSP enable (soft toggle).
	 *
	 * Re-enables SSP using the preserved connection state.
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response|\WP_Error
	 * @since 1.57.0
	 */
	public function handle_enable( $request ) {

		$result = SSP_Sync::enable();

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return new \WP_REST_Response([
			'success' => true,
			'message' => 'SSP enabled.',
			'status'  => SSP_Sync::get_status(),
		], 200);
	}

	/**
	 * Handle SSP disable (soft toggle).
	 *
	 * Disables SSP but preserves all connection state (token, hostname, etc.).
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response
	 * @since 1.57.0
	 */
	public function handle_disable( $request ) {

		SSP_Sync::disable();

		return new \WP_REST_Response([
			'success' => true,
			'message' => 'SSP disabled. Connection state preserved.',
			'status'  => SSP_Sync::get_status(),
		], 200);
	}

	/**
	 * Handle SSP disconnect.
	 *
	 * Fully removes the sync token and all connection data.
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response
	 * @since 1.57.0
	 */
	public function handle_disconnect( $request ) {

		SSP_Sync::disconnect();

		return new \WP_REST_Response([
			'success' => true,
			'message' => 'SSP disconnected. All connection data has been removed.',
			'status'  => SSP_Sync::get_status(),
		], 200);
	}

	/**
	 * Handle saving an SSP setting.
	 *
	 * Currently supports: proxy_failure_behavior
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response|\WP_Error
	 * @since 1.57.0
	 */
	public function handle_save_setting( $request ) {

		$data = Helpers::generic_sanitization( $request->get_json_params() );
		$key  = isset($data['key']) ? $data['key'] : '';

		if ( 'proxy_failure_behavior' !== $key ) {
			return new \WP_Error(
				'ssp_invalid_setting',
				'Unknown setting key.',
				[ 'status' => 400 ]
			);
		}

		$value           = isset($data['value']) ? $data['value'] : '';
		$allowed_values  = [ 'fallback_to_wc', 'drop_events' ];

		if ( ! in_array( $value, $allowed_values, true ) ) {
			return new \WP_Error(
				'ssp_invalid_value',
				'Invalid value. Allowed: ' . implode( ', ', $allowed_values ),
				[ 'status' => 400 ]
			);
		}

		$options = get_option( PMW_DB_OPTIONS_NAME );
		$options['ssp']['proxy_failure_behavior'] = $value;
		update_option( PMW_DB_OPTIONS_NAME, $options );
		Options::invalidate_cache();

		return new \WP_REST_Response([
			'success' => true,
			'message' => 'Setting saved.',
			'value'   => $value,
		], 200);
	}

	/**
	 * Handle inbound resync trigger from the SSP portal.
	 *
	 * Authenticates via X-SSP-Resync-Token header, then pushes
	 * current config to the SSP.
	 *
	 * @param \WP_REST_Request $request
	 *
	 * @return \WP_REST_Response|\WP_Error
	 * @since 1.57.0
	 */
	public function handle_resync( $request ) {

		// Extract token from header
		$token = $request->get_header('X-SSP-Resync-Token');

		if (empty($token)) {
			Logger::warning('SSP: Resync request received without token.');
			return new \WP_Error(
				'ssp_unauthorized',
				'Missing resync token.',
				[ 'status' => 401 ]
			);
		}

		$stored_token = Options::get_ssp_resync_callback_token();

		if (empty($stored_token)) {
			Logger::warning('SSP: Resync request received but no callback token is stored.');
			return new \WP_Error(
				'ssp_unauthorized',
				'SSP is not configured.',
				[ 'status' => 401 ]
			);
		}

		// Constant-time comparison to prevent timing attacks
		if (!hash_equals($stored_token, $token)) {
			Logger::warning('SSP: Resync request with invalid token.');
			return new \WP_Error(
				'ssp_unauthorized',
				'Invalid resync token.',
				[ 'status' => 401 ]
			);
		}

		Logger::info('SSP: Resync triggered by SSP portal.');

		$result = SSP_Sync::push_config_to_ssp();

		if (is_wp_error($result)) {
			return new \WP_REST_Response([
				'success' => false,
				'message' => 'Config push failed: ' . $result->get_error_message(),
			], 500);
		}

		return new \WP_REST_Response([
			'success' => true,
			'message' => 'Config synced successfully.',
		], 200);
	}
}
