use anyhow::{Result, bail};
use mullvad_management_interface::MullvadProxyClient;
use mullvad_types::{
    constraints::Constraint,
    relay_constraints::{RelayConstraints, RelaySettings},
};

#[derive(clap::Subcommand, Debug)]
pub enum DebugCommands {
    /// Block all internet connection by setting an invalid relay constraint.
    BlockConnection,
    /// Relay
    #[clap(subcommand)]
    Relay(RelayDebugCommands),
    /// Handy commands for interacting with the app release rollout system.
    #[clap(subcommand)]
    Rollout(RolloutDebugCommands),
}

#[derive(clap::Subcommand, Debug)]
pub enum RelayDebugCommands {
    /// Inactivate this _category of relays_ - a category can be one of the following: a relay, a
    /// city, a country.
    Disable { relay: String },
    /// (Re)Activate this _category of relays_ - a category can be one of the following: a relay, a
    /// city, a country.
    Enable { relay: String },
}

#[derive(clap::Subcommand, Debug)]
pub enum RolloutDebugCommands {
    /// Print your rollout threshold.
    Get,
    /// Generate a new rollout threshold (overwrites the current threshold value)
    Reroll,
    /// Set your rollout threshold seed to a known value.
    ///
    /// The seed is used to generate a rollout threshold.
    Seed { value: u32 },
}

impl DebugCommands {
    pub async fn handle(self) -> Result<()> {
        match self {
            DebugCommands::BlockConnection => {
                let mut rpc = MullvadProxyClient::new().await?;
                let settings = rpc.get_settings().await?;

                let relay_settings = settings.get_relay_settings();
                let mut constraints = match relay_settings {
                    RelaySettings::Normal(normal) => normal,
                    RelaySettings::CustomTunnelEndpoint(_custom) => {
                        println!("Removing custom relay settings");
                        RelayConstraints::default()
                    }
                };
                constraints.location = Constraint::Only(
                    mullvad_types::relay_constraints::LocationConstraint::Location(
                        mullvad_types::relay_constraints::GeographicLocationConstraint::Country(
                            "xx".into(),
                        ),
                    ),
                );
                rpc.set_relay_settings(RelaySettings::Normal(constraints))
                    .await?;

                rpc.connect_tunnel().await?;

                eprintln!("WARNING: ENTERED BLOCKED MODE");
                Ok(())
            }
            DebugCommands::Relay(RelayDebugCommands::Disable { relay }) => {
                let mut rpc = MullvadProxyClient::new().await?;
                rpc.disable_relay(relay.clone()).await?;
                println!("{relay} is now marked as inactive");
                Ok(())
            }
            DebugCommands::Relay(RelayDebugCommands::Enable { relay }) => {
                let mut rpc = MullvadProxyClient::new().await?;
                rpc.enable_relay(relay.clone()).await?;
                println!("{relay} is now marked as active");
                Ok(())
            }
            DebugCommands::Rollout(rollout_cmd) => rollout_cmd.handle().await,
        }
    }
}

impl RolloutDebugCommands {
    pub async fn handle(self) -> Result<()> {
        let mut rpc = MullvadProxyClient::new().await?;
        match self {
            RolloutDebugCommands::Get => {
                let Ok(threshold) = rpc.get_rollout_threshold().await else {
                    bail!("Failed to get rollout");
                };
                println!("{threshold}");
                Ok(())
            }
            RolloutDebugCommands::Reroll => {
                let Ok(threshold) = rpc.generate_new_rollout_threshold().await else {
                    bail!("Failed to get rollout");
                };
                println!("{threshold}");
                Ok(())
            }
            RolloutDebugCommands::Seed { value: seed } => {
                if rpc.set_new_rollout_threshold_seed(seed).await.is_err() {
                    bail!("Failed to update rollout seed");
                }
                Ok(())
            }
        }
    }
}
