---
# Create and configure default users for all machines.

## Variables ##

- name: General | Account Management | Users | Use BASH (Default)
  set_fact:
    user_shell: "{{ bash_exec.stdout }}"

- name: General | Account Management | Users | Allow BASH (Arch)
  lineinfile:
    path: /etc/shells
    regexp: "{{ bash_exec.stdout }}"
    line: "{{ bash_exec.stdout }}"
    insertbefore: "# End of file"
    backup: yes
    create: no
    state: present
  when: ansible_distribution == "Archlinux"

#- name: General | Account Management | Users | Use ZSH (Arch+Manjaro)
#  set_fact:
#    user_shell: "{{ zsh_exec.stdout }}"
#  when: ansible_distribution == "Archlinux"
#
#- name: General | Account Management | Users | Allow ZSH (Arch)
#  lineinfile:
#    path: /etc/shells
#    regexp: "{{ zsh_exec.stdout }}"
#    line: "{{ zsh_exec.stdout }}"
#    insertbefore: "# End of file"
#    backup: yes
#    create: no
#    state: present
#  when: ansible_distribution == "Archlinux"


## Root ##

- name: General | Account Management | Users | Root
  user:
    name: root
    shell: "{{ bash_exec.stdout }}"
    create_home: yes
    generate_ssh_key: yes
  register: user_root


## Scheduler ##

- name: General | Account Management | Users | Ansible
  user:
    name: ansible
    comment: Ansible
    system: yes
  register: user_ansible


## Superuser ##

- name: General | Account Management | Users | User
  user:
    name: "{{ user }}"
    comment: "{{ user_desc }}"
    groups:
      - sudo
      - video
      - render
      - wheel
    append: yes
    shell: "{{ user_shell }}"
    create_home: yes
    generate_ssh_key: yes
  register: user_user

- name: General | Account Management | Users | User | Test Logging In
  shell: "echo SUCCESS"
  args:
    executable: "{{ user_shell }}"
  become_user: "{{ user }}"


## Folders ##

- name: General | Account Management | Users | Root | Create Folders
  file:
    path: "{{ item }}"
    state: directory
    mode: '0755'
  loop:
    - "{{ user_root.home }}/bin"
    - "{{ user_root.home }}/Downloads"
    - "{{ user_root.home }}/TRASH"
  when: user_root.home != ""

- name: General | Account Management | Users | User | Create Folders
  file:
    path: "{{ item }}"
    state: directory
    mode: '0755'
  loop:
    - "{{ user_user.home }}/bin"
    - "{{ user_user.home }}/LBRY"
    - "{{ user_user.home }}/TRASH"
    - "{{ user_user.home }}/Downloads"
    - "{{ user_user.home }}/Reports"
  become_user: "{{ user }}"
  when: user_user.home != ""

- name: General | Account Management | Users | User | Create Folders | Coding
  file:
    path: "{{ item }}"
    state: directory
    mode: '0755'
  loop:
    - "{{ user_user.home }}/Code"
  become_user: "{{ user }}"
  when: user_user.home != "" and coding == true

- name: General | Account Management | Users | Home Permissions
  shell: "chmod 700 {{ user_user.home }}/../*"


## Files ##

- name: General | Account Management | Users | Files | RC Variables
  set_fact:
    alias_cp: alias cp='cp -v'
    alias_mv: alias mv='mv -v'
    alias_rm: alias rm='echo "Use mv ~/TRASH/ instead!"'
    export_path_additions: export PATH="~/bin:"{{ global_bin }}":$PATH"
    function_wttr: |
      function weather() {
        # 20210301 - Someone showed me an awesome weather API! Had to implement it!
        if [[ "$1" == "-"* || $2 != "" ]]; then
          echo 'USAGE: weather [location]
        Any "-" paramaters call the usage since this function does not take any options.
        Location is optional since the API can determine your connection'"'"'s location.
        Useful location types:
          $zip_code              | Ex: 12345
          $city,$state           | Ex: Austin,Texas
          @$domain_dot_extension | Ex: @gitea.com
          ~$special_location     | Ex: ~Manitou Incline
        Full documentation: https://github.com/chubin/wttr.in'
          return 1
        fi
        curl "https://wttr.in/${1//\ /+}"
      }
    export_PS1: export PS1='[\u@\h \w]\$ '
    alias_remount: |
      alias remount='
        sudo umount /mnt/*
        sudo umount /mnt/*/*
        sudo mount -a
        echo -e "\nRemount completed!"
        mount | grep /mnt
      '
    function_update: |
      function update() {
        PROG=$FUNCNAME
        usage="Usage: $PROG [-y]
        $PROG is used to run all the system's package manager commands
          in one swoop. Flow stops if any command returns a failure code.
          The hope is to run something as easy as 'pacman -Syyu'.
        -y : Assume yes to any prompts.
        -g : Shutdown after updating.
        -s : System updates only, no Flatpaks.
        -f : Flatpaks only, no system updates."

        unset OPTIND
        unset accept
        unset goodbye
        unset only_sys
        unset only_flat

        while getopts ":hygsf" opt; do
            case $opt in
              h) echo -e "$usage"
                return 0 ;;
              y) accept="-y" ;;
              g) goodbye="Y" ;;
              s) only_sys="Y" ;;
              f) only_flat="Y" ;;
              *) echo "ERROR: -$OPTARG is not a recognized option." >&2
                echo -e "$usage"
                return 1 ;;
            esac
        done

        if [[ "$only_flat" == "Y" ]]; then
          echo -e "\n*** Only Flatpaks - Skipping System Updates ***\n\n"
        else
          {{ update_package_manager }}
        fi

        if [[ "$goodbye" == "Y" && "{{ battery }}" == "True" ]]; then
          echo -e "\n*** Only System Updates - Skipping Flatpak ***\n\n"
        elif [[ "$only_sys" == "Y" ]]; then
          echo -e "\n*** Manually Skipping Flatpak ***\n\n"
        else
          {{ update_flatpak }}
        fi

        echo "*** Completed Successfully ***"

        if [[ $goodbye == "Y" ]]; then
          bye
        fi

        return 0
      }
    function_update_firmware: |
      function update-firmware() {
        PROG=$FUNCNAME
        usage="Usage: $PROG [-y]
        $PROG is used to run the firmware updater in one swoop. Flow stops if
          any command returns a failure code. The hope is to run something as
          easy as 'pacman -Syyu' but for non-Arch systems.
        -y : Assume yes to any prompts."

        unset OPTIND
        unset accept
        while getopts ":hy" opt; do
            case $opt in
              h) echo -e "$usage"
                return 0 ;;
              y) accept="-y" ;;
              *) echo "ERROR: -$OPTARG is not a recognized option." >&2
                echo -e "$usage"
                return 1 ;;
            esac
        done

        {{ update_firmware }}
        echo "*** Completed Successfully ***"
        return 0
      }
    alias_sync: alias sync='date && echo "Syncing!" && sync && date'
    export_editor: export EDITOR='vi'
    init_aliases: |
      alias init-video='
        mkdir -v raw
        mkdir -v exports
        cp ~/Templates/*video* ./
      '
      alias init-vid=init-video
      alias init-program='
        echo "#!/usr/bin/env bash"
        echo "# `date +%Y-%m-%d` Hyperling"
        echo ""
        echo "exit 0"
      '
      alias init-prog=init-program
    bye_aliases: |
      alias bye="{{ shutdown_command }}"
      alias goodbye="update -yg"
    metasploit_aliases: |
      alias metasploit="msfconsole"
      alias hax="metasploit"
    show_config_aliases: |
      alias show-config-gen="cat {{ gen_file }}"
      alias show-config-wrk="cat {{ wrk_file }}"
      alias show-config-mnr="cat {{ mnr_file }}"
      alias show-config-srv="cat {{ srv_file }}"
      alias show-config-all="
        show-config-gen &&
        echo '' &&
        show-config-wrk &&
        echo '' &&
        show-config-mnr &&
        echo '' &&
        show-config-srv
      "
      alias show-config="show-config-all"
    edit_config_aliases: |
      alias edit-config-gen="sudo $EDITOR {{ gen_file }}"
      alias edit-config-wrk="sudo $EDITOR {{ wrk_file }}"
      alias edit-config-mnr="sudo $EDITOR {{ mnr_file }}"
      alias edit-config-srv="sudo $EDITOR {{ srv_file }}"
    function_check_trash: |
      function check-trash() {
        unset OPTIND
        unset clean
        unset network
        du_params="-ha"
        while (( $# > 0 )); do
          case $1 in
            -c | -y | --clean )
              clean="Y" ;;
            -n | -net | --network )
              network="Y" ;;
            -s | -sum | --summarize )
              du_params="-sh" ;;
            * )
              echo "
                ERROR: Option '$1' with value '$2' not recognized.
                $PROG [-c | -y | --clean] [-n | -net | --network] \
                [-s | -sum | --summarize]
              " >&2
              return 1
              ;;
          esac
          shift
        done
        echo "clean=$clean"
        echo "network=$network"
        echo "Grabbing sudo permissions..."
        sudo echo "Success! Starting search..."
        function dirs_to_check {
          echo "/root 0"
          echo "/home 4"
          echo "/media 0"
        }
        dirs_to_check | while read dir depth; do
          if [[ "$depth" != 0 ]]; then
            maxdepth="-maxdepth $depth"
          fi
          sudo="sudo"
          if [[ "$dir" == "/media" ]]; then
            sudo=""
            dir="$dir/$LOGNAME"
          fi
          echo "Checking $dir..."
          $sudo find $dir -name TRASH | while read trash; do
            if [[ "$trash" != "" && `$sudo ls -a $trash` ]]; then
              echo "Found $trash:"
              $sudo du $du_params $trash | sort -h
              if [[ "$clean" == "Y" ]]; then
                echo "Cleaning trash..."
                $sudo sh -c "cd $trash; rm -rfv ..?* .[!.]* *"
              fi
            fi
          done
          $sudo find $dir $maxdepth -name "*"Trash"*" | while read trash; do
            if [[ "$trash" != "" && `$sudo ls -a $trash` ]]; then
              echo "Found $trash:"
              $sudo du $du_params $trash | sort -h
              if [[ "$clean" == "Y" ]]; then
                echo "Cleaning trash..."
                $sudo sh -c "cd $trash; rm -rfv ..?* .[!.]* *"
              fi
            fi
          done
        done
        if [[ "$network" == "Y" ]]; then
          function network_to_check {
            find /mnt -maxdepth 1 -mindepth 1
          }
          network_to_check | while read dir; do
            echo "Checking $dir..."
            sudo find $dir -name TRASH | while read trash; do
              if [[ "$trash" != "" && `sudo ls -a $trash` ]]; then
                echo "Found $trash:"
                sudo du $du_params $trash | sort -h
                if [[ "$clean" == "Y" ]]; then
                  echo "Cleaning trash..."
                  sudo sh -c "cd $trash; rm -rfv ..?* .[!.]* *"
                fi
              fi
            done
            sudo find $dir -name .Trash"*" | while read trash; do
              if [[ "$trash" != "" && `sudo ls -a $trash` ]]; then
                echo "Found $trash:"
                sudo du $du_params $trash | sort -h
                if [[ "$clean" == "Y" ]]; then
                  echo "Cleaning trash..."
                  sudo sh -c "cd $trash; rm -rfv ..?* .[!.]* *"
                fi
              fi
            done
          done
        fi
        echo "Checking but not cleaning /var/mail..."
        du -ha /var/mail | sort -h
        return 0
      }
      alias check_trash="check-trash"
    alias_clean_trash: alias clean-trash='check-trash --clean'
    alias_trash_check: alias trash-check='check-trash'
    alias_trash_clean: alias trash-clean='trash-check --clean'
    alias_clean_dir: |
      function clean-dir() {
        clean_dir="`date '+%Y%m%d_%H%M%S'`_CLEANED"
        trash_dir="$HOME/TRASH/$clean_dir"
        curr_dir="`pwd`"
        mkdir -pv "$trash_dir"
        echo "$curr_dir is being cleaned at `date`." | tee "$trash_dir"/INFO.txt
        mv -v ..?* .[!.]* * "$trash_dir"/ | tee -a "$trash_dir"/INFO.txt
      }
    function_clean: |
      function clean() {
        sudo du -hs
        clean-dir
        sudo du -hs
      }
    function_flatpak_usage: |
      function flatpak-usage() {
        flatpak list --columns=application | while read app; do
          size=`flatpak info -s $app 2>/dev/null`
          if [[ ! -z $size ]]; then
            mb=$(( size / (1000*1000) ))
            echo "${mb} MB, $size Bytes, $app"
          fi
        done | sort -n
      }
    function_flatpak_purge: |
      function flatpak-purge() {
        flatpak remove --all --delete-data &&
        flatpak repair &&
        echo -n "Finished purging all Flatpak apps. " &&
        echo "Executable may still need uninstalled." &&
        return
        echo "ERROR: Something went wrong while removing Flatpak apps!" >&2
      }
    alias_vim: alias vi=vim
    alias_here: alias here='ls -alh `pwd`/*'
    alias_docker_reload: |
      alias docker-reload='
        docker compose down &&
        docker compose build &&
        docker compose up -d
      '
    alias_docker_update: |
      alias docker-update='
        docker compose down &&
        docker compose pull &&
        docker compose build &&
        docker compose up -d
      '
    function_docker_upgrade: |
      function docker-upgrade() {
        # Wrapper for a full-scale upgrade and log view of a container.
        # Paramaters:
        #   1) Container ID or Container Name, as seen in 'docker ps' command.
        container=$1
        if [[ -z $container ]]; then
          echo "ERROR: Container name or ID is required." >&2
          return;
        fi
        if [[ -n $2 ]]; then
          echo "ERROR: A second parameter is not expected, aborting." >&2
          return;
        fi
        # Ensure the container exists. Should be found even if stopped.
        exists=`docker ps | grep -c $container`
        if [[ $exists != "1" ]]; then
          echo "ERROR: Container '$container' was not found." >&2
          echo "Please choose from the available list:"
          docker ps
          return;
        fi
        echo "*** Going Down ***" &&
          docker compose down &&
        echo "*** Upgrading Images ***" &&
          docker compose pull &&
        echo "*** Building Containers ***" &&
          docker compose build &&
        echo "*** Starting Daemons ***" &&
          docker compose up -d &&
        echo "*** Following Log ***" &&
          echo "Press ^C to escape." &&
          docker logs -f $container
      }
    alias_docker_restart: |
      alias docker-restart='docker compose down && docker compose up -d'
    alias_code_check: |
      alias code-check='
        echo "Checking ~/Code directory for git changes."
        ls -d ~/Code/* | while read project; do
          if [[ ! -d $project ]]; then
            continue
          fi
          echo -e "\n*** `basename $project` ***"
          cd $project
          if [[ -d .git ]]; then
            git pull
            git push
          else
            echo "Not a Git project, skipping!"
          fi
        done
        echo -e "\nDone!"
      '
    alias_code_reset: |
      alias code-reset='
        ls -d ~/Code/* | while read project
          do echo "*** `basename $project` ***"
          cd $project
          git stash
          git switch main
          git pull
          git branch -D dev
          git checkout dev
        done
      '
    function_code_reseed: |
      function git_projects_to_sync {
        cat <<- EOF
          env-ansible
          env-docker
          env-termux
          nodejs-website
          android-break-the-habit
          android-tictactoe
          android-carb-up
          ebook-health-protocol
          flutter-expense-tracker
          flutter-social-traveler-app
          nodejs-social-traveler-server
      EOF
      }
      function code-reseed {
        unseed_dir="$HOME/TRASH/`date ++%Y%m%d_%H%M%S`_UnseededCodeProjects"
        mkdir -pv "$unseed_dir"
        mv -v ~/Code/* "$unseed_dir"/ 2>/dev/null
        git_repo_ssh={{ git_repo_ssh }}
        git_main_project={{ git_project }}
        git_repo_ssh_basename=${git_repo_ssh//$git_main_project/}
        git_projects_to_sync | while read git_project; do
          dest="$git_project"
          if [[ "$dest" == "flutter-*" ]]; then
            dest="${dest//-/_}"
          fi
          git clone ${git_repo_ssh_basename}${git_project} \
            --branch dev ~/Code/$dest
        done
      }
    function_clean_filenames: |
      function clean-filenames() {
        # Must provide the directory you'd like to clean all the filenames in.
        # Otherwise defaults to the current directory and all of its files.
        dir="$1"
        if [[ -z $dir ]]; then
          echo -e "Using current directory."
          dir="."
        fi
        ls "$dir" | while read file; do
          clean="${file//IMG/}"
          clean="${clean//_/}"
          clean="${clean//-/}"
          clean="${clean// /}"
          if [[ "$file" != "$clean" && ! -d "$file" ]]; then
            mv -v "$dir"/"$file" "$dir"/"$clean"
          fi
        done
      }
    function_clean_filenames_tree: |
      function clean-filenames-tree() {
        find ./ | while read folder; do
          if [[ -d "$folder" ]]; then
            echo -e "\n*** Checking '$folder' ***"
            clean-filenames "$folder"
          fi
        done
        echo -e "\nDone!\n"
      }
    alias_clone: |
      alias clone="rsync -auPhz --delete --exclude '.gradle' --exclude 'app/build'"
    export_hyperling: |
      export HYPERLING6="2a07:e03:3:80::1"
      export HYPERLING4="185.130.47.173"
      export HYPERLING="$HYPERLING4"
    source_docker_env: |
      DOCKER_SOURCE="/opt/Docker/source.env"
      if [[ -e $DOCKER_SOURCE && $LOGNAME == "root" ]]; then
        source $DOCKER_SOURCE
      fi
    alias_scan: |
      alias scan="nmap -A -p- --script=vuln"
    alias_prod: |
      alias prod="ssh -p {{ prod_port }} {{ prod_user }}@{{ prod_host }}"
    function_clean_code: |
      function clean-code {
        echo -e "******* Android *******\n*** Build Caches ***"
        find ~/Code/android-*/app -maxdepth 1 -type d -name "build" \
          -exec du -hs {} \; -exec rm -rf {} \;
        echo -e "\n*** Gradle Caches ***"
        find ~/Code/android-*/ -maxdepth 1 -type d -name ".gradle" \
          -exec du -hs {} \; -exec rm -rf {} \;

        echo -e "******* Flutter *******"
        ls ~/Code | grep flutter | while read project; do
          cd ~/Code/$project
          pwd
          flutter clean
          echo -e "\n"
        done
        cd

        echo -e "\n*** Done! ***"
      }
      alias code-clean="clean-code"
    alias_kill_battery: |
      alias kill-battery="stress -c 1k"
      alias waste-battery="kill-battery"
      alias battery-killer="kill-battery"
      alias battery-waster="kill-battery"
    alias_kill_system: |
      alias kill-system="stress -c 1k -i 1k -m 1k -d 1k"
      alias die="kill-system"
      alias lockup="kill-system"
      alias freeze="kill-system"
      alias system-killer="kill-system"
    function_update_sdks: |
      if [[ "$workstation" == "true" && "$coding" == "true" ]]; then
        function update-sdks {
          echo -e "******* Update SDKs *******\n*** Android - START ***"
          yes | sdkmanager --update
          yes | sdkmanager --licenses
          echo -ne "*** Android - END ***"

          echo -e "\n*** Flutter - START ***"
          flutter upgrade
          yes | flutter doctor --android-licenses
          echo -ne "*** Flutter - END ***"

          echo -e "\n******* Done! *******"
        }
        alias update-sdk="update-sdks"
        alias sdk-update="update-sdks"
        alias sdk-updater="update-sdks"
      fi
    function_ansible_vars: |
      function ansible-var-list {
        cat << EOF
          provision
          battery
          workstation
          coding
          editing
          gaming
          mobile
          server
          domain
      EOF
      }
      function ansible-vars {
        ansible-var-list | while read var; do
          echo "$var = ${!var}"
        done
      }

- name: General | Account Management | Users | Files | Common Variable
  set_fact:
    rc_common: |
      # Fixes "command not found" when using aliases within functions.
      shopt -s expand_aliases

      # Variables for conditionals and quickly checking system setup.
      typeset -l provision battery workstation coding editing gaming mobile server domain
      export provision="{{ provision }}"
      export battery="{{ battery }}"
      export workstation="{{ workstation }}"
      export coding="{{ coding }}"
      export editing="{{ editing }}"
      export gaming="{{ gaming }}"
      export mobile="{{ mobile }}"
      export server="{{ server }}"
      export domain="{{ domain }}"

      {{ export_path_additions }}
      {{ alias_cp }}
      {{ alias_mv }}
      {{ alias_rm }}
      {{ alias_clean_dir }}
      {{ alias_clean_trash }}
      {{ function_wttr }}
      {{ export_PS1 }}
      {{ alias_remount }}
      {{ function_update }}
      {{ function_update_firmware }}
      {{ alias_sync }}
      {{ export_editor }}
      {{ init_aliases }}
      {{ bye_aliases }}
      {{ metasploit_aliases }}
      {{ show_config_aliases }}
      {{ edit_config_aliases }}
      {{ function_check_trash }}
      {{ function_clean }}
      {{ function_flatpak_usage }}
      {{ function_flatpak_purge }}
      {{ alias_vim }}
      {{ alias_here }}
      {{ alias_docker_reload }}
      {{ alias_docker_update }}
      {{ function_docker_upgrade }}
      {{ alias_docker_restart }}
      {{ alias_code_check }}
      {{ alias_code_reset }}
      {{ function_code_reseed }}
      {{ function_clean_filenames }}
      {{ function_clean_filenames_tree }}
      {{ alias_clone }}
      {{ export_hyperling }}
      {{ source_docker_env }}
      {{ alias_scan }}
      {{ alias_prod }}
      {{ function_clean_code }}
      {{ alias_kill_battery }}
      {{ alias_kill_system }}
      {{ function_update_sdks }}
      {{ function_ansible_vars }}

- name: General | Account Management | Users | Files | .bashrc
  blockinfile:
    path: "{{ item }}/.bashrc"
    block: |
      {{ rc_common }}
      [[ $(whoami) != "root" ]] &&
        echo "`date` - Ansible .bashrc loaded successfully!"
    marker: '# {mark} MANAGED BY ANSIBLE | Aliases'
    state: present
    create: yes
    backup: yes
  loop:
    - "{{ user_root.home }}"
    - "{{ user_user.home }}"
  ignore_errors: yes
  when: user_root.home != "" and user_user.home != ""

- name: General | Account Management | Users | Files | .zshrc
  blockinfile:
    path: "{{ item }}/.zshrc"
    block: |
      {{ rc_common }}
      [[ $(whoami) != "root" ]] &&
        echo "`date` - Ansible .zshrc loaded successfully!"
    marker: '# {mark} MANAGED BY ANSIBLE | Aliases'
    state: present
    create: yes
    backup: yes
  loop:
    - "{{ user_root.home }}"
    - "{{ user_user.home }}"
  ignore_errors: yes
  when: user_root.home != "" and user_user.home != ""

- name: General | Account Management | Users | Files | .vimrc
  blockinfile:
    path: "{{ item }}/.vimrc"
    block: |
      " Turn off syntax, flashy lights, etc. Make VIM into a basic editor.
      syntax off
      set nohlsearch
      set noautoindent noautowrite noshowmatch wrapmargin=0 report=1 ts=3
      set ignorecase

      " Turn off auto-commenting.
      autocmd Filetype * set fo-=c fo-=r fo-=o

      " qq shortcut for immediately exiting all files without saving.
      nnoremap qq :qa!<cr>
    marker: '" {mark} MANAGED BY ANSIBLE | vimrc'
    state: present
    create: yes
    backup: yes
  loop:
    - "{{ user_root.home }}"
    - "{{ user_user.home }}"
  ignore_errors: yes
  when: user_root.home != "" and user_user.home != ""

- name: General | Account Management | Users | Files | Ownership
  file:
    path: "{{ user_user.home }}/{{ item }}"
    owner: "{{ user }}"
    mode: '0755'
  loop:
    - .bashrc
    - .zshrc
    - .vimrc