// SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
// SPDX-License-Identifier: BSD-2-Clause
//! Parse command-line options for the `u8loc` command-line tool.

use anyhow::{bail, Context as _, Result};
use clap::error::ErrorKind as ClapErrorKind;
use clap::Parser as _;
use clap_derive::Parser;

/// The top-level command-line parser.
#[derive(Debug, Parser)]
#[clap(version)]
struct Cli {
    /// Display the features supported by the program and exit.
    #[clap(long)]
    features: bool,

    /// Use a locale specified in the LANG and LC_* variables if appropriate.
    #[clap(short)]
    preferred: bool,

    /// Output the value of an environment variable.
    #[clap(short)]
    query: Option<String>,

    /// Run the specified program in a UTF-8-friendly environment.
    #[clap(short)]
    run: bool,

    /// The program to run if the `-r` flag is specified.
    program: Vec<String>,
}

/// The action to take as specified by the command-line arguments.
#[derive(Debug)]
pub enum Mode {
    Features,
    Handled,
    QueryEnv(String, bool),
    QueryList,
    QueryPreferred,
    Run(Vec<String>, bool),
}

/// Parse the command-line arguments, determine the operation mode.
///
/// # Errors
///
/// Propagate [`clap`] command-line parsing errors.
pub fn parse_args() -> Result<Mode> {
    let args = match Cli::try_parse() {
        Ok(args) => args,
        Err(err)
            if matches!(
                err.kind(),
                ClapErrorKind::DisplayHelp | ClapErrorKind::DisplayVersion
            ) =>
        {
            err.print()
                .context("Could not display the help or version output")?;
            return Ok(Mode::Handled);
        }
        Err(err) if err.kind() == ClapErrorKind::DisplayHelpOnMissingArgumentOrSubcommand => {
            err.print()
                .context("Could not display the usage or version message")?;
            bail!("Invalid or missing command-line options");
        }
        Err(err) => return Err(err).context("Could not parse the command-line options"),
    };
    let preferred = args.preferred;
    if let Some(query) = args.query {
        if args.run {
            bail!("Exactly one of the -q and -r options must be specified");
        }
        match &*query {
            "list" => Ok(Mode::QueryList),
            "preferred" => Ok(Mode::QueryPreferred),
            var @ ("LC_ALL" | "LANGUAGE") => Ok(Mode::QueryEnv(var.to_owned(), preferred)),
            other => bail!(format!("Invalid query name '{other}' specified")),
        }
    } else if args.run {
        if args.program.is_empty() {
            bail!("No program specified to run");
        }
        Ok(Mode::Run(args.program, preferred))
    } else if args.features {
        Ok(Mode::Features)
    } else {
        bail!("Exactly one of the -q and -r options must be specified");
    }
}
