use super::bootstrap::{
    BootstrapOutcome, BootstrapRetryOutcome, bootstrap_failure_diagnostics,
    bootstrap_success_diagnostics,
};
#[cfg(feature = "_async")]
use super::bootstrap::{retry_async, retry_step_async};
#[cfg(feature = "_blocking")]
use super::bootstrap::{retry_blocking, retry_step_blocking};
use super::support::commands_get_request;
#[cfg(feature = "bot")]
use super::support::typed_commands_request;
use super::*;

fn get_default_menu_button_request() -> crate::types::advanced::AdvancedGetChatMenuButtonRequest {
    crate::types::advanced::AdvancedGetChatMenuButtonRequest::new()
}

fn get_chat_menu_button_request(
    chat_id: i64,
) -> crate::types::advanced::AdvancedGetChatMenuButtonRequest {
    crate::types::advanced::AdvancedGetChatMenuButtonRequest {
        chat_id: Some(chat_id.into()),
    }
}

fn set_menu_button_request(
    config: &MenuButtonConfig,
) -> crate::types::advanced::AdvancedSetChatMenuButtonRequest {
    config.into()
}

fn commands_in_sync(current: &[BotCommand], commands: &SetMyCommandsRequest) -> bool {
    current == commands.commands.as_slice()
}

fn menu_button_in_sync(current: &MenuButton, menu_button: &MenuButtonConfig) -> bool {
    current == &menu_button.menu_button
}

fn single_attempt_bootstrap_policy() -> BootstrapRetryPolicy {
    BootstrapRetryPolicy {
        max_attempts: 1,
        continue_on_failure: false,
        ..BootstrapRetryPolicy::default()
    }
}

fn sync_step_report(
    status: BootstrapStepStatus,
    phase: BootstrapStepPhase,
    attempt_count: usize,
    applied: Option<bool>,
    synced: Option<bool>,
) -> BootstrapSyncStepReport {
    BootstrapSyncStepReport {
        applied,
        synced,
        diagnostics: bootstrap_success_diagnostics(status, phase, attempt_count),
    }
}

fn sync_step_failure_report(
    status: BootstrapStepStatus,
    phase: BootstrapStepPhase,
    error: &Error,
    attempt_count: usize,
    applied: Option<bool>,
    synced: Option<bool>,
) -> BootstrapSyncStepReport {
    BootstrapSyncStepReport {
        applied,
        synced,
        diagnostics: bootstrap_failure_diagnostics(status, phase, error, attempt_count),
    }
}

fn handle_get_me_failure(
    report: &mut BootstrapReport,
    policy: BootstrapGetMePolicy,
    error: Error,
    attempt_count: usize,
) -> Option<Error> {
    let warn_and_continue =
        matches!(policy, BootstrapGetMePolicy::WarnAndContinueOnRetryable) && error.is_retryable();
    report.me.diagnostics = bootstrap_failure_diagnostics(
        if warn_and_continue {
            BootstrapStepStatus::Warned
        } else {
            BootstrapStepStatus::Failed
        },
        BootstrapStepPhase::Fetch,
        &error,
        attempt_count,
    );
    if warn_and_continue { None } else { Some(error) }
}

#[cfg(feature = "_async")]
#[derive(Clone)]
pub struct SetupApi {
    client: Client,
}

#[cfg(feature = "_async")]
impl SetupApi {
    pub(crate) fn new(client: Client) -> Self {
        Self { client }
    }

    async fn get_me_retry_attempt(&self) -> Result<User> {
        self.client
            .call_method_no_params_without_transport_retry("getMe")
            .await
    }

    async fn set_my_commands_retry_attempt(&self, request: &SetMyCommandsRequest) -> Result<bool> {
        request.validate()?;
        self.client
            .call_method_without_transport_retry("setMyCommands", request)
            .await
    }

    async fn get_my_commands_retry_attempt(
        &self,
        request: &GetMyCommandsRequest,
    ) -> Result<Vec<BotCommand>> {
        request.validate()?;
        self.client
            .call_method_without_transport_retry("getMyCommands", request)
            .await
    }

    async fn call_advanced_retry_attempt<Q>(&self, request: &Q) -> Result<Q::Response>
    where
        Q: AdvancedRequest,
    {
        request.validate()?;
        self.client
            .call_method_without_transport_retry(Q::METHOD, request)
            .await
    }

    pub async fn set_commands(&self, commands: Vec<BotCommand>) -> Result<bool> {
        let request = SetMyCommandsRequest::new(commands)?;
        self.client.bot().set_my_commands(&request).await
    }

    #[cfg(feature = "bot")]
    pub async fn set_typed_commands<C>(&self) -> Result<bool>
    where
        C: crate::bot::BotCommands,
    {
        self.set_commands(crate::bot::command_definitions::<C>())
            .await
    }

    pub async fn set_commands_with_retry(
        &self,
        commands: Vec<BotCommand>,
        policy: BootstrapRetryPolicy,
    ) -> Result<bool> {
        let request = SetMyCommandsRequest::new(commands)?;
        retry_async(policy, || async {
            self.set_my_commands_retry_attempt(&request).await
        })
        .await
    }

    #[cfg(feature = "bot")]
    pub async fn set_typed_commands_with_options<C>(
        &self,
        scope: Option<BotCommandScope>,
        language_code: Option<String>,
    ) -> Result<bool>
    where
        C: crate::bot::BotCommands,
    {
        let request = typed_commands_request::<C>(scope, language_code)?;
        self.client.bot().set_my_commands(&request).await
    }

    #[cfg(feature = "bot")]
    pub async fn set_typed_commands_with_options_retry<C>(
        &self,
        scope: Option<BotCommandScope>,
        language_code: Option<String>,
        policy: BootstrapRetryPolicy,
    ) -> Result<bool>
    where
        C: crate::bot::BotCommands,
    {
        let request = typed_commands_request::<C>(scope, language_code)?;
        retry_async(policy, || async {
            self.set_my_commands_retry_attempt(&request).await
        })
        .await
    }

    pub async fn get_menu_button(&self) -> Result<MenuButton> {
        self.client
            .advanced()
            .get_chat_menu_button_typed(&get_default_menu_button_request())
            .await
    }

    pub async fn get_chat_menu_button(&self, chat_id: i64) -> Result<MenuButton> {
        let request = get_chat_menu_button_request(chat_id);
        self.client
            .advanced()
            .get_chat_menu_button_typed(&request)
            .await
    }

    pub async fn set_menu_button(&self, config: impl Into<MenuButtonConfig>) -> Result<bool> {
        let config = config.into();
        let request = set_menu_button_request(&config);
        self.client
            .advanced()
            .set_chat_menu_button_typed(&request)
            .await
    }

    pub async fn set_chat_menu_button(
        &self,
        chat_id: i64,
        menu_button: impl Into<MenuButton>,
    ) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::for_chat(chat_id, menu_button))
            .await
    }

    pub async fn set_default_menu_button(&self) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::default_button())
            .await
    }

    pub async fn set_chat_default_menu_button(&self, chat_id: i64) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::for_chat_default(chat_id))
            .await
    }

    pub async fn set_commands_menu_button(&self) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::commands()).await
    }

    pub async fn set_chat_commands_menu_button(&self, chat_id: i64) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::for_chat_commands(chat_id))
            .await
    }

    pub async fn set_menu_button_with_retry(
        &self,
        config: impl Into<MenuButtonConfig>,
        policy: BootstrapRetryPolicy,
    ) -> Result<bool> {
        let config = config.into();
        let request = set_menu_button_request(&config);
        retry_async(policy, || async {
            self.call_advanced_retry_attempt(&request).await
        })
        .await
    }

    pub async fn bootstrap_with_retry(
        &self,
        plan: &BootstrapPlan,
        policy: BootstrapRetryPolicy,
    ) -> BootstrapOutcome {
        let mut report = BootstrapReport::default();
        if let Err(error) = policy.validate() {
            return BootstrapOutcome::failure(report, error);
        }

        if !matches!(plan.get_me, BootstrapGetMePolicy::Skip) {
            match retry_step_async(policy, || async { self.get_me_retry_attempt().await }).await {
                BootstrapRetryOutcome::Success {
                    value,
                    attempt_count,
                } => {
                    report.me.value = Some(value);
                    report.me.diagnostics = bootstrap_success_diagnostics(
                        BootstrapStepStatus::Succeeded,
                        BootstrapStepPhase::Fetch,
                        attempt_count,
                    );
                }
                BootstrapRetryOutcome::Failed {
                    error,
                    attempt_count,
                } => {
                    if let Some(error) =
                        handle_get_me_failure(&mut report, plan.get_me, error, attempt_count)
                    {
                        return BootstrapOutcome::failure(report, error);
                    }
                }
            }
        }

        if let Some(commands) = plan.commands.as_ref() {
            report.commands = Some(BootstrapSyncStepReport::default());
            let get_request = commands_get_request(commands);
            let fetch_attempts = match retry_step_async(policy, || {
                let get_request = get_request.clone();
                async move { self.get_my_commands_retry_attempt(&get_request).await }
            })
            .await
            {
                BootstrapRetryOutcome::Success {
                    value,
                    attempt_count,
                } => {
                    if commands_in_sync(&value, commands) {
                        report.commands = Some(sync_step_report(
                            BootstrapStepStatus::Unchanged,
                            BootstrapStepPhase::Check,
                            attempt_count,
                            Some(false),
                            Some(true),
                        ));
                        0
                    } else {
                        attempt_count
                    }
                }
                BootstrapRetryOutcome::Failed {
                    error,
                    attempt_count,
                } => {
                    report.commands = Some(sync_step_failure_report(
                        if policy.continue_on_failure {
                            BootstrapStepStatus::Warned
                        } else {
                            BootstrapStepStatus::Failed
                        },
                        BootstrapStepPhase::Check,
                        &error,
                        attempt_count,
                        Some(false),
                        Some(false),
                    ));
                    if policy.continue_on_failure {
                        0
                    } else {
                        return BootstrapOutcome::failure(report, error);
                    }
                }
            };
            if fetch_attempts > 0 {
                match retry_step_async(policy, || async {
                    self.set_my_commands_retry_attempt(commands).await
                })
                .await
                {
                    BootstrapRetryOutcome::Success {
                        value,
                        attempt_count,
                    } => {
                        report.commands = Some(sync_step_report(
                            if value {
                                BootstrapStepStatus::Applied
                            } else {
                                BootstrapStepStatus::Succeeded
                            },
                            BootstrapStepPhase::Apply,
                            fetch_attempts + attempt_count,
                            Some(value),
                            Some(value),
                        ));
                    }
                    BootstrapRetryOutcome::Failed {
                        error,
                        attempt_count,
                    } => {
                        report.commands = Some(sync_step_failure_report(
                            if policy.continue_on_failure {
                                BootstrapStepStatus::Warned
                            } else {
                                BootstrapStepStatus::Failed
                            },
                            BootstrapStepPhase::Apply,
                            &error,
                            fetch_attempts + attempt_count,
                            Some(false),
                            Some(false),
                        ));
                        if !policy.continue_on_failure {
                            return BootstrapOutcome::failure(report, error);
                        }
                    }
                }
            }
        }

        if let Some(menu_button) = plan.menu_button.as_ref() {
            report.menu_button = Some(BootstrapSyncStepReport::default());
            let get_request: crate::types::advanced::AdvancedGetChatMenuButtonRequest =
                menu_button.into();
            let fetch_attempts = match retry_step_async(policy, || {
                let get_request = get_request.clone();
                async move { self.call_advanced_retry_attempt(&get_request).await }
            })
            .await
            {
                BootstrapRetryOutcome::Success {
                    value,
                    attempt_count,
                } => {
                    if menu_button_in_sync(&value, menu_button) {
                        report.menu_button = Some(sync_step_report(
                            BootstrapStepStatus::Unchanged,
                            BootstrapStepPhase::Check,
                            attempt_count,
                            Some(false),
                            Some(true),
                        ));
                        0
                    } else {
                        attempt_count
                    }
                }
                BootstrapRetryOutcome::Failed {
                    error,
                    attempt_count,
                } => {
                    report.menu_button = Some(sync_step_failure_report(
                        if policy.continue_on_failure {
                            BootstrapStepStatus::Warned
                        } else {
                            BootstrapStepStatus::Failed
                        },
                        BootstrapStepPhase::Check,
                        &error,
                        attempt_count,
                        Some(false),
                        Some(false),
                    ));
                    if policy.continue_on_failure {
                        0
                    } else {
                        return BootstrapOutcome::failure(report, error);
                    }
                }
            };
            if fetch_attempts > 0 {
                let set_request = set_menu_button_request(menu_button);
                match retry_step_async(policy, || async {
                    self.call_advanced_retry_attempt(&set_request).await
                })
                .await
                {
                    BootstrapRetryOutcome::Success {
                        value,
                        attempt_count,
                    } => {
                        report.menu_button = Some(sync_step_report(
                            if value {
                                BootstrapStepStatus::Applied
                            } else {
                                BootstrapStepStatus::Succeeded
                            },
                            BootstrapStepPhase::Apply,
                            fetch_attempts + attempt_count,
                            Some(value),
                            Some(value),
                        ));
                    }
                    BootstrapRetryOutcome::Failed {
                        error,
                        attempt_count,
                    } => {
                        report.menu_button = Some(sync_step_failure_report(
                            if policy.continue_on_failure {
                                BootstrapStepStatus::Warned
                            } else {
                                BootstrapStepStatus::Failed
                            },
                            BootstrapStepPhase::Apply,
                            &error,
                            fetch_attempts + attempt_count,
                            Some(false),
                            Some(false),
                        ));
                        if !policy.continue_on_failure {
                            return BootstrapOutcome::failure(report, error);
                        }
                    }
                }
            }
        }

        BootstrapOutcome::success(report)
    }

    pub async fn bootstrap(&self, plan: &BootstrapPlan) -> BootstrapOutcome {
        self.bootstrap_with_retry(plan, single_attempt_bootstrap_policy())
            .await
    }
}

#[cfg(feature = "_blocking")]
#[derive(Clone)]
pub struct BlockingSetupApi {
    client: BlockingClient,
}

#[cfg(feature = "_blocking")]
impl BlockingSetupApi {
    pub(crate) fn new(client: BlockingClient) -> Self {
        Self { client }
    }

    fn get_me_retry_attempt(&self) -> Result<User> {
        self.client
            .call_method_no_params_without_transport_retry("getMe")
    }

    fn set_my_commands_retry_attempt(&self, request: &SetMyCommandsRequest) -> Result<bool> {
        request.validate()?;
        self.client
            .call_method_without_transport_retry("setMyCommands", request)
    }

    fn get_my_commands_retry_attempt(
        &self,
        request: &GetMyCommandsRequest,
    ) -> Result<Vec<BotCommand>> {
        request.validate()?;
        self.client
            .call_method_without_transport_retry("getMyCommands", request)
    }

    fn call_advanced_retry_attempt<Q>(&self, request: &Q) -> Result<Q::Response>
    where
        Q: AdvancedRequest,
    {
        request.validate()?;
        self.client
            .call_method_without_transport_retry(Q::METHOD, request)
    }

    pub fn set_commands(&self, commands: Vec<BotCommand>) -> Result<bool> {
        let request = SetMyCommandsRequest::new(commands)?;
        self.client.bot().set_my_commands(&request)
    }

    #[cfg(feature = "bot")]
    pub fn set_typed_commands<C>(&self) -> Result<bool>
    where
        C: crate::bot::BotCommands,
    {
        self.set_commands(crate::bot::command_definitions::<C>())
    }

    pub fn set_commands_with_retry(
        &self,
        commands: Vec<BotCommand>,
        policy: BootstrapRetryPolicy,
    ) -> Result<bool> {
        let request = SetMyCommandsRequest::new(commands)?;
        retry_blocking(policy, || self.set_my_commands_retry_attempt(&request))
    }

    #[cfg(feature = "bot")]
    pub fn set_typed_commands_with_options<C>(
        &self,
        scope: Option<BotCommandScope>,
        language_code: Option<String>,
    ) -> Result<bool>
    where
        C: crate::bot::BotCommands,
    {
        let request = typed_commands_request::<C>(scope, language_code)?;
        self.client.bot().set_my_commands(&request)
    }

    #[cfg(feature = "bot")]
    pub fn set_typed_commands_with_options_retry<C>(
        &self,
        scope: Option<BotCommandScope>,
        language_code: Option<String>,
        policy: BootstrapRetryPolicy,
    ) -> Result<bool>
    where
        C: crate::bot::BotCommands,
    {
        let request = typed_commands_request::<C>(scope, language_code)?;
        retry_blocking(policy, || self.set_my_commands_retry_attempt(&request))
    }

    pub fn get_menu_button(&self) -> Result<MenuButton> {
        self.client
            .advanced()
            .get_chat_menu_button_typed(&get_default_menu_button_request())
    }

    pub fn get_chat_menu_button(&self, chat_id: i64) -> Result<MenuButton> {
        let request = get_chat_menu_button_request(chat_id);
        self.client.advanced().get_chat_menu_button_typed(&request)
    }

    pub fn set_menu_button(&self, config: impl Into<MenuButtonConfig>) -> Result<bool> {
        let config = config.into();
        let request = set_menu_button_request(&config);
        self.client.advanced().set_chat_menu_button_typed(&request)
    }

    pub fn set_chat_menu_button(
        &self,
        chat_id: i64,
        menu_button: impl Into<MenuButton>,
    ) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::for_chat(chat_id, menu_button))
    }

    pub fn set_default_menu_button(&self) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::default_button())
    }

    pub fn set_chat_default_menu_button(&self, chat_id: i64) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::for_chat_default(chat_id))
    }

    pub fn set_commands_menu_button(&self) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::commands())
    }

    pub fn set_chat_commands_menu_button(&self, chat_id: i64) -> Result<bool> {
        self.set_menu_button(MenuButtonConfig::for_chat_commands(chat_id))
    }

    pub fn set_menu_button_with_retry(
        &self,
        config: impl Into<MenuButtonConfig>,
        policy: BootstrapRetryPolicy,
    ) -> Result<bool> {
        let config = config.into();
        let request = set_menu_button_request(&config);
        retry_blocking(policy, || self.call_advanced_retry_attempt(&request))
    }

    pub fn bootstrap_with_retry(
        &self,
        plan: &BootstrapPlan,
        policy: BootstrapRetryPolicy,
    ) -> BootstrapOutcome {
        let mut report = BootstrapReport::default();
        if let Err(error) = policy.validate() {
            return BootstrapOutcome::failure(report, error);
        }

        if !matches!(plan.get_me, BootstrapGetMePolicy::Skip) {
            match retry_step_blocking(policy, || self.get_me_retry_attempt()) {
                BootstrapRetryOutcome::Success {
                    value,
                    attempt_count,
                } => {
                    report.me.value = Some(value);
                    report.me.diagnostics = bootstrap_success_diagnostics(
                        BootstrapStepStatus::Succeeded,
                        BootstrapStepPhase::Fetch,
                        attempt_count,
                    );
                }
                BootstrapRetryOutcome::Failed {
                    error,
                    attempt_count,
                } => {
                    if let Some(error) =
                        handle_get_me_failure(&mut report, plan.get_me, error, attempt_count)
                    {
                        return BootstrapOutcome::failure(report, error);
                    }
                }
            }
        }

        if let Some(commands) = plan.commands.as_ref() {
            report.commands = Some(BootstrapSyncStepReport::default());
            let get_request = commands_get_request(commands);
            let fetch_attempts = match retry_step_blocking(policy, || {
                self.get_my_commands_retry_attempt(&get_request)
            }) {
                BootstrapRetryOutcome::Success {
                    value,
                    attempt_count,
                } => {
                    if commands_in_sync(&value, commands) {
                        report.commands = Some(sync_step_report(
                            BootstrapStepStatus::Unchanged,
                            BootstrapStepPhase::Check,
                            attempt_count,
                            Some(false),
                            Some(true),
                        ));
                        0
                    } else {
                        attempt_count
                    }
                }
                BootstrapRetryOutcome::Failed {
                    error,
                    attempt_count,
                } => {
                    report.commands = Some(sync_step_failure_report(
                        if policy.continue_on_failure {
                            BootstrapStepStatus::Warned
                        } else {
                            BootstrapStepStatus::Failed
                        },
                        BootstrapStepPhase::Check,
                        &error,
                        attempt_count,
                        Some(false),
                        Some(false),
                    ));
                    if policy.continue_on_failure {
                        0
                    } else {
                        return BootstrapOutcome::failure(report, error);
                    }
                }
            };
            if fetch_attempts > 0 {
                match retry_step_blocking(policy, || self.set_my_commands_retry_attempt(commands)) {
                    BootstrapRetryOutcome::Success {
                        value,
                        attempt_count,
                    } => {
                        report.commands = Some(sync_step_report(
                            if value {
                                BootstrapStepStatus::Applied
                            } else {
                                BootstrapStepStatus::Succeeded
                            },
                            BootstrapStepPhase::Apply,
                            fetch_attempts + attempt_count,
                            Some(value),
                            Some(value),
                        ));
                    }
                    BootstrapRetryOutcome::Failed {
                        error,
                        attempt_count,
                    } => {
                        report.commands = Some(sync_step_failure_report(
                            if policy.continue_on_failure {
                                BootstrapStepStatus::Warned
                            } else {
                                BootstrapStepStatus::Failed
                            },
                            BootstrapStepPhase::Apply,
                            &error,
                            fetch_attempts + attempt_count,
                            Some(false),
                            Some(false),
                        ));
                        if !policy.continue_on_failure {
                            return BootstrapOutcome::failure(report, error);
                        }
                    }
                }
            }
        }

        if let Some(menu_button) = plan.menu_button.as_ref() {
            report.menu_button = Some(BootstrapSyncStepReport::default());
            let get_request: crate::types::advanced::AdvancedGetChatMenuButtonRequest =
                menu_button.into();
            let fetch_attempts = match retry_step_blocking(policy, || {
                self.call_advanced_retry_attempt(&get_request)
            }) {
                BootstrapRetryOutcome::Success {
                    value,
                    attempt_count,
                } => {
                    if menu_button_in_sync(&value, menu_button) {
                        report.menu_button = Some(sync_step_report(
                            BootstrapStepStatus::Unchanged,
                            BootstrapStepPhase::Check,
                            attempt_count,
                            Some(false),
                            Some(true),
                        ));
                        0
                    } else {
                        attempt_count
                    }
                }
                BootstrapRetryOutcome::Failed {
                    error,
                    attempt_count,
                } => {
                    report.menu_button = Some(sync_step_failure_report(
                        if policy.continue_on_failure {
                            BootstrapStepStatus::Warned
                        } else {
                            BootstrapStepStatus::Failed
                        },
                        BootstrapStepPhase::Check,
                        &error,
                        attempt_count,
                        Some(false),
                        Some(false),
                    ));
                    if policy.continue_on_failure {
                        0
                    } else {
                        return BootstrapOutcome::failure(report, error);
                    }
                }
            };
            if fetch_attempts > 0 {
                let set_request = set_menu_button_request(menu_button);
                match retry_step_blocking(policy, || self.call_advanced_retry_attempt(&set_request))
                {
                    BootstrapRetryOutcome::Success {
                        value,
                        attempt_count,
                    } => {
                        report.menu_button = Some(sync_step_report(
                            if value {
                                BootstrapStepStatus::Applied
                            } else {
                                BootstrapStepStatus::Succeeded
                            },
                            BootstrapStepPhase::Apply,
                            fetch_attempts + attempt_count,
                            Some(value),
                            Some(value),
                        ));
                    }
                    BootstrapRetryOutcome::Failed {
                        error,
                        attempt_count,
                    } => {
                        report.menu_button = Some(sync_step_failure_report(
                            if policy.continue_on_failure {
                                BootstrapStepStatus::Warned
                            } else {
                                BootstrapStepStatus::Failed
                            },
                            BootstrapStepPhase::Apply,
                            &error,
                            fetch_attempts + attempt_count,
                            Some(false),
                            Some(false),
                        ));
                        if !policy.continue_on_failure {
                            return BootstrapOutcome::failure(report, error);
                        }
                    }
                }
            }
        }

        BootstrapOutcome::success(report)
    }

    pub fn bootstrap(&self, plan: &BootstrapPlan) -> BootstrapOutcome {
        self.bootstrap_with_retry(plan, single_attempt_bootstrap_policy())
    }
}
