<?php
// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Normalize redirect source to path-only format.
 * Examples:
 * https://example.com/contact-us/ -> /contact-us/
 * contact-us/                    -> /contact-us/
 * /contact-us/                   -> /contact-us/
 *
 * @param string $url_or_path Source URL or path.
 * @return string
 */
function myaio_sac_normalize_redirect_source( $url_or_path ) {
	$value = trim( (string) $url_or_path );

	if ( '' === $value ) {
		return '';
	}

	if ( preg_match( '~^https?://~i', $value ) ) {
		$parts = wp_parse_url( $value );
		$path  = isset( $parts['path'] ) ? $parts['path'] : '/';
		$query = isset( $parts['query'] ) ? $parts['query'] : '';

		if ( '' === $path || null === $path ) {
			$path = '/';
		}

		if ( '/' !== $path[0] ) {
			$path = '/' . $path;
		}

		return '' !== $query ? $path . '?' . $query : $path;
	}

	if ( '/' !== $value[0] ) {
		$value = '/' . $value;
	}

	return $value;
}

/**
 * Normalize destination to full URL.
 * If path is given, convert to full site URL.
 *
 * @param string $url_or_path Destination URL or path.
 * @return string
 */
function myaio_sac_normalize_redirect_destination( $url_or_path ) {
	$value = trim( (string) $url_or_path );

	if ( '' === $value ) {
		return '';
	}

	if ( preg_match( '~^https?://~i', $value ) ) {
		return esc_url_raw( $value );
	}

	if ( '/' !== $value[0] ) {
		$value = '/' . $value;
	}

	return esc_url_raw( home_url( $value ) );
}

/**
 * Send strong no-cache headers for redirects API responses.
 *
 * @return void
 */
function myaio_sac_send_no_cache_headers() {
	if ( ! headers_sent() ) {
		nocache_headers();
		header( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' );
		header( 'Pragma: no-cache' );
		header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' );
		header( 'Vary: Accept-Encoding' );
	}
}

/**
 * Get redirects table name.
 *
 * @return string
 */
function myaio_sac_get_redirects_table() {
	global $wpdb;

	return $wpdb->prefix . 'myaio_redirects';
}

/**
 * Get escaped redirects table name for SQL use.
 *
 * @return string
 */
function myaio_sac_get_redirects_table_sql() {
	return esc_sql( myaio_sac_get_redirects_table() );
}

/**
 * Get cache group for redirects.
 *
 * @return string
 */
function myaio_sac_get_redirects_cache_group() {
	return 'myaio_redirects';
}

/**
 * Build single redirect cache key.
 *
 * @param int $id Redirect ID.
 * @return string
 */
function myaio_sac_get_redirect_cache_key( $id ) {
	return 'redirect_' . (int) $id;
}

/**
 * Cache key for all redirects.
 *
 * @return string
 */
function myaio_sac_get_all_redirects_cache_key() {
	return 'redirects_all';
}

/**
 * Purge cache after redirects data changes.
 * Includes LiteSpeed hooks plus a generic object-cache flush fallback.
 *
 * @return void
 */
function myaio_sac_purge_redirect_cache() {
	if ( class_exists( 'LiteSpeed\\Purge' ) && method_exists( 'LiteSpeed\\Purge', 'purge_all' ) ) {
		\LiteSpeed\Purge::purge_all();
	}
	// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
	do_action( 'litespeed_purge_all' );

	wp_cache_delete( myaio_sac_get_all_redirects_cache_key(), myaio_sac_get_redirects_cache_group() );

	if ( function_exists( 'wp_cache_flush_group' ) ) {
		wp_cache_flush_group( myaio_sac_get_redirects_cache_group() );
	} elseif ( function_exists( 'wp_cache_flush' ) ) {
		wp_cache_flush();
	}
}

/**
 * Prepare redirect response row.
 *
 * @param array $row Database row.
 * @return array|null
 */
function myaio_sac_prepare_redirect_response( $row ) {
	if ( empty( $row ) || ! is_array( $row ) ) {
		return null;
	}

	return [
		'id'          => isset( $row['id'] ) ? (int) $row['id'] : 0,
		'from_url'    => (string) ( $row['from_url'] ?? '' ),
		'to_url'      => (string) ( $row['to_url'] ?? '' ),
		'http_code'   => isset( $row['http_code'] ) ? (int) $row['http_code'] : 301,
		'is_active'   => isset( $row['is_active'] ) ? (int) $row['is_active'] : 1,
		'is_regex'    => isset( $row['is_regex'] ) ? (int) $row['is_regex'] : 0,
		'match_query' => isset( $row['match_query'] ) ? (int) $row['match_query'] : 0,
		'regex_flags' => (string) ( $row['regex_flags'] ?? '' ),
		'source'      => (string) ( $row['source'] ?? '' ),
		'created_at'  => (string) ( $row['created_at'] ?? '' ),
		'updated_at'  => (string) ( $row['updated_at'] ?? '' ),
	];
}

/**
 * Get a single redirect row by ID with cache.
 *
 * @param int $id Redirect ID.
 * @return array|null
 */
function myaio_sac_get_redirect_row_by_id( $id ) {
	global $wpdb;

	$id        = (int) $id;
	$cache_key = myaio_sac_get_redirect_cache_key( $id );
	$cache     = wp_cache_get( $cache_key, myaio_sac_get_redirects_cache_group() );

	if ( false !== $cache ) {
		return $cache;
	}

	$table = esc_sql( myaio_sac_get_redirects_table() );

// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$prepared_query = $wpdb->prepare(
  // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
	"SELECT * FROM `{$table}` WHERE id = %d",
	$id
);

// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
$row = $wpdb->get_row( $prepared_query, ARRAY_A );

	wp_cache_set( $cache_key, $row, myaio_sac_get_redirects_cache_group(), 300 );

	return $row;
}

/**
 * Get all redirect rows with cache.
 *
 * @return array
 */
function myaio_sac_get_all_redirect_rows() {
	global $wpdb;

	$cache_key = myaio_sac_get_all_redirects_cache_key();
	$cache     = wp_cache_get( $cache_key, myaio_sac_get_redirects_cache_group() );

	if ( false !== $cache ) {
		return $cache;
	}

	$table = myaio_sac_get_redirects_table_sql();
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
	$rows = $wpdb->get_results(
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		"SELECT * FROM `{$table}` ORDER BY id DESC",
		ARRAY_A
	);

	if ( ! is_array( $rows ) ) {
		$rows = [];
	}

	wp_cache_set( $cache_key, $rows, myaio_sac_get_redirects_cache_group(), 300 );

	return $rows;
}

/**
 * Check if redirect already exists.
 *
 * @param string $from_url Source URL.
 * @param string $to_url Destination URL.
 * @param int    $http_code HTTP code.
 * @param int    $is_regex Is regex.
 * @param int    $match_query Match query.
 * @param int    $exclude_id Optional ID to exclude.
 * @return bool
 */
function myaio_sac_redirect_exists( $from_url, $to_url, $http_code, $is_regex, $match_query, $exclude_id = 0 ) {
	global $wpdb;

	$table = myaio_sac_get_redirects_table_sql();

	$query = "SELECT id FROM `{$table}` WHERE from_url = %s AND to_url = %s AND http_code = %d AND is_regex = %d AND match_query = %d AND is_active = 1";
	$args  = [
		$from_url,
		$to_url,
		$http_code,
		$is_regex,
		$match_query,
	];

	if ( $exclude_id > 0 ) {
		$query .= ' AND id != %d';
		$args[] = (int) $exclude_id;
	}

	$query .= ' LIMIT 1';

// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$prepared_query = $wpdb->prepare( $query, $args );


// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
$existing_id = $wpdb->get_var( $prepared_query );

	return ! empty( $existing_id );
}

/**
 * Prevent caching for MYAIO redirects REST API routes.
 */
add_filter(
	'rest_pre_serve_request',
	function ( $served, $result, $request, $server ) {
		$route = $request->get_route();

		if ( 0 === strpos( $route, '/myaio/v1/seo-redirects' ) ) {
			myaio_sac_send_no_cache_headers();
		}

		return $served;
	},
	10,
	4
);

// Register API routes.
add_action(
	'rest_api_init',
	function () {
		register_rest_route(
			'myaio/v1',
			'/seo-redirects',
			[
				'methods'             => 'GET',
				'callback'            => 'myaio_sac_get_all_redirects',
				'permission_callback' => 'myaio_sac_rest_permission_check',
			]
		);

		register_rest_route(
			'myaio/v1',
			'/seo-redirects',
			[
				'methods'             => 'POST',
				'callback'            => 'myaio_sac_create_redirect',
				'permission_callback' => 'myaio_sac_rest_permission_check',
			]
		);

		register_rest_route(
			'myaio/v1',
			'/seo-redirects/(?P<id>\d+)',
			[
				'methods'             => 'GET',
				'callback'            => 'myaio_sac_get_single_redirect',
				'permission_callback' => 'myaio_sac_rest_permission_check',
			]
		);

		register_rest_route(
			'myaio/v1',
			'/seo-redirects/(?P<id>\d+)',
			[
				'methods'             => 'PUT, PATCH',
				'callback'            => 'myaio_sac_update_redirect',
				'permission_callback' => 'myaio_sac_rest_permission_check',
			]
		);

		register_rest_route(
			'myaio/v1',
			'/seo-redirects/(?P<id>\d+)/status',
			[
				'methods'             => 'PUT, PATCH',
				'callback'            => 'myaio_sac_update_redirect_status',
				'permission_callback' => 'myaio_sac_rest_permission_check',
			]
		);

		register_rest_route(
			'myaio/v1',
			'/seo-redirects/(?P<id>\d+)',
			[
				'methods'             => 'DELETE',
				'callback'            => 'myaio_sac_delete_redirect',
				'permission_callback' => 'myaio_sac_rest_permission_check',
			]
		);
	}
);

/**
 * Create redirect.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response|WP_HTTP_Response|WP_Error
 */
function myaio_sac_create_redirect( WP_REST_Request $request ) {
	global $wpdb;

	$table = myaio_sac_get_redirects_table();

	$from_url    = myaio_sac_normalize_redirect_source( $request->get_param( 'from_url' ) );
	$raw_to_url  = trim( (string) $request->get_param( 'to_url' ) );
	$http_code   = (int) $request->get_param( 'http_code' );
	$is_active   = $request->has_param( 'is_active' ) ? (int) (bool) $request->get_param( 'is_active' ) : 1;
	$is_regex    = $request->has_param( 'is_regex' ) ? (int) (bool) $request->get_param( 'is_regex' ) : 0;
	$match_query = $request->has_param( 'match_query' ) ? (int) (bool) $request->get_param( 'match_query' ) : 0;
	$regex_flags = sanitize_text_field( (string) $request->get_param( 'regex_flags' ) );
	$source      = sanitize_text_field( (string) $request->get_param( 'source' ) );

	if ( '' === $from_url ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Missing from_url' ], 400 );
	}

	if ( ! in_array( $http_code, [ 301, 302, 307, 308, 410, 451 ], true ) ) {
		$http_code = 301;
	}

	$to_url = '';
	if ( in_array( $http_code, [ 410, 451 ], true ) ) {
		$to_url = '';
	} else {
		$to_url = myaio_sac_normalize_redirect_destination( $raw_to_url );
		if ( '' === $to_url ) {
			myaio_sac_send_no_cache_headers();
			return new WP_REST_Response( [ 'error' => 'Missing to_url' ], 400 );
		}
	}

	if ( '' === $source ) {
		$source = 'api';
	}

	if ( $is_active === 1 && myaio_sac_redirect_exists( $from_url, $to_url, $http_code, $is_regex, $match_query ) ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Redirect already exists' ], 409 );
	}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
	$inserted = $wpdb->insert(
		$table,
		[
			'from_url'    => $from_url,
			'to_url'      => $to_url,
			'http_code'   => $http_code,
			'is_active'   => $is_active,
			'is_regex'    => $is_regex,
			'match_query' => $match_query,
			'regex_flags' => $regex_flags,
			'source'      => $source,
			'created_at'  => current_time( 'mysql' ),
			'updated_at'  => current_time( 'mysql' ),
		],
		[ '%s', '%s', '%d', '%d', '%d', '%d', '%s', '%s', '%s', '%s' ]
	);

	if ( false === $inserted ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Failed to create redirect' ], 500 );
	}

	$id  = (int) $wpdb->insert_id;
	$row = myaio_sac_get_redirect_row_by_id( $id );

	myaio_sac_purge_redirect_cache();
	myaio_sac_send_no_cache_headers();

	return rest_ensure_response(
		[
			'success' => true,
			'message' => 'Redirect created successfully',
			'data'    => myaio_sac_prepare_redirect_response( $row ),
		]
	);
}

/**
 * Update full redirect.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response|WP_HTTP_Response|WP_Error
 */
function myaio_sac_update_redirect( WP_REST_Request $request ) {
	global $wpdb;

	$table    = myaio_sac_get_redirects_table();
	$id       = (int) $request['id'];
	$existing = myaio_sac_get_redirect_row_by_id( $id );

	if ( empty( $existing ) ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Redirect not found' ], 404 );
	}

	$from_url    = $request->has_param( 'from_url' ) ? myaio_sac_normalize_redirect_source( $request->get_param( 'from_url' ) ) : $existing['from_url'];
	$http_code   = $request->has_param( 'http_code' ) ? (int) $request->get_param( 'http_code' ) : (int) $existing['http_code'];
	$is_active   = $request->has_param( 'is_active' ) ? (int) (bool) $request->get_param( 'is_active' ) : (int) $existing['is_active'];
	$is_regex    = $request->has_param( 'is_regex' ) ? (int) (bool) $request->get_param( 'is_regex' ) : (int) $existing['is_regex'];
	$match_query = $request->has_param( 'match_query' ) ? (int) (bool) $request->get_param( 'match_query' ) : (int) $existing['match_query'];
	$regex_flags = $request->has_param( 'regex_flags' ) ? sanitize_text_field( (string) $request->get_param( 'regex_flags' ) ) : (string) $existing['regex_flags'];
	$source      = $request->has_param( 'source' ) ? sanitize_text_field( (string) $request->get_param( 'source' ) ) : (string) $existing['source'];

	if ( '' === $from_url ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Missing from_url' ], 400 );
	}

	if ( ! in_array( $http_code, [ 301, 302, 307, 308, 410, 451 ], true ) ) {
		$http_code = 301;
	}

	if ( in_array( $http_code, [ 410, 451 ], true ) ) {
		$to_url = '';
	} else {
		$to_url = $request->has_param( 'to_url' )
			? myaio_sac_normalize_redirect_destination( $request->get_param( 'to_url' ) )
			: $existing['to_url'];

		if ( '' === $to_url ) {
			myaio_sac_send_no_cache_headers();
			return new WP_REST_Response( [ 'error' => 'Missing to_url' ], 400 );
		}
	}

	if ( '' === $source ) {
		$source = 'api';
	}

	if ( $is_active === 1 && myaio_sac_redirect_exists( $from_url, $to_url, $http_code, $is_regex, $match_query, $id ) ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Another redirect with same values already exists' ], 409 );
	}
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
	$updated = $wpdb->update(
		$table,
		[
			'from_url'    => $from_url,
			'to_url'      => $to_url,
			'http_code'   => $http_code,
			'is_active'   => $is_active,
			'is_regex'    => $is_regex,
			'match_query' => $match_query,
			'regex_flags' => $regex_flags,
			'source'      => $source,
			'updated_at'  => current_time( 'mysql' ),
		],
		[ 'id' => $id ],
		[ '%s', '%s', '%d', '%d', '%d', '%d', '%s', '%s', '%s' ],
		[ '%d' ]
	);

	if ( false === $updated ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Failed to update redirect' ], 500 );
	}

	wp_cache_delete( myaio_sac_get_redirect_cache_key( $id ), myaio_sac_get_redirects_cache_group() );

	$row = myaio_sac_get_redirect_row_by_id( $id );

	myaio_sac_purge_redirect_cache();
	myaio_sac_send_no_cache_headers();

	return rest_ensure_response(
		[
			'success' => true,
			'message' => 'Redirect updated successfully',
			'data'    => myaio_sac_prepare_redirect_response( $row ),
		]
	);
}

/**
 * Update only redirect status.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response|WP_HTTP_Response|WP_Error
 */
function myaio_sac_update_redirect_status( WP_REST_Request $request ) {
	global $wpdb;

	$table     = myaio_sac_get_redirects_table();
	$id        = (int) $request['id'];
	$is_active = (int) (bool) $request->get_param( 'is_active' );
	$existing  = myaio_sac_get_redirect_row_by_id( $id );

	if ( empty( $existing ) ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Redirect not found' ], 404 );
	}
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
	$updated = $wpdb->update(
		$table,
		[
			'is_active'  => $is_active,
			'updated_at' => current_time( 'mysql' ),
		],
		[ 'id' => $id ],
		[ '%d', '%s' ],
		[ '%d' ]
	);

	if ( false === $updated ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Failed to update redirect status' ], 500 );
	}

	wp_cache_delete( myaio_sac_get_redirect_cache_key( $id ), myaio_sac_get_redirects_cache_group() );

	$row = myaio_sac_get_redirect_row_by_id( $id );

	myaio_sac_purge_redirect_cache();
	myaio_sac_send_no_cache_headers();

	return rest_ensure_response(
		[
			'success' => true,
			'message' => 'Redirect status updated successfully',
			'data'    => myaio_sac_prepare_redirect_response( $row ),
		]
	);
}

/**
 * Delete redirect.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response|WP_HTTP_Response|WP_Error
 */
function myaio_sac_delete_redirect( WP_REST_Request $request ) {
	global $wpdb;

	$table    = myaio_sac_get_redirects_table();
	$id       = (int) $request['id'];
	$existing = myaio_sac_get_redirect_row_by_id( $id );

	if ( empty( $existing ) ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Redirect not found' ], 404 );
	}
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
	$deleted = $wpdb->delete(
		$table,
		[ 'id' => $id ],
		[ '%d' ]
	);

	if ( false === $deleted ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Failed to delete redirect' ], 500 );
	}

	wp_cache_delete( myaio_sac_get_redirect_cache_key( $id ), myaio_sac_get_redirects_cache_group() );

	myaio_sac_purge_redirect_cache();
	myaio_sac_send_no_cache_headers();

	return rest_ensure_response(
		[
			'success' => true,
			'message' => 'Redirect deleted successfully',
			'data'    => myaio_sac_prepare_redirect_response( $existing ),
		]
	);
}

/**
 * Get single redirect.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response|WP_HTTP_Response|WP_Error
 */
function myaio_sac_get_single_redirect( WP_REST_Request $request ) {
	$id  = (int) $request['id'];
	$row = myaio_sac_get_redirect_row_by_id( $id );

	if ( empty( $row ) ) {
		myaio_sac_send_no_cache_headers();
		return new WP_REST_Response( [ 'error' => 'Redirect not found' ], 404 );
	}

	myaio_sac_send_no_cache_headers();

	return rest_ensure_response(
		[
			'success' => true,
			'data'    => myaio_sac_prepare_redirect_response( $row ),
		]
	);
}

/**
 * Get all redirects.
 *
 * @param WP_REST_Request $request Request object.
 * @return WP_REST_Response|WP_HTTP_Response|WP_Error
 */
function myaio_sac_get_all_redirects( WP_REST_Request $request ) {
	$rows = myaio_sac_get_all_redirect_rows();
	$data = [];

	if ( ! empty( $rows ) ) {
		foreach ( $rows as $row ) {
			$data[] = myaio_sac_prepare_redirect_response( $row );
		}
	}

	myaio_sac_send_no_cache_headers();

	return rest_ensure_response(
		[
			'success' => true,
			'message' => 'Redirects fetched successfully',
			'data'    => $data,
			'total'   => count( $data ),
		]
	);
}