#![deny(missing_docs)]
// SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
// SPDX-License-Identifier: BSD-2-Clause
//! u8loc - run a command in a UTF-8-capable locale

use std::collections::HashMap;
use std::env;
use std::os::unix::process::CommandExt as _;
use std::process::Command;

use anyhow::{Context as _, Error as AnyError, Result};
use utf8_locale::{LanguagesDetect, Utf8Detect};

use cli::Mode;

mod cli;

fn show_features() -> Vec<String> {
    vec![format!(
        "Features: u8loc={ver} query-env=0.1 query-preferred=0.1 run=0.1",
        ver = env!("CARGO_PKG_VERSION")
    )]
}

fn get_env(preferred: bool) -> Result<HashMap<String, String>> {
    let det = if preferred {
        let langs = LanguagesDetect::new()
            .detect()
            .context("Could not determine the list of preferred languages")?;
        Utf8Detect::new().with_languages(langs)
    } else {
        Utf8Detect::new()
    };
    Ok(det.detect().context("Could not detect a UTF-8 locale")?.env)
}

fn query_env(name: &str, preferred: bool) -> Result<Vec<String>> {
    let value = {
        let mut env = get_env(preferred)?;
        env.remove(name)
            .with_context(|| format!("Internal error: {name:?} should be present in {env:?}"))?
    };
    Ok(vec![value])
}

fn query_list() -> Vec<String> {
    vec![
        "LANGUAGE  - The LANGUAGE environment variable".to_owned(),
        "LC_ALL    - The LC_ALL environment variable".to_owned(),
        "list      - List the available query parameters".to_owned(),
        "preferred - List the preferred languages as per the locale variables".to_owned(),
    ]
}

fn query_preferred() -> Result<Vec<String>> {
    LanguagesDetect::new()
        .detect()
        .context("Could not determine the list of preferred languages")
}

fn run_program(prog: &[String], preferred: bool) -> Result<Vec<String>> {
    let env = get_env(preferred)?;
    let (prog_name, args) = prog.split_first().context("Not even a program name?")?;
    Err(AnyError::new(
        Command::new(prog_name)
            .args(args)
            .env_clear()
            .envs(env)
            .exec(),
    )
    .context(format!("Could not execute the {prog_name} program")))
}

fn run() -> Result<Option<Vec<String>>> {
    match cli::parse_args()? {
        Mode::Features => Ok(Some(show_features())),
        Mode::Handled => Ok(None),
        Mode::QueryEnv(ref name, ref preferred) => query_env(name, *preferred).map(Some),
        Mode::QueryList => Ok(Some(query_list())),
        Mode::QueryPreferred => query_preferred().map(Some),
        Mode::Run(ref prog, ref preferred) => run_program(prog, *preferred).map(Some),
    }
}

#[allow(clippy::print_stdout)]
fn main() -> Result<()> {
    if let Some(res) = run()? {
        println!("{res}", res = res.join("\n"));
    }
    Ok(())
}
