// Copyright 2025 Heath Stewart.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.

use super::VAULT_ENV_NAME;
use crate::credential;
use akv_cli::Result;
use azure_core::http::Url;
use azure_security_keyvault_secrets::{models::SecretClientGetSecretOptions, SecretClient};
use clap::{ArgGroup, Parser};
use std::{
    fs,
    io::{self, Write},
    path::PathBuf,
};
use tracing::{Level, Span};

#[derive(Debug, Parser)]
#[command(group(ArgGroup::new("ident").args(&["id", "name"]).required(true)))]
pub struct Args {
    /// The secret URL e.g., "https://my-vault.vault.azure.net/secrets/my-secret".
    #[arg(value_name = "URL", conflicts_with_all = ["name", "version"])]
    id: Option<Url>,

    /// The secret name.
    #[arg(long, requires = "vault")]
    name: Option<String>,

    /// The vault URL e.g., "https://my-vault.vault.azure.net".
    #[arg(long, value_name = "URL", env = VAULT_ENV_NAME)]
    vault: Option<Url>,

    /// Optional secrets version.
    #[arg(long, requires = "name")]
    version: Option<String>,

    /// Do not print a new line after the secret.
    #[arg(short = 'n', long)]
    no_newline: bool,

    /// Write the secret to a file instead of stdout.
    #[arg(short = 'o', long, value_name = "PATH")]
    out_file: Option<PathBuf>,

    /// Force overwriting an existing file.
    #[arg(short = 'f', long)]
    force: bool,
}

impl Args {
    #[tracing::instrument(level = Level::INFO, skip(self), fields(vault, name, version), err)]
    pub async fn read(&self) -> Result<()> {
        let (vault, name, version) = super::select(
            self.id.as_ref(),
            self.vault.as_ref(),
            self.name.as_ref(),
            self.version.as_ref(),
        )?;

        let current = Span::current();
        current.record("vault", &*vault);
        current.record("name", &*name);
        current.record("version", version.as_deref());

        let client = SecretClient::new(&vault, credential()?, None)?;
        let secret = client
            .get_secret(
                &name,
                Some(SecretClientGetSecretOptions {
                    secret_version: version.map(Into::into),
                    ..Default::default()
                }),
            )
            .await?
            .into_model()?;
        tracing::debug!("retrieved {:?}", &secret);

        if let Some(value) = secret.value {
            match self.out_file.as_ref() {
                // Write to a file.
                Some(path) => {
                    let mut file = fs::OpenOptions::new()
                        .create(true)
                        .write(true)
                        .truncate(true)
                        .create_new(!self.force)
                        .open(path)?;
                    file.write_all(value.as_bytes())?;
                }
                // Print to stdout without a newline.
                _ if self.no_newline => {
                    print!("{value}");
                    io::stdout().flush()?;
                }
                // Print line to stdout.
                _ => println!("{value}"),
            }
        }

        Ok(())
    }
}
