use crate::ask;
extern crate custom_error;
use custom_error::custom_error;
use std::process::Command;
use std::str::FromStr;
use strum::{EnumProperty, IntoEnumIterator};
use strum_macros;
custom_error! {pub ExampleError
    ExampleNotFound{name:String} = "Unknown example: {name}"
}

#[derive(
    strum_macros::EnumProperty,
    strum_macros::EnumString,
    strum_macros::EnumIter,
    strum_macros::Display,
    Debug,
)]
#[allow(dead_code)]
enum Example {
    #[strum(props(Name = "Ask into a variable"))]
    Ask,
    #[strum(props(Name = "Confirm inline"))]
    Confirm,
    #[strum(props(Name = "Confirm into variable true/false"))]
    ConfirmTrueFalse,
    #[strum(props(Name = "Choose a single option from an inline list"))]
    ChooseInline,
    #[strum(props(Name = "Choose a single option from a bash array"))]
    ChooseArray,
    #[strum(props(Name = "Select multiple options into a bash array"))]
    SelectArray,
    #[strum(props(Name = "Date picker between two dates"))]
    DatePick,
    #[strum(props(Name = "Editor for free form text box converted to JSON"))]
    Editor,
    #[strum(props(Name = "Menu of shell commands to execute"))]
    Menu,
}

pub fn choose_example() -> Result<String, ExampleError> {
    let options: Vec<&str> = Example::iter()
        .map(|e| e.get_str("Name").unwrap())
        .collect();
    let choice = ask::choose("Choose an example", "", options, &true, 1).parse::<usize>();
    match choice {
        Ok(i) => match Example::iter().nth(i) {
            Some(ex) => example(ex.to_string().as_str()),
            None => panic!("invalid choice: not a valid example"),
        },
        Err(_) => panic!("invalid choice: not a number"),
    }
}

pub fn run_bash(src: &'static str) -> String {
    Command::new("/bin/bash")
        .args(["-c", src])
        .status()
        .unwrap();
    format!("\n{}", src)
}

pub fn example(name: &str) -> Result<String, ExampleError> {
    match name {
        "" => Ok(choose_example().unwrap().to_string()),
        s => match Example::from_str(s) {
            Ok(ex) => match ex {
                Example::Ask => Ok(run_bash(example_ask())),
                Example::Confirm => Ok(run_bash(example_confirm_inline())),
                Example::ConfirmTrueFalse => Ok(run_bash(example_confirm_true_false())),
                Example::ChooseInline => Ok(run_bash(example_choose_inline())),
                Example::ChooseArray => Ok(run_bash(example_choose_array())),
                Example::SelectArray => Ok(run_bash(example_select_array())),
                Example::DatePick => Ok(run_bash(example_date_pick())),
                Example::Editor => Ok(run_bash(example_editor())),
                Example::Menu => Ok(run_bash(example_menu())),
            },
            Err(_) => Err(ExampleError::ExampleNotFound {
                name: name.to_string(),
            }),
        },
    }
}

pub fn example_ask() -> &'static str {
    r#"#!/bin/bash
## Shorter alias for the script-wizard ask command:
ask() { script-wizard ask "$@"; }

## Ask a question and capture the response into the NAME variable:
NAME=$(ask "What is your name?")

echo "Hi there ${NAME}"
"#
}

pub fn example_confirm_inline() -> &'static str {
    r#"#!/bin/bash
## Shorter alias for the script-wizard confirm command:
confirm() { script-wizard confirm "$@"; }

## Ask a confirmation question with the default answer being yes:
if confirm "Do you like Linux?" yes; then
  echo "Tux is great!"
else
  echo "Well, thats ok."
fi
"#
}

pub fn example_confirm_true_false() -> &'static str {
    r#"#!/bin/bash
## Shorter alias for the script-wizard confirm command:
confirm() { script-wizard confirm "$@"; }

## Example confirm into a variable as literal true/false:
LIKES_LINUX=$(confirm "Do you like Linux?" yes && echo "true" || echo "false")

if [[ "${LIKES_LINUX}" == "true" ]]; then
    echo "$USER likes Linux"
else
    echo "$USER does not like Linux"
fi
"#
}

pub fn example_choose_inline() -> &'static str {
    r#"#!/bin/bash
## Shorter alias for script-wizard choose command:
choose() { script-wizard choose "$@"; }

## Example of choosing a single option from an inline list:
CHOSEN_CLASS=$(choose "Select your character class" \
                "Rogue" "Wizard" "Paladin" "Cleric" "Bard")

echo "Chosen class: ${CHOSEN_CLASS}"
"#
}

pub fn example_choose_array() -> &'static str {
    r#"#!/bin/bash
## Shorter alias for script-wizard choose command:
choose() { script-wizard choose "$@"; }

## Example of choosing a single option from a bash array:
options=("red" "blue" "greenish orange" "purple")
COLOR=$(choose "Choose a color" "${options[@]}")
echo "You chose ${COLOR}"
"#
}

pub fn example_select_array() -> &'static str {
    r#"#!/bin/bash
readarray -t SELECTED < <(script-wizard select "Which games do you like?" "Rocket League" "Portal" "Quake" "Magic the Gathering")

echo "These are the games you said you like, printed on separate lines:"
printf '%s\n' "${SELECTED[@]}"
echo
echo "Games you like, printed on one row: ${SELECTED[@]@Q}"
"#
}

pub fn example_date_pick() -> &'static str {
    r#"#!/bin/bash
# Pick a date between 2023/10/01 and 2023/10/20:
DATE=$(script-wizard date "Enter a date" --week-start monday --format "%Y-%m-%d" --min-date "2023-10-01" --max-date "2023-10-20" --help-message "yadda yadda")

echo "The date you chose: ${DATE}"
"#
}

pub fn example_editor() -> &'static str {
    r#"#!/bin/bash
BIOGRAPHY=$(script-wizard editor "Tell me alllll about yourself" --default "Describe yourself" --json | sed 's/^[^\"]*//')

echo "This is what you told me about yourself:"
echo "${BIOGRAPHY}"
"#
}

pub fn example_menu() -> &'static str {
    r#"#!/bin/bash
script-wizard menu "main menu" \
  "print username = whoami" \
  "print all users = cat /etc/passwd | cut -d ':' -f 1" \
  "exit = exit 2"
"#
}
