use anyhow::Context;

const KEYRING_SERVICE: &str = "bv-cli";

pub fn resolve_github_token(flag: Option<&str>, non_interactive: bool) -> anyhow::Result<String> {
    if let Some(t) = flag {
        return Ok(t.to_string());
    }
    if let Some(t) = try_gh_auth_token() {
        return Ok(t);
    }
    if let Some(t) = keyring_get("github-token") {
        return Ok(t);
    }
    if non_interactive {
        anyhow::bail!(
            "no GitHub token found\n  \
             bv publish needs a GitHub token with `repo` and `write:packages` scopes to push the image to GHCR and open the registry PR.\n  \
             Get one at https://github.com/settings/tokens/new?scopes=repo,write:packages&description=bv-publish\n  \
             Then either:\n    \
                 - export GITHUB_TOKEN=<token>\n    \
                 - pass --github-token <token>\n    \
                 - run `gh auth login` (if you have the GitHub CLI)"
        );
    }
    eprintln!("  bv publish needs a GitHub token with `repo` and `write:packages` scopes.");
    eprintln!(
        "  Generate one at: https://github.com/settings/tokens/new?scopes=repo,write:packages&description=bv-publish"
    );
    eprintln!("  Or run `gh auth login` and re-run this command.");
    let token = prompt_token("Paste GitHub token")?;
    if inquire::Confirm::new("Save token to OS keychain for future use?")
        .with_default(true)
        .prompt()
        .unwrap_or(false)
    {
        keyring_set("github-token", &token);
    }
    Ok(token)
}

pub fn resolve_ghcr_token(flag: Option<&str>, github_token: &str) -> String {
    if let Some(t) = flag {
        return t.to_string();
    }
    if let Ok(t) = std::env::var("GHCR_TOKEN")
        && !t.is_empty()
    {
        return t;
    }
    if let Some(t) = keyring_get("ghcr-token") {
        return t;
    }
    github_token.to_string()
}

fn try_gh_auth_token() -> Option<String> {
    let out = std::process::Command::new("gh")
        .args(["auth", "token"])
        .output()
        .ok()?;
    if out.status.success() {
        let t = String::from_utf8_lossy(&out.stdout).trim().to_string();
        if t.is_empty() { None } else { Some(t) }
    } else {
        None
    }
}

fn keyring_get(key: &str) -> Option<String> {
    let entry = keyring::Entry::new(KEYRING_SERVICE, key).ok()?;
    match entry.get_password() {
        Ok(t) if !t.is_empty() => Some(t),
        _ => None,
    }
}

fn keyring_set(key: &str, token: &str) {
    if let Ok(entry) = keyring::Entry::new(KEYRING_SERVICE, key) {
        let _ = entry.set_password(token);
    }
}

fn prompt_token(prompt: &str) -> anyhow::Result<String> {
    inquire::Password::new(prompt)
        .without_confirmation()
        .prompt()
        .context("failed to read token from terminal")
}
