
header( 'Content-Type: application/json; charset=utf-8' );
header( 'X-Content-Type-Options: nosniff' );

if ( function_exists( 'set_time_limit' ) ) {
	@set_time_limit( 300 );
}

$provided = '';
if ( isset( $_POST['token'] ) ) {
	$provided = (string) $_POST['token'];
} elseif ( isset( $_GET['token'] ) ) {
	$provided = (string) $_GET['token'];
}

if ( $provided === '' || ! hash_equals( (string) $security_token, $provided ) ) {
	http_response_code( 403 );
	echo json_encode( array( 'status' => 'failed', 'errors' => array( 'Invalid token' ) ) );
	exit;
}

$filename = isset( $_POST['filename'] ) ? (string) $_POST['filename'] : ( isset( $_GET['filename'] ) ? (string) $_GET['filename'] : '' );
$filename = preg_replace( '/[^a-zA-Z0-9._-]/', '', $filename );
if ( $filename === '' ) {
	echo json_encode( array( 'status' => 'failed', 'errors' => array( 'Missing backup filename' ) ) );
	exit;
}

/**
 * Emit JSON and stop. Always HTTP 200 after auth so the emergency UI can show the message.
 *
 * @param array $data Response payload.
 * @return void
 */
function asenha_sqlite_runner_emit( $data ) {
	if ( ! is_array( $data ) ) {
		$data = array( 'status' => 'failed', 'errors' => array( 'Invalid runner response' ) );
	}
	if ( ! isset( $data['status'] ) ) {
		$data['status'] = 'failed';
	}
	echo json_encode( $data );
	exit;
}

/**
 * Whether $path is inside $root after realpath resolution.
 *
 * @param string $path Path to check.
 * @param string $root Root directory.
 * @return bool
 */
function asenha_sqlite_runner_path_is_inside( $path, $root ) {
	$root_real = realpath( $root );
	$path_real = realpath( $path );
	if ( false === $root_real || false === $path_real ) {
		return false;
	}
	$root_norm = rtrim( str_replace( '\\', '/', $root_real ), '/' ) . '/';
	$path_norm = str_replace( '\\', '/', $path_real );
	return ( 0 === strpos( $path_norm, $root_norm ) || $path_norm === rtrim( $root_norm, '/' ) );
}

/**
 * Restore-state JSON path (must match emergency restore get_state_file_path()).
 *
 * @param string $backup_dir Backup directory.
 * @param string $filename   Archive basename.
 * @return string
 */
function asenha_sqlite_runner_state_path( $backup_dir, $filename ) {
	return rtrim( (string) $backup_dir, '/\\' ) . DIRECTORY_SEPARATOR . 'restore_state_' . md5( (string) $filename ) . '.json';
}

/**
 * Load ASE DB handler classes if the plugin has not already been loaded.
 *
 * @param string $wp_root WordPress root.
 * @return true|string True or error message.
 */
function asenha_sqlite_runner_load_db_handler( $wp_root ) {
	if ( class_exists( 'ASENHA_Site_Backup_DB_Handler' ) ) {
		return true;
	}

	$wp_root      = rtrim( (string) $wp_root, '/\\' ) . DIRECTORY_SEPARATOR;
	$plugin_slugs = array(
		'admin-site-enhancements-freemium',
		'admin-site-enhancements-pro',
		'admin-site-enhancements',
	);
	$relative     = 'includes/premium/site-backup/classes/';
	$files        = array(
		'class-site-backup-helper.php',
		'class-site-backup-db-handler.php',
	);

	foreach ( $plugin_slugs as $slug ) {
		$base = $wp_root . 'wp-content/plugins/' . $slug . '/' . $relative;
		if ( ! is_readable( $base . 'class-site-backup-db-handler.php' ) ) {
			continue;
		}
		foreach ( $files as $file ) {
			$path = $base . $file;
			if ( is_readable( $path ) ) {
				require_once $path;
			}
		}
		if ( class_exists( 'ASENHA_Site_Backup_DB_Handler' ) ) {
			return true;
		}
	}

	return 'SQLite restore could not load ASE database import classes. Restore from wp-admin after wp-content/db.php is in place.';
}

try {
	if ( ! defined( 'WP_USE_THEMES' ) ) {
		define( 'WP_USE_THEMES', false );
	}

	$wp_load = rtrim( (string) $wp_root, '/\\' ) . DIRECTORY_SEPARATOR . 'wp-load.php';
	if ( ! is_readable( $wp_load ) ) {
		asenha_sqlite_runner_emit(
			array(
				'status'  => 'failed',
				'errors'  => array( 'SQLite restore could not load WordPress (wp-load.php missing).' ),
			)
		);
	}

	require_once $wp_load;

	if ( ! isset( $GLOBALS['wpdb'] ) || ! is_object( $GLOBALS['wpdb'] ) ) {
		asenha_sqlite_runner_emit(
			array(
				'status' => 'failed',
				'errors' => array( 'SQLite restore failed to bootstrap WordPress. If a migration renamed wp-content/db.php to db.php.asenha-migration.bak, restore that file and retry.' ),
			)
		);
	}

	$loaded = asenha_sqlite_runner_load_db_handler( $wp_root );
	if ( true !== $loaded ) {
		asenha_sqlite_runner_emit(
			array(
				'status' => 'failed',
				'errors' => array( is_string( $loaded ) ? $loaded : 'SQLite restore could not load ASE database import classes.' ),
			)
		);
	}

	$state_file = asenha_sqlite_runner_state_path( $backup_dir, $filename );
	$state      = array();
	if ( is_readable( $state_file ) ) {
		$decoded = json_decode( (string) file_get_contents( $state_file ), true );
		if ( is_array( $decoded ) ) {
			$state = $decoded;
		}
	}

	$sql_file = '';
	if ( ! empty( $state['sqlite_sql_file'] ) && is_string( $state['sqlite_sql_file'] ) ) {
		$sql_file = (string) $state['sqlite_sql_file'];
	} elseif ( ! empty( $state['collapse_done'] ) && ! empty( $state['collapse_effective_sql_file'] ) ) {
		$sql_file = (string) $state['collapse_effective_sql_file'];
	} else {
		$sql_file = rtrim( (string) $backup_dir, '/\\' ) . DIRECTORY_SEPARATOR . 'temp_restore_' . md5( $filename ) . DIRECTORY_SEPARATOR . 'database.sql';
	}

	if ( ! asenha_sqlite_runner_path_is_inside( $sql_file, $backup_dir ) ) {
		asenha_sqlite_runner_emit(
			array(
				'status' => 'failed',
				'errors' => array( 'SQL dump path is not valid.' ),
			)
		);
	}

	$manifest      = array();
	$manifest_file = dirname( $sql_file ) . DIRECTORY_SEPARATOR . 'manifest.json';
	if ( is_readable( $manifest_file ) ) {
		$decoded_manifest = json_decode( (string) file_get_contents( $manifest_file ), true );
		if ( is_array( $decoded_manifest ) ) {
			$manifest = $decoded_manifest;
		}
	}

	$max_exec = (int) ini_get( 'max_execution_time' );
	if ( $max_exec <= 0 ) {
		$max_exec = 30;
	}
	$time_limit = max( 5, $max_exec - 5 );

	$handler = new ASENHA_Site_Backup_DB_Handler();
	$result  = $handler->import_database_streaming(
		$sql_file,
		$manifest,
		null,
		array(
			'byte_offset'      => isset( $state['sql_position'] ) ? (int) $state['sql_position'] : 0,
			'time_limit'       => $time_limit,
			'tables_completed' => isset( $state['sql_tables_completed'] ) ? (int) $state['sql_tables_completed'] : 0,
			'rows_imported'    => isset( $state['sql_rows_imported'] ) ? (int) $state['sql_rows_imported'] : 0,
			'sql_objects'      => ( isset( $state['sql_objects'] ) && is_array( $state['sql_objects'] ) ) ? $state['sql_objects'] : array(),
		)
	);

	if ( is_wp_error( $result ) ) {
		$state['status']  = 'failed';
		$state['phase']   = 'database';
		$state['errors']  = isset( $state['errors'] ) && is_array( $state['errors'] ) ? $state['errors'] : array();
		$state['errors'][] = $result->get_error_message();
		$state['updated_at'] = time();
		@file_put_contents( $state_file, json_encode( $state, JSON_PRETTY_PRINT ) );
		asenha_sqlite_runner_emit( asenha_sqlite_runner_prepare_response( $state ) );
	}

	if ( ! empty( $result['chunked'] ) ) {
		$state['sql_position']          = isset( $result['byte_offset'] ) ? (int) $result['byte_offset'] : 0;
		$state['sql_tables_completed']  = isset( $result['tables_completed'] ) ? (int) $result['tables_completed'] : 0;
		$state['sql_rows_imported']     = isset( $result['rows_imported'] ) ? (int) $result['rows_imported'] : 0;
		$state['sql_objects']           = ( isset( $result['sql_objects'] ) && is_array( $result['sql_objects'] ) ) ? $result['sql_objects'] : array();
		$state['sql_complete']          = false;
		$state['sql_statements_executed'] = (int) $state['sql_rows_imported'];
		$state['status']                = 'running';
		$state['phase']                 = 'database';
		$state['messages']              = isset( $state['messages'] ) && is_array( $state['messages'] ) ? $state['messages'] : array();
		$msg                            = 'Importing database via WordPress (SQLite)...';
		if ( empty( $state['messages'] ) || end( $state['messages'] ) !== $msg ) {
			$state['messages'][] = $msg;
		}
		$state['updated_at'] = time();
		@file_put_contents( $state_file, json_encode( $state, JSON_PRETTY_PRINT ) );
		asenha_sqlite_runner_emit( asenha_sqlite_runner_prepare_response( $state ) );
	}

	$state['sql_complete'] = true;
	$state['sql_tables']   = isset( $result['tables_imported'] ) ? (int) $result['tables_imported'] : ( isset( $state['sql_tables_completed'] ) ? (int) $state['sql_tables_completed'] : 0 );
	$state['sql_rows']     = isset( $result['rows_imported'] ) ? (int) $result['rows_imported'] : ( isset( $state['sql_rows_imported'] ) ? (int) $state['sql_rows_imported'] : 0 );
	$state['sql_statements_executed'] = (int) $state['sql_rows'];
	$state['sql_position'] = isset( $result['byte_offset'] ) ? (int) $result['byte_offset'] : ( isset( $state['sql_position'] ) ? (int) $state['sql_position'] : 0 );
	$state['status']       = 'running';
	$state['phase']        = 'database';
	$state['updated_at']   = time();
	@file_put_contents( $state_file, json_encode( $state, JSON_PRETTY_PRINT ) );
	asenha_sqlite_runner_emit( asenha_sqlite_runner_prepare_response( $state ) );
} catch ( Throwable $e ) {
	$fail = array(
		'status'       => 'failed',
		'phase'        => 'database',
		'sql_complete' => false,
		'errors'       => array( $e->getMessage() ),
	);
	if ( isset( $state_file ) && is_string( $state_file ) && $state_file !== '' && isset( $state ) && is_array( $state ) ) {
		$state['status']     = 'failed';
		$state['phase']      = 'database';
		$state['errors']     = isset( $state['errors'] ) && is_array( $state['errors'] ) ? $state['errors'] : array();
		$state['errors'][]   = $e->getMessage();
		$state['updated_at'] = time();
		@file_put_contents( $state_file, json_encode( $state, JSON_PRETTY_PRINT ) );
		$fail = asenha_sqlite_runner_prepare_response( $state );
	}
	asenha_sqlite_runner_emit( $fail );
}

/**
 * Strip large internal keys before returning JSON to the emergency UI.
 *
 * @param array $state Restore state.
 * @return array
 */
function asenha_sqlite_runner_prepare_response( $state ) {
	if ( ! is_array( $state ) ) {
		return array( 'status' => 'failed', 'errors' => array( 'Invalid restore state' ) );
	}
	$response = $state;
	unset( $response['copy_files'] );
	unset( $response['backup_path'] );
	unset( $response['sync_scan_dirs'] );
	unset( $response['sync_scan_current_dir'] );
	unset( $response['sync_scan_current_index'] );
	unset( $response['sync_delete_queue_path'] );
	unset( $response['archive_passphrase'] );
	unset( $response['sql_objects'] );
	unset( $response['sqlite_sql_file'] );
	return $response;
}

