{ pkgs, inputs, phps, lib, config, modulesPath, ... }:

{
  # https://devenv.sh/packages/
  packages = with pkgs; [
    inputs.version.defaultPackage."${builtins.currentSystem}"
    appimage-run
    blackbox
    blackbox-terminal
    coreutils-full
    dbeaver
    delve
    dialog
    drill
    exa
    fd
    fd
    gcc12
    gdlv
    git
    glab
    gnugrep
    gnumake
    gnused
    go-licenses
    go-task
    gum
    httpie
    hurl
    jq
    libffi
    logrotate
    meld
    memcached
    netcat
    nixfmt
    procps
    ranger
    unixtools.xxd
    unzip
    util-linux
    wget
    zlib
    nodePackages.mermaid-cli
    feh
    openssh
    
  ];

  # https://devenv.sh/languages/
  # languages.nix.enable = true;
  languages = { go = { enable = true; }; };

  difftastic.enable = true;

  scripts.run-sshd.exec = ''
  set -x
  
  cd ${config.devenv.root}/docker/sftp-server
  ${pkgs.docker-client}/bin/docker docker build -t jobqueue-sftp-server .
  cd -
  
  
  
  ${pkgs.coreutils}/bin/chmod 700 ${config.devenv.root}/.config/temp_rsa_key
  ${pkgs.coreutils}/bin/chmod 700 ${config.devenv.root}/.config/temp_ed25518_key
  
  ${pkgs.coreutils}/bin/mkdir -p ${config.devenv.root}/.devenv/chroot/home/demo/.ssh 
  ${pkgs.coreutils}/bin/cat ${config.devenv.root}/.config/demo_ssh_key.pub > ${config.devenv.root}/.devenv/chroot/home/demo/.ssh/authorized_keys
  ${pkgs.coreutils}/bin/chmod 700 ${config.devenv.root}/.devenv/chroot/home/demo/.ssh
  ${pkgs.coreutils}/bin/chmod 600 ${config.devenv.root}/.devenv/chroot/home/demo/.ssh/authorized_keys
  
  
  ${pkgs.coreutils}/bin/cat <<EOF > ${config.devenv.root}/.config/sshd_config

AuthorizedKeysCommand ${pkgs.coreutils}/bin/cat ${config.devenv.root}/.devenv/chroot/home/demo/.ssh/authorized_keys
AuthorizedKeysCommandUser nobody

Match User root
    ChrootDirectory ${config.devenv.root}/.devenv/chroot
    ForceCommand internal-sftp
    PasswordAuthentication no
    PermitTunnel no
    AllowAgentForwarding no
    AllowTcpForwarding no
    X11Forwarding no
    PermitRootLogin no
    AllowUsers demo root
EOF
 
  
  ${pkgs.openssh}/bin/sshd -D -e -o \
     HostKey=${config.devenv.root}/.config/temp_rsa_key \
     -f ${config.devenv.root}/.config/sshd_config \
     -o HostKey=${config.devenv.root}/.config/temp_ed25518_key \
     -o Port=''${1:-2222}  \
     -o AuthorizedKeysFile=${config.devenv.root}/.devenv/chroot/home/demo/.ssh/authorized_keys \
     -o PidFile=${config.devenv.root}/.devenv/sshd.pid   
  
  '';
  
  scripts.draw-graph.exec = ''
  echo -e "Enter Meirmaid graph definition. ''${RED}End with Ctrl+D''${RESET}\n"  
  diagram=$(${pkgs.gum}/bin/gum write --placeholder "Enter Meirmaid graph definition. End with Ctrl+D")
  
  tmpOutput=$(mktemp).png
  
  echo "$diagram" | ${pkgs.nodePackages.mermaid-cli}/bin/mmdc -i - -o "$tmpOutput"
  ${pkgs.feh}/bin/feh $tmpOutput
  
  # should delte the file, but does not work ask with gum
  ${pkgs.gum}/bin/gum confirm "Delete temporary file?"
    if [ $? -eq 0 ]; then
       rm "$tmpOutput"
       else
         echo "not deleting; file is at $tmpOutput"
    fi    
    
    
  
  
  '';
  
  scripts.get-go-default-packages.exec = ''
    #!${pkgs.bash}/bin/bash
    echo $(awk -F ' ' '/^module / { print $2 }' go.mod)
  '';

  # This script is executed when the app is built
  # You can use it to build the app  
  scripts.test-lib.exec = ''
    #!${pkgs.bash}/bin/bash
    #set -euo pipefail
    set -x

    PATH="''${PATH}":${pkgs.coreutils}/bin
    PATH="''${PATH}":${pkgs.findutils}/bin
    PATH="''${PATH}":${pkgs.jq}/bin/
    PATH="''${PATH}":${pkgs.rsync}/bin/
    PATH="''${PATH}":${pkgs.bash}/bin/
    PATH="''${PATH}":${pkgs.curl}/bin/
    PATH="''${PATH}":${pkgs.moreutils}/bin/
    PATH="''${PATH}":${pkgs.gnutar}/bin
    PATH="''${PATH}":${pkgs.gzip}/bin/
    PATH="''${PATH}":${pkgs.procps}/bin/
    PATH="''${PATH}":${pkgs.exa}/bin/
    PATH="''${PATH}":${pkgs.git}/bin/
    PATH="''${PATH}":${pkgs.gnugrep}/bin/
    PATH="''${PATH}":${
      inputs.version.defaultPackage."${builtins.currentSystem}"
    }/bin/

    export PATH

    task test

  '';

  # This scritp is used to deploy the app to the gitlab registry
  # It is used by the gitlab-ci.yml file
  # The environment variables are set in the gitlab project settings
  scripts.deploy-lib.exec = ''
    #!${pkgs.bash}/bin/bash

    PATH="''${PATH}":${pkgs.coreutils}/bin
    PATH="''${PATH}":${pkgs.jq}/bin/
    PATH="''${PATH}":${pkgs.curl}/bin/
    PATH="''${PATH}":${pkgs.moreutils}/bin/
    PATH="''${PATH}":${pkgs.gnutar}/bin
    PATH="''${PATH}":${pkgs.gzip}/bin/
    PATH="''${PATH}":${pkgs.exa}/bin/
    PATH="''${PATH}":${pkgs.git}/bin/
    PATH="''${PATH}":${
      inputs.version.defaultPackage."${builtins.currentSystem}"
    }/bin/

    export PATH

    if [[ -f .env-gitlab-ci ]]; then
        source .env-gitlab-ci
        rm .env-gitlab-ci
    fi

    set -x
    ## if $HOME not set, set it to current directory
    if [[ -z "''${HOME}" ]]; then
        HOME=$(pwd)
    fi

    export HOME

    git config user.email "''${GITLAB_USER_EMAIL}"
    git config user.name "''${GITLAB_USER_NAME:-"Gitlab CI"}"
    git config pull.rebase true
    git config http.sslVerify "false"
    git remote set-url origin https://pad:''${GITLAB_TOKEN}@''${CI_REPOSITORY_URL#*@}

    git fetch --all --tags --unshallow
    git reset --hard origin/master
    git checkout $CI_COMMIT_REF_NAME
    git pull origin $CI_COMMIT_REF_NAME

    if [ ! -z "''${CI_PROJECT_DIR}" ]; then
        echo "CI_PROJECT_DIR is set, using it as project root."
        project_root=$(realpath "''${CI_PROJECT_DIR}")/
    elif [ ! -z "''${DEVENV_ROOT}" ]; then
        echo "DEVENV_ROOT is set, using it as project root."
        project_root=$(realpath "''${DEVENV_ROOT}")/
    else
        echo "Error: DEVENV_ROOT or CI_PROJECT_DIR environment variables are not set."
        exit 1  
    fi

    if [ ! -d "''${project_root}" ]; then
        echo "Error: Project root directory does not seem to be valid."
        echo "Check the DEVENV_ROOT or CI_PROJECT_DIR environment variables."
        exit 1
    fi

    if [ -z "'CI_JOB_TOKEN" ]; then
        echo "Error: CI_JOB_TOKEN variable is not set."
        exit 1
    fi

    git --no-pager log --decorate=short --pretty=oneline
    gitVersion=v$(version predict)
    git tag -a $gitVersion -m"chore: bump version" 
    git --no-pager log --decorate=short --pretty=oneline
    git push -o ci.skip origin ''${CI_COMMIT_REF_NAME} --tags

    echo "done"

  '';

  enterShell = ''
        
    cat <<'EOF' > Taskfile.yml

    # THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL
    # DO NOT EDIT THIS FILE MANUALLY
    # INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix 
    # AND OPEN A SHELL WITH THE COMMAND devenv shell
    #
    # Information about the task runner can be found here:
    # https://taskfile.dev

    version: '3'

    tasks:
      default:
        cmds:
          - task --list
        silent: true
        
      test:
        desc: Execute unit tests in Go.
        cmds:
          - echo "Execute unit tests in Go."
          - go test -cover -v ./...
          - go test -bench -v ./...
          - go test -race -v ./...

      test-fuzz:
        desc: Conduct fuzzing tests.#
        cmds:  
          - echo "Conduct fuzzing tests."
          - go test -v -fuzztime=30s -fuzz=Fuzz ./...

      add-licenses:
        desc: Attach license headers to Go files.
        cmds:
          - echo "Attach license headers to Go files."
          - go install github.com/google/addlicense@latest
          - addlicense -c "schukai GmbH" -s -l "AGPL-3.0" ./*.go
        silent: true
        
      check-licenses:
        desc: Check license headers of Go files.
        silent: true
        cmds:
          - go-licenses save "$(get-go-default-packages)"  --ignore "gitlab.schukai.com"  --force --save_path ''${DEVENV_ROOT}/licenses/

      check:
        desc: Confirm repository status.
        cmds:
          - git diff-index --quiet HEAD || (echo "There are uncommitted changes after running make. Please commit or stash them before running make."; exit 1)
        silent: true

      commit:
        desc: Commit changes to the repository.
        aliases:
          - c
          - ci
          - git-commit
        cmds:
          - do-git-commit
    EOF

    cat <<'EOF' > .gitlab-ci.yml

    # THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL
    # DO NOT EDIT THIS FILE MANUALLY
    # INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix
    # AND OPEN A SHELL WITH THE COMMAND devenv shell
    #

    image: docker-registry.schukai.com:443/nixos-ci-devenv:latest

    services:
      - docker:dind
        

    variables:
      # The repo name as used in
      # https://github.com/nix-community/NUR/blob/master/repos.json
      NIXOS_VERSION: "23.05"
      NIXPKGS_ALLOW_UNFREE: "1"
      NIXPKGS_ALLOW_INSECURE: "1"
      DOCKER_DRIVER: overlay2
      DOCKER_HOST: unix:///var/run/docker.sock
      GIT_DEPTH: 10

    stages:
      - test
      - deploy 

    before_script:
      - nix shell nixpkgs#coreutils -c mkdir -p /certs/client/
      - nix shell nixpkgs#coreutils -c ln -fs /etc/ssl/certs/ca-bundle.crt /certs/client/ca.pem
      - echo > .env-gitlab-ci
      - variables=("HOME=$HOME" "CI_COMMIT_REF_NAME=$CI_COMMIT_REF_NAME" "CI_REPOSITORY_URL=$CI_REPOSITORY_URL" "GITLAB_TOKEN=$GITLAB_TOKEN" "CI_JOB_TOKEN=$CI_JOB_TOKEN" "GITLAB_USER_EMAIL=$GITLAB_USER_EMAIL" "GITLAB_USER_NAME=\"$GITLAB_USER_NAME\"" "CI_REGISTRY_USER=$CI_REGISTRY_USER" "CI_PROJECT_ID=$CI_PROJECT_ID" "CI_PROJECT_DIR=$CI_PROJECT_DIR" "CI_API_V4_URL=$CI_API_V4_URL" "CI_PROJECT_NAME=$CI_PROJECT_NAME" "CI_COMMIT_SHORT_SHA=$CI_COMMIT_SHORT_SHA"); for var in "''${variables[@]}"; do echo "$var" >> .env-gitlab-ci; done
      - cat .env-gitlab-ci
      - sudo usermod -aG 998 gitlab-runner
      - env
      - ls -la /var/run/docker.sock
      - nix shell nixpkgs#docker-client -c docker --host="unix:///var/run/docker.sock" info

    after_script:
      - if [ -f .env-gitlab-ci ]; then rm .env-gitlab-ci; fi

    test:
      stage: test
      tags:
        - nixos
      script:
        - devenv shell test-lib

      cache:
        - key: nixos
          paths:
            - /nix/store
      
      artifacts:
        paths:
          - dist

    deploy:
      stage: deploy
      tags:
        - nixos
      script:
        - devenv shell -c deploy-lib

      when: on_success

      cache:
        - key: nixos
          paths:
            - /nix/store


      artifacts:
        paths:
          - dist   
    EOF

       
        
  '';

  scripts.do-git-commit.exec = ''
    #!/usr/bin/env bash

    # Define colors if the terminal supports it
    if [ -t 1 ]; then
      RED='\033[0;31m'
      GREEN='\033[0;32m'
      RESET='\033[0m'
      BOLD='\033[1m'
    else
      RED=""
      GREEN=""
      RESET=""
    fi

    step=1

    reset
    clear

    # create random log file
    LOGFILE="$(mktemp)"
    if [ $? -ne 0 ]; then
      echo -e "''${RED}✖ Could not create temporary log file. Exiting.''${RESET}"
      exit 1
    fi

    log_and_display() {
      echo -e "''${GREEN}==> $step. $1''${RESET}" | tee -a $LOGFILE
      step=$((step + 1))
    }

    log_error_and_display() {
      echo -e "''${RED}==> $step. $1''${RESET}" | tee -a $LOGFILE
    }

    printLogfileAndExit() {
      exit_code=$1
      echo -e "\n\n========================================\n\n\n"

      echo -e "\n\n''${BOLD}Git and GitLab Automation Script''${RESET}\n\nI have performed the following actions:\n\n"
      cat "$LOGFILE"

      # Optional: Remove log file
      rm -f "$LOGFILE"

      if [ $exit_code -eq 0 ]; then
        echo -e "\n''${GREEN}✔''${RESET} All actions were successful" | tee -a $LOGFILE
      elif [ $exit_code -eq -1 ]; then
        echo -e "\n''${RED}✖''${RESET} The script was manually cancelled" | tee -a $LOGFILE
        exit_code=0
      else
        echo -e "\n''${RED}✖''${RESET} Some actions failed" | tee -a $LOGFILE
      fi

      exit $exit_code
    }

    print_headline() {
      local title=$1
      local underline=$(printf '─%.0s' $(seq 1 ''${#title}))
      echo -e "\n\n''${BOLD}''${title}\n''${underline}''${RESET}\n"
    }

    do_cancel() {
      echo -e "''${RED}==> ✖ Cancelled.''${RESET}" | tee -a $LOGFILE
      printLogfileAndExit -1
    }

    # Function for unified logging and display
    log_action() {
      if [ $? -eq 0 ]; then
        echo -e "    ''${GREEN}✔''${RESET} $1: Successful" | tee -a $LOGFILE
      else
        echo -e "    ''${RED}✖''${RESET} $1: Failed" | tee -a $LOGFILE
        printLogfileAndExit 1
      fi
    }

    print_what_to_do() {
      echo -e "\n\nWhat do you want to do?\n"
    }

    git_status=$(git status --porcelain)
    if [[ -z "$git_status" ]]; then
      log_error_and_display "No changes to commit. Exiting."
      printLogfileAndExit 0
    fi

    print_headline "Choose commit type"
    selection=$(gum choose "feat: (new feature for the user, not a new feature for build script)" "fix: (bug fix for the user, not a fix to a build script)" "chore: (updating grunt tasks etc.; no production code change)" "docs: (changes to the documentation)" "style: (formatting, missing semi colons, etc; no production code change)" "refactor: (refactoring production code, eg. renaming a variable)" "test: (adding missing tests, refactoring tests; no production code change)" "Cancel")

    commit_type=$(echo "$selection" | awk -F':' '{print $1}')

    if [[ "$commit_type" == "Cancel" ]]; then
      do_cancel
    fi

    log_and_display "You chose the commit type: $commit_type"

    # NEXT STEP ISSUE HANDLING ############################################################################################################
    #log_and_display "Issue handling"

    gitlabIssues=()
    while IFS= read -r line; do
      if [[ $line =~ ^# ]]; then
        id=$(echo "$line" | awk '{print substr($1, 2)}')
        title=$(echo "$line" | awk -F'about' '{print $1}' | awk '{$1=$2=""; print substr($0, 3)}')
        gitlabIssues+=("$id > $title")
      fi
    done < <(gum spin --spinner dot --show-output --title "Ask gitlab ..." -- glab issue list --output-format=details)

    ## if issues are available, ask if user wants to use an existing issue or create a new one
    createOption="Create new issue"
    existingOption="Use existing issue"
    cancelOption="Cancel"

    print_headline "Choose issue handling"
    if [ ''${#gitlabIssues[@]} -eq 0 ]; then
      log_and_display "There are no issues available."

      print_what_to_do
      choice=$(gum choose "$createOption" "$cancelOption")

    else
      log_and_display "There are ''${#gitlabIssues[@]} issues available."
      print_what_to_do
      choice=$(gum choose "$createOption" "$existingOption" "$cancelOption")
    fi

    if [[ "$choice" == "$cancelOption" ]]; then
      do_cancel
    fi

    ## array of issue ids
    work_on_issue_ids=()

    issue_text=""

    if [[ "$choice" == "$createOption" ]]; then
      print_headline "Create new issue"
      issue_text=$(gum input --placeholder "Enter issue title")
      echo -e "Enter issue description. ''${RED}End with Ctrl+D''${RESET}\n"
      issue_description=$(gum write --placeholder "Enter issue description. End with Ctrl+D")
      
      if [[ -z "$issue_text" ]]; then
        log_error_and_display "Issue title is empty. Exiting."
        printLogfileAndExit 1
      fi

      log_and_display "You entered the issue title: $issue_text"
      log_and_display "You entered the issue description: $issue_description"
      echo -e "\n"

      gum confirm "Do you want to create this issue?"
      # gum confirm exits with status 0 if confirmed and status 1 if cancelled.
      if [ $? -eq 1 ]; then
        do_cancel
      fi

      issue_output=$(glab issue create -t"$issue_text" --no-editor --description "$issue_description")
      issue_id=$(echo "$issue_output" | grep -oP '(?<=/issues/)\d+')

      work_on_issue_ids+=("$issue_id")

      log_action "glab issue with id $issue_id created"

    else

      print_headline "Use existing issue"
      echo -e "Select issue with arrow keys and press tab or space to select. Press enter to confirm.\n"
      issue_ids=$(gum choose --no-limit "''${gitlabIssues[@]}")

      # assign issue_ids to work_on_issue_ids. iterate over lines and take integer from beginning of line
      while IFS= read -r line; do
        work_on_issue_ids+=($(echo "$line" | grep -oP '^\d+'))
      done <<<"$issue_ids"

    fi

    if [ ''${#work_on_issue_ids[@]} -eq 0 ]; then
      log_and_display "No issue selected. Exiting."
      printLogfileAndExit 0
    fi 

    # NEXT STEP COMMIT MESSAGE ############################################################################################################
    # print work_on_issue_ids
    work_on_issue_ids_string=""
    for i in "''${work_on_issue_ids[@]}"; do
      work_on_issue_ids_string+="#$i "
    done

    log_and_display "You chose to work on the following issues: ''${work_on_issue_ids_string}"


    print_headline "Check for changes to commit"

    # ' ' = unmodified
    # M = modified
    # T = file type changed (regular file, symbolic link or submodule)
    # A = added
    # D = deleted
    # R = renamed
    # C = copied (if config option status.renames is set to "copies")
    # U = updated but unmerged
    # https://man.freebsd.org/cgi/man.cgi?query=git-status&sektion=1&manpath=freebsd-release-ports

    count_all_changes=$(echo "$git_status" | wc -l)
    count_staged_changes=$(echo "$git_status" | grep -c '^M')
    count_new_staged_files=$(echo "$git_status" | grep -c '^A')
    count_staged_changes=$((count_staged_changes + count_new_staged_files))



    git_options_all="All $count_all_changes changes"
    git_options_staged="Only the $count_staged_changes staged changes"
    git_options_select_files="Select files"
    git_options_cancel="Cancel"

    git_options_array=()
    if [[ $count_all_changes -gt 0 ]]; then
      git_options_array+=("$git_options_all")
    fi

    if [[ $count_staged_changes -gt 0 ]]; then
      git_options_array+=("$git_options_staged")
    fi

    git_options_array+=( "$git_options_select_files" )
    git_options_array+=( "$git_options_cancel" )


    selection=$(gum choose "''${git_options_array[@]}")
    if [[ "$selection" == "$git_options_cancel" ]]; then
      do_cancel
    fi

    if [[ "$selection" == "$git_options_all" ]]; then
      git add -A
      echo "1"
    elif [[ "$selection" == "$git_options_select_files" ]]; then

      files=()
      while IFS= read -r line; do
        files+=("$line")
      done <<<"$git_status"
        
      selected_files=$(gum choose --no-limit "''${files[@]}")
      
      # no files selected
      if [[ -z "$selected_files" ]]; then
        log_and_display "No files selected. Exiting."
        printLogfileAndExit 0
      fi

      # add selected files
      while IFS= read -r line; do
        ## git proclimne could have letter, ? or ! at the beginning of the line
        file=$(echo "$line" | awk '{print $2}')
        if [[ -z "$file" || ! -f "$file" ]]; then
          log_and_display "No file found in line: $line"
          continue
        fi
        
        git add "$file"
      done <<<"$selected_files"
      
    fi

    ## count staged changes again and print
    count_staged_changes=$(echo "$git_status" | grep -c '^M')
    count_new_staged_files=$(echo "$git_status" | grep -c '^A')
    count_staged_changes=$((count_staged_changes + count_new_staged_files))

    log_and_display "You have $count_staged_changes staged changes to commit."

    # NEXT STEP COMMIT MESSAGE ############################################################################################################

    print_headline "Enter commit message"
    commit_message=$(gum input --placeholder "Enter commit message" --value "$commit_type: $issue_text $work_on_issue_ids_string")

    if [[ -z "$commit_message" ]]; then
      log_error_and_display "Commit message is empty. Exiting."
      printLogfileAndExit 1
    fi

    log_and_display "You entered the commit message: $commit_message"

    gum confirm "Do you want to commit with this message?"
    if [ $? -eq 1 ]; then
      do_cancel
    fi

    # NEXT STEP COMMIT ####################################################################################################################
    print_headline "Committing changes"

    if ! git commit -m "$commit_message" ; then
      log_error_and_display "Commit failed. Exiting."
      printLogfileAndExit 1
    fi

    log_and_display "Commit successful."

    # NEXT STEP PUSH ######################################################################################################################

    print_headline "Pushing changes"

    if ! git push ; then
      log_error_and_display "Push failed. Exiting."
      printLogfileAndExit 1
    fi

    log_and_display "Push successful."

    # Close issue ######################################################################################################################

    print_headline "Closing issues"

    for issue_id in "''${work_on_issue_ids[@]}"; do
      
      gum confirm "Do you want to close issue #$issue_id?"
      if [ $? -eq 1 ]; then
        continue
      fi
      
      if ! glab issue close "$issue_id" ; then
        log_error_and_display "Closing issue $issue_id failed. Exiting."
      else
        log_and_display "Closing issue $issue_id successful."  
      fi
    done

    printLogfileAndExit 0
  '';


}