/**
 * Sanitize a backup artifact basename without mutating dot segments.
 *
 * @param string $filename Raw filename.
 * @return string
 */
function asenha_restore_sanitize_backup_basename( $filename ) {
	$filename = basename( (string) $filename );
	$filename = preg_replace( '/[^A-Za-z0-9._-]/', '', $filename );
	if ( ! is_string( $filename ) || '' === $filename || false !== strpos( $filename, '..' ) ) {
		return '';
	}
	return $filename;
}

/**
 * Read admin_site_enhancements_extra from the restored database.
 *
 * @param mysqli $mysqli    Database connection.
 * @param string $db_prefix Table prefix.
 * @return array
 */
function asenha_restore_read_extra_option( $mysqli, $db_prefix ) {
	$table = preg_replace( '/[^A-Za-z0-9_]/', '', (string) $db_prefix . 'options' );
	if ( '' === $table ) {
		return array( 'exists' => false, 'value' => array() );
	}

	$option_name = $mysqli->real_escape_string( 'admin_site_enhancements_extra' );
	$result      = $mysqli->query( 'SELECT option_value FROM `' . $table . "` WHERE option_name = '" . $option_name . "' LIMIT 1" );
	$row         = $result ? $result->fetch_assoc() : null;
	if ( $result ) {
		$result->free();
	}

	if ( ! is_array( $row ) || ! array_key_exists( 'option_value', $row ) ) {
		return array( 'exists' => false, 'value' => array() );
	}

	$value = @unserialize( (string) $row['option_value'], array( 'allowed_classes' => false ) );
	if ( ! is_array( $value ) ) {
		$value = array();
	}

	return array( 'exists' => true, 'value' => $value );
}

/**
 * Persist admin_site_enhancements_extra to the restored database.
 *
 * @param mysqli $mysqli    Database connection.
 * @param string $db_prefix Table prefix.
 * @param array  $value     Option value.
 * @param bool   $exists    Whether the option row exists.
 * @return bool
 */
function asenha_restore_write_extra_option( $mysqli, $db_prefix, $value, $exists ) {
	$table = preg_replace( '/[^A-Za-z0-9_]/', '', (string) $db_prefix . 'options' );
	if ( '' === $table || ! is_array( $value ) ) {
		return false;
	}

	$option_name  = $mysqli->real_escape_string( 'admin_site_enhancements_extra' );
	$option_value = $mysqli->real_escape_string( serialize( $value ) );

	if ( $exists ) {
		return (bool) $mysqli->query( 'UPDATE `' . $table . "` SET option_value = '" . $option_value . "' WHERE option_name = '" . $option_name . "'" );
	}

	$autoload = $mysqli->real_escape_string( 'yes' );
	return (bool) $mysqli->query( 'INSERT INTO `' . $table . "` (option_name, option_value, autoload) VALUES ('" . $option_name . "', '" . $option_value . "', '" . $autoload . "')" );
}

/**
 * Normalize an archive-index entry enough for restore preservation.
 *
 * @param array $entry Raw entry.
 * @return array
 */
function asenha_restore_sanitize_archive_index_entry( $entry ) {
	$entry    = is_array( $entry ) ? $entry : array();
	$filename = isset( $entry['filename'] ) ? asenha_restore_sanitize_backup_basename( (string) $entry['filename'] ) : '';
	if ( '' === $filename ) {
		return array();
	}

	$locations = array();
	if ( ! empty( $entry['locations'] ) && is_array( $entry['locations'] ) ) {
		foreach ( $entry['locations'] as $location_id => $present ) {
			$location_id = preg_replace( '/[^A-Za-z0-9._:-]/', '', (string) $location_id );
			if ( '' !== $location_id && ! empty( $present ) ) {
				$locations[ $location_id ] = true;
			}
		}
	}
	if ( empty( $locations ) ) {
		return array();
	}

	$artifacts = array();
	if ( ! empty( $entry['artifacts'] ) && is_array( $entry['artifacts'] ) ) {
		foreach ( $entry['artifacts'] as $artifact ) {
			$artifact = asenha_restore_sanitize_backup_basename( (string) $artifact );
			if ( '' !== $artifact ) {
				$artifacts[] = $artifact;
			}
		}
	}
	$artifacts[] = $filename;
	$artifacts   = array_values( array_unique( array_filter( $artifacts ) ) );

	$parts = array();
	if ( ! empty( $entry['multipart_parts'] ) && is_array( $entry['multipart_parts'] ) ) {
		foreach ( $entry['multipart_parts'] as $part ) {
			if ( ! is_array( $part ) ) {
				continue;
			}
			$part_name  = isset( $part['name'] ) ? asenha_restore_sanitize_backup_basename( (string) $part['name'] ) : '';
			$part_index = isset( $part['index'] ) ? max( 0, (int) $part['index'] ) : 0;
			$part_size  = isset( $part['size'] ) ? max( 0, (int) $part['size'] ) : 0;
			if ( '' === $part_name || $part_index <= 0 ) {
				continue;
			}
			$parts[] = array(
				'index' => $part_index,
				'name'  => $part_name,
				'size'  => $part_size,
			);
		}
	}

	usort(
		$parts,
		function ( $a, $b ) {
			return ( isset( $a['index'] ) ? (int) $a['index'] : 0 ) - ( isset( $b['index'] ) ? (int) $b['index'] : 0 );
		}
	);

	$clean = array(
		'filename'   => $filename,
		'size'       => isset( $entry['size'] ) ? max( 0, (int) $entry['size'] ) : 0,
		'created_at' => isset( $entry['created_at'] ) ? max( 0, (int) $entry['created_at'] ) : time(),
		'locations'  => $locations,
		'artifacts'  => $artifacts,
	);
	if ( ! empty( $parts ) ) {
		$clean['multipart_parts'] = $parts;
	}

	$meta = isset( $entry['meta'] ) && is_array( $entry['meta'] ) ? $entry['meta'] : $entry;
	$string_fields = array(
		'backup_method',
		'chain_id',
		'base_filename',
		'parent_filename',
		'template_id',
		'policy_id',
		'policy_title',
		'template_scope_hash',
		'backup_id',
		'wp_content_snapshot',
		'archive_encryption_algo',
	);
	foreach ( $string_fields as $field ) {
		if ( ! isset( $meta[ $field ] ) ) {
			continue;
		}
		$value = (string) $meta[ $field ];
		if ( in_array( $field, array( 'base_filename', 'parent_filename', 'wp_content_snapshot' ), true ) ) {
			$value = asenha_restore_sanitize_backup_basename( $value );
		} elseif ( in_array( $field, array( 'backup_method', 'archive_encryption_algo' ), true ) ) {
			$value = preg_replace( '/[^a-z0-9_-]/', '', strtolower( $value ) );
		} else {
			$value = trim( preg_replace( '/[\r\n\t]+/', ' ', $value ) );
		}
		if ( '' !== $value ) {
			$clean[ $field ] = $value;
		}
	}
	if ( array_key_exists( 'sequence', $meta ) ) {
		$clean['sequence'] = max( 0, (int) $meta['sequence'] );
	}
	if ( array_key_exists( 'archive_encryption_enabled', $meta ) ) {
		$clean['archive_encryption_enabled'] = ! empty( $meta['archive_encryption_enabled'] );
	}
	if ( array_key_exists( 'archive_passphrase_required', $meta ) ) {
		$clean['archive_passphrase_required'] = ! empty( $meta['archive_passphrase_required'] );
	}

	return $clean;
}

/**
 * Recompute archive index location counts from by_backup.
 *
 * @param array $index Archive index.
 * @return array
 */
function asenha_restore_normalize_archives_index( $index ) {
	$index     = is_array( $index ) ? $index : array();
	$by_backup = isset( $index['by_backup'] ) && is_array( $index['by_backup'] ) ? $index['by_backup'] : array();
	$clean     = array();

	foreach ( $by_backup as $key => $entry ) {
		if ( ! is_array( $entry ) ) {
			continue;
		}
		$entry = asenha_restore_sanitize_archive_index_entry( $entry );
		if ( empty( $entry['filename'] ) || empty( $entry['locations'] ) ) {
			continue;
		}
		$clean[ $entry['filename'] ] = $entry;
	}

	$counts = array();
	foreach ( $clean as $entry ) {
		foreach ( array_keys( (array) $entry['locations'] ) as $location_id ) {
			if ( ! isset( $counts[ $location_id ] ) ) {
				$counts[ $location_id ] = 0;
			}
			$counts[ $location_id ]++;
		}
	}

	$by_location = array();
	foreach ( $counts as $location_id => $count ) {
		$by_location[ $location_id ] = array( 'count' => (int) $count );
	}

	return array(
		'version'      => 2,
		'seeded_local' => ! empty( $index['seeded_local'] ),
		'by_backup'    => $clean,
		'by_location'  => $by_location,
	);
}

/**
 * Check whether all local artifacts for an archive are still present.
 *
 * @param string $backup_dir Backup directory.
 * @param array  $entry      Archive entry.
 * @return bool
 */
function asenha_restore_local_artifacts_present( $backup_dir, $entry ) {
	$backup_dir = rtrim( (string) $backup_dir, '/\\' );
	if ( '' === $backup_dir || ! is_dir( $backup_dir ) || ! is_array( $entry ) ) {
		return false;
	}

	$filename  = isset( $entry['filename'] ) ? asenha_restore_sanitize_backup_basename( (string) $entry['filename'] ) : '';
	$artifacts = isset( $entry['artifacts'] ) && is_array( $entry['artifacts'] ) ? $entry['artifacts'] : array( $filename );
	$artifacts[] = $filename;
	$artifacts = array_values( array_unique( array_filter( array_map( 'asenha_restore_sanitize_backup_basename', $artifacts ) ) ) );
	if ( empty( $artifacts ) ) {
		return false;
	}

	foreach ( $artifacts as $artifact ) {
		$path = $backup_dir . DIRECTORY_SEPARATOR . $artifact;
		if ( ! file_exists( $path ) ) {
			return false;
		}
	}

	return true;
}

/**
 * Merge preserved remote locations and archive index entries before restore completion.
 *
 * @param array  $state      Restore state.
 * @param mysqli $mysqli     Database connection.
 * @param string $db_prefix  Database table prefix.
 * @param string $backup_dir Backup directory.
 * @return array Updated state.
 */
function asenha_restore_finalize_archive_preservation_invariant( $state, $mysqli, $db_prefix, $backup_dir ) {
	$state = is_array( $state ) ? $state : array();
	$preservation = isset( $state['archives_index_preservation'] ) && is_array( $state['archives_index_preservation'] )
		? $state['archives_index_preservation']
		: array();
	$archives     = isset( $preservation['archives'] ) && is_array( $preservation['archives'] ) ? $preservation['archives'] : array();
	if ( ! empty( $state['source_archive_preservation'] ) && is_array( $state['source_archive_preservation'] ) ) {
		$source_entry = asenha_restore_sanitize_archive_index_entry( $state['source_archive_preservation'] );
		if ( ! empty( $source_entry['filename'] ) ) {
			$archives[ $source_entry['filename'] ] = $state['source_archive_preservation'];
		}
	}
	if ( empty( $archives ) ) {
		return $state;
	}

	$option = asenha_restore_read_extra_option( $mysqli, $db_prefix );
	$extra  = isset( $option['value'] ) && is_array( $option['value'] ) ? $option['value'] : array();
	if ( ! isset( $extra['site_backup'] ) || ! is_array( $extra['site_backup'] ) ) {
		$extra['site_backup'] = array();
	}

	$locations = isset( $extra['site_backup']['locations'] ) && is_array( $extra['site_backup']['locations'] ) ? $extra['site_backup']['locations'] : array();
	if ( ! isset( $locations['version'] ) ) {
		$locations['version'] = 2;
	}
	if ( ! isset( $locations['local'] ) || ! is_array( $locations['local'] ) ) {
		$locations['local'] = array( 'enabled' => true );
	}
	if ( ! isset( $locations['remotes'] ) || ! is_array( $locations['remotes'] ) ) {
		$locations['remotes'] = array();
	}

	$existing_remote_ids = array();
	foreach ( $locations['remotes'] as $remote ) {
		if ( ! is_array( $remote ) || empty( $remote['id'] ) ) {
			continue;
		}
		$remote_id = preg_replace( '/[^A-Za-z0-9._:-]/', '', (string) $remote['id'] );
		if ( '' !== $remote_id ) {
			$existing_remote_ids[ $remote_id ] = true;
		}
	}

	$restored_locations = array();
	$preserved_remotes  = isset( $preservation['remote_locations'] ) && is_array( $preservation['remote_locations'] ) ? $preservation['remote_locations'] : array();
	foreach ( $preserved_remotes as $remote_id => $remote ) {
		$remote_id = preg_replace( '/[^A-Za-z0-9._:-]/', '', (string) $remote_id );
		if ( '' === $remote_id || ! is_array( $remote ) || ! empty( $existing_remote_ids[ $remote_id ] ) ) {
			continue;
		}
		$remote['id'] = $remote_id;
		if ( empty( $remote['type'] ) ) {
			continue;
		}
		$locations['remotes'][] = $remote;
		$existing_remote_ids[ $remote_id ] = true;
		$restored_locations[] = $remote_id;
	}

	$index = isset( $extra['site_backup']['archives_index'] ) && is_array( $extra['site_backup']['archives_index'] ) ? $extra['site_backup']['archives_index'] : array();
	$index = asenha_restore_normalize_archives_index( $index );

	$preserved_pairs = array();
	$remote_expected = array();
	$remote_preserved = array();
	$skipped_local = array();

	foreach ( $archives as $archive ) {
		$entry = asenha_restore_sanitize_archive_index_entry( $archive );
		if ( empty( $entry['filename'] ) || empty( $entry['locations'] ) ) {
			continue;
		}

		$filename = (string) $entry['filename'];
		foreach ( $entry['locations'] as $location_id => $present ) {
			if ( 'local' !== $location_id && ! empty( $present ) ) {
				$remote_expected[ $filename . '@' . $location_id ] = true;
			}
		}

		if ( ! empty( $entry['locations']['local'] ) && ! asenha_restore_local_artifacts_present( $backup_dir, $entry ) ) {
			unset( $entry['locations']['local'] );
			$skipped_local[] = $filename;
		}
		if ( empty( $entry['locations'] ) ) {
			continue;
		}

		$current = isset( $index['by_backup'][ $filename ] ) && is_array( $index['by_backup'][ $filename ] ) ? $index['by_backup'][ $filename ] : array();
		$current_locations = isset( $current['locations'] ) && is_array( $current['locations'] ) ? $current['locations'] : array();
		foreach ( $entry['locations'] as $location_id => $present ) {
			if ( ! empty( $present ) ) {
				$current_locations[ $location_id ] = true;
				$pair_key = $filename . '@' . $location_id;
				$preserved_pairs[ $pair_key ] = true;
				if ( 'local' !== $location_id ) {
					$remote_preserved[ $pair_key ] = true;
				}
			}
		}

		$current_artifacts = isset( $current['artifacts'] ) && is_array( $current['artifacts'] ) ? $current['artifacts'] : array();
		$entry['artifacts'] = array_values( array_unique( array_filter( array_merge( $current_artifacts, (array) $entry['artifacts'] ) ) ) );
		$entry['locations'] = $current_locations;
		$index['by_backup'][ $filename ] = array_merge( $current, $entry );
	}

	$index = asenha_restore_normalize_archives_index( $index );
	$extra['site_backup']['archives_index'] = $index;
	$extra['site_backup']['locations'] = $locations;

	$state['archive_preservation_finalized'] = true;
	$state['archive_preservation_finalized_at'] = time();
	$state['archive_preservation_result'] = array(
		'runner'                 => true,
		'preserved_count'        => count( $preserved_pairs ),
		'remote_preserved_count' => count( $remote_preserved ),
		'remote_expected_count'  => count( $remote_expected ),
		'skipped_local'          => array_values( array_unique( $skipped_local ) ),
		'restored_locations'     => array_values( array_unique( $restored_locations ) ),
	);

	if ( count( $remote_expected ) > 0 && count( $remote_preserved ) <= 0 ) {
		$state['archive_preservation_warning'] = 'Restore completed, but remote archive index preservation could not confirm any remote archive entries were re-merged.';
		$state['db_last_warning'] = $state['archive_preservation_warning'];
	}

	if ( ! asenha_restore_write_extra_option( $mysqli, $db_prefix, $extra, ! empty( $option['exists'] ) ) ) {
		$state['archive_preservation_warning'] = 'Restore completed, but remote archive index preservation could not update ASE archive bookkeeping.';
		$state['db_last_warning'] = $state['archive_preservation_warning'];
	}

	return $state;
}

/**
 * Read a single WordPress option value from the restored database.
 *
 * @param mysqli $mysqli       Database connection.
 * @param string $db_prefix    Table prefix.
 * @param string $option_name  Option name.
 * @return string|null Option value or null when missing.
 */
function asenha_restore_read_option_value( $mysqli, $db_prefix, $option_name ) {
	$table = preg_replace( '/[^A-Za-z0-9_]/', '', (string) $db_prefix . 'options' );
	if ( '' === $table || '' === (string) $option_name ) {
		return null;
	}

	$name_esc = $mysqli->real_escape_string( (string) $option_name );
	$result   = $mysqli->query( "SELECT option_value FROM `{$table}` WHERE option_name = '{$name_esc}' LIMIT 1" );
	$row      = $result ? $result->fetch_assoc() : null;
	if ( $result ) {
		$result->free();
	}

	if ( ! is_array( $row ) || ! array_key_exists( 'option_value', $row ) ) {
		return null;
	}

	return (string) $row['option_value'];
}

/**
 * Build a DateTimeZone from WordPress timezone settings.
 *
 * @param string $timezone_string Timezone identifier (IANA), if configured.
 * @param float  $gmt_offset      GMT offset when no identifier is set.
 * @return DateTimeZone
 */
function asenha_restore_build_timezone( $timezone_string, $gmt_offset ) {
	$timezone_string = is_string( $timezone_string ) ? trim( $timezone_string ) : '';
	if ( '' !== $timezone_string ) {
		try {
			return new DateTimeZone( $timezone_string );
		} catch ( Exception $e ) {
			// Fall through to offset-based timezone.
		}
	}

	$offset  = (float) $gmt_offset;
	$sign    = ( $offset < 0 ) ? '-' : '+';
	$offset  = abs( $offset );
	$hours   = (int) floor( $offset );
	$minutes = (int) round( ( $offset - $hours ) * 60 );
	if ( $minutes >= 60 ) {
		$hours++;
		$minutes = 0;
	}

	$offset_string = sprintf( '%s%02d:%02d', $sign, $hours, $minutes );

	try {
		return new DateTimeZone( $offset_string );
	} catch ( Exception $e ) {
		return new DateTimeZone( 'UTC' );
	}
}

/**
 * Current site-local MySQL datetime (matches WordPress current_time( 'mysql' )).
 *
 * @param mysqli $mysqli    Database connection.
 * @param string $db_prefix Table prefix.
 * @return string
 */
function asenha_restore_mysql_datetime_now( $mysqli, $db_prefix ) {
	global $wp_timezone_string, $wp_gmt_offset;

	$tz_string  = asenha_restore_read_option_value( $mysqli, $db_prefix, 'timezone_string' );
	$offset_raw = asenha_restore_read_option_value( $mysqli, $db_prefix, 'gmt_offset' );

	if ( null === $tz_string && null === $offset_raw ) {
		$tz_string  = isset( $wp_timezone_string ) ? (string) $wp_timezone_string : '';
		$gmt_offset = isset( $wp_gmt_offset ) ? (float) $wp_gmt_offset : 0.0;
	} else {
		$tz_string  = null !== $tz_string ? (string) $tz_string : '';
		$gmt_offset = null !== $offset_raw ? (float) $offset_raw : 0.0;
	}

	try {
		$tz = asenha_restore_build_timezone( $tz_string, $gmt_offset );
		$dt = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) );
		return $dt->setTimezone( $tz )->format( 'Y-m-d H:i:s' );
	} catch ( Exception $e ) {
		return gmdate( 'Y-m-d H:i:s' );
	}
}

/**
 * Read stored max log entries from the restored ASE extra option.
 *
 * @param mysqli $mysqli    Database connection.
 * @param string $db_prefix Table prefix.
 * @return int
 */
function asenha_restore_get_log_max_entries( $mysqli, $db_prefix ) {
	$allowed = array( 100, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000 );
	$default = 5000;
	$option  = asenha_restore_read_extra_option( $mysqli, $db_prefix );
	$extra   = isset( $option['value'] ) && is_array( $option['value'] ) ? $option['value'] : array();
	$raw     = 0;
	if ( isset( $extra['site_backup']['log_max_entries'] ) ) {
		$raw = (int) $extra['site_backup']['log_max_entries'];
	}
	return in_array( $raw, $allowed, true ) ? $raw : $default;
}

/**
 * Trim backup log rows to the configured maximum (newest by id).
 *
 * @param mysqli $mysqli    Database connection.
 * @param string $db_prefix Table prefix.
 * @param int    $max       Maximum rows to keep.
 * @return void
 */
function asenha_restore_trim_backup_logs( $mysqli, $db_prefix, $max ) {
	$max = max( 1, (int) $max );
	$table = preg_replace( '/[^A-Za-z0-9_]/', '', (string) $db_prefix . 'asenha_backup_logs' );
	if ( '' === $table ) {
		return;
	}

	$max_esc = (int) $max;
	$sql     = "DELETE FROM `{$table}` WHERE id < (
		SELECT cutoff_id FROM (
			SELECT MIN(inner_id) AS cutoff_id FROM (
				SELECT id AS inner_id FROM `{$table}` ORDER BY id DESC LIMIT {$max_esc}
			) AS innermost
		) AS mid
	)";
	$mysqli->query( $sql );
}

/**
 * Replace backup logs with preserved target-site rows from restore temp file.
 *
 * @param mysqli $mysqli     Database connection.
 * @param string $db_prefix  Table prefix.
 * @param string $logs_path  Path to .backup_logs_preserve.json.
 * @return void
 */
function asenha_restore_preserved_backup_logs( $mysqli, $db_prefix, $logs_path ) {
	$rows = asenha_read_json_file( $logs_path );
	if ( ! is_array( $rows ) ) {
		return;
	}

	$table = preg_replace( '/[^A-Za-z0-9_]/', '', (string) $db_prefix . 'asenha_backup_logs' );
	if ( '' === $table ) {
		return;
	}

	$mysqli->query( "TRUNCATE TABLE `{$table}`" );

	foreach ( $rows as $row ) {
		if ( ! is_array( $row ) ) {
			continue;
		}

		$operation = isset( $row['operation'] ) ? preg_replace( '/[^a-z0-9_-]/', '', strtolower( (string) $row['operation'] ) ) : '';
		$status    = isset( $row['status'] ) ? preg_replace( '/[^a-z0-9_-]/', '', strtolower( (string) $row['status'] ) ) : '';
		if ( '' === $operation || '' === $status ) {
			continue;
		}

		$backup_id  = isset( $row['backup_id'] ) ? $mysqli->real_escape_string( (string) $row['backup_id'] ) : '';
		$message    = isset( $row['message'] ) ? $mysqli->real_escape_string( (string) $row['message'] ) : '';
		$details    = isset( $row['details'] ) && '' !== (string) $row['details'] ? $mysqli->real_escape_string( (string) $row['details'] ) : '';
		$created_at = isset( $row['created_at'] ) ? $mysqli->real_escape_string( (string) $row['created_at'] ) : $mysqli->real_escape_string( asenha_restore_mysql_datetime_now( $mysqli, $db_prefix ) );

		$backup_sql = '' !== $backup_id ? "'{$backup_id}'" : 'NULL';
		$details_sql = '' !== $details ? "'{$details}'" : 'NULL';

		$mysqli->query(
			"INSERT INTO `{$table}` (`backup_id`,`operation`,`status`,`message`,`details`,`created_at`) VALUES ({$backup_sql},'{$operation}','{$status}','{$message}',{$details_sql},'{$created_at}')"
		);
	}

	asenha_restore_trim_backup_logs( $mysqli, $db_prefix, asenha_restore_get_log_max_entries( $mysqli, $db_prefix ) );

	if ( '' !== $logs_path && file_exists( $logs_path ) ) {
		@unlink( $logs_path );
	}
}

/**
 * Insert restore/completed row into backup logs (runner has no WordPress bootstrap).
 *
 * @param mysqli $mysqli    Database connection.
 * @param string $db_prefix Table prefix.
 * @param array  $state     Restore state.
 * @return void
 */
function asenha_restore_insert_restore_completed_log( $mysqli, $db_prefix, $state ) {
	$state = is_array( $state ) ? $state : array();

	$table = preg_replace( '/[^A-Za-z0-9_]/', '', (string) $db_prefix . 'asenha_backup_logs' );
	if ( '' === $table ) {
		return;
	}

	$filename = isset( $state['filename'] ) ? asenha_restore_sanitize_backup_basename( (string) $state['filename'] ) : '';
	if ( '' === $filename ) {
		return;
	}

	$restore_id_raw = '';
	if ( ! empty( $state['restore_id'] ) ) {
		$restore_id_raw = (string) $state['restore_id'];
	} elseif ( ! empty( $state['id'] ) ) {
		$restore_id_raw = (string) $state['id'];
	}
	$restore_id = preg_replace( '/[^A-Za-z0-9-]/', '', $restore_id_raw );
	$components = array();
	if ( ! empty( $state['components'] ) && is_array( $state['components'] ) ) {
		foreach ( $state['components'] as $component ) {
			$component = preg_replace( '/[^a-z0-9_-]/', '', strtolower( (string) $component ) );
			if ( '' !== $component ) {
				$components[] = $component;
			}
		}
	}

	$message = 'Restore completed from ' . $filename;
	$details = array(
		'restore_id' => $restore_id,
		'filename'   => $filename,
		'components' => $components,
	);
	$details_json = $mysqli->real_escape_string( json_encode( $details ) );
	$message_esc  = $mysqli->real_escape_string( $message );
	$created_at   = $mysqli->real_escape_string( asenha_restore_mysql_datetime_now( $mysqli, $db_prefix ) );

	$mysqli->query(
		"INSERT INTO `{$table}` (`backup_id`,`operation`,`status`,`message`,`details`,`created_at`) VALUES (NULL,'restore','completed','{$message_esc}','{$details_json}','{$created_at}')"
	);

	asenha_restore_trim_backup_logs( $mysqli, $db_prefix, asenha_restore_get_log_max_entries( $mysqli, $db_prefix ) );
}

// Final state: completed (finalizing).
