use anyhow::Context as _;
use clap::{Parser, Subcommand};

use crate::metadata::Instance;

mod iproute2;
mod metadata;

fn main() {
    common::entrypoint(program);
}

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct Args {
    #[command(subcommand)]
    pub command: Commands,
}

#[derive(Debug, Subcommand)]
pub enum Commands {
    /// Configures the ipv6 address using instance metadata and iproute2
    Configure,
}

fn program() -> anyhow::Result<()> {
    let args = Args::parse();
    match args.command {
        Commands::Configure => configure(),
    }
}

fn configure() -> anyhow::Result<()> {
    let metadata_api = common::env::read_env("INSTANCE_API_URI")
        .unwrap_or(String::from("http://169.254.169.254/hetzner/v1/metadata"));
    let metadata = common::curl::read_text_as_string(&metadata_api)?;
    let metadata: Instance = common::yaml::from_str(&metadata)
        .context("Could not parse instance metadata into expected format")?;
    let link_names = iproute2::get_link_names()?;
    for m in metadata.network_config.config {
        for subnet in m.subnets {
            match subnet {
                metadata::InstanceNetworkConfigConfigSubnet::Static {
                    ipv6,
                    ipv4,
                    address,
                    gateway,
                } => {
                    if ipv4.is_some_and(|v| v) {
                        continue;
                    }
                    if !ipv6.is_some_and(|v| v) {
                        continue;
                    }

                    // Apparently the configuration used renames the eth0 device to enp1s0, I should really figure out why this happens, but for now
                    // this will do.
                    let dev_name = if m.name == "eth0" && !link_names.contains("eth0") {
                        "enp1s0"
                    } else {
                        &m.name
                    };
                    if !iproute2::has_ipv6_address(dev_name, &address)? {
                        let mut cmd = common::proc::Command::new("ip");
                        cmd.args(["-6", "addr", "add", &address, "dev", dev_name]);
                        cmd.try_spawn_to_string()?;
                    }
                    if !iproute2::has_default_route(dev_name, &gateway)? {
                        let mut cmd = common::proc::Command::new("ip");
                        cmd.args([
                            "-6", "route", "add", "default", "via", &gateway, "dev", dev_name,
                        ]);
                        cmd.try_spawn_to_string()?;
                    }
                }
                metadata::InstanceNetworkConfigConfigSubnet::Dhcp {} => continue,
            }
        }
    }
    Ok(())
}
