#!/bin/bash
SETUP_DIR=$(readlink -f `pwd`)
cd "$SETUP_DIR"
CONTROLFLOOR_DIR=$(dirname $SETUP_DIR)
CURRENT_INSTALL_VERSION=$(basename $SETUP_DIR)
CONTROLFLOOR_CURRENT=$CONTROLFLOOR_DIR/current
PYTHON3_EXE_PATH=
MIN_PYTHON_VERSION=3.11

if [ ! -d "./server" ]; then
    echo "Directory './server' does not exist.  Are you running this from a ControlFloor installation?"
    exit 1
fi

compare_versions() {
    local versionA=$1
    local versionB=$2

    if [ "$versionA" == "$versionB" ]; then
        echo "eq"
        return
    fi

    # Sort the versions
    local sorted_versions=$(printf "%s\n%s\n" "$versionA" "$versionB" | sort -V)

    # Compare the sorted versions
    if [[ "$(echo "$sorted_versions" | head -n1)" == "$versionA" ]]; then
        echo "lt"
    else
        echo "gt"
    fi
}

# Function to check if Xcode or Xcode command line tools are installed
check_xcode_installation() {
    if command -v xcode-select >/dev/null 2>&1; then
        xpath=$(xcode-select --print-path 2>/dev/null)
    fi

    if [[ -z "$xpath" || "$xpath" == "/Library/Developer/CommandLineTools" || ! -d "$xpath" || ! -x "$xpath" ]]; then
        echo "This installer expects a full installation of xcode to be available.  It will"
        echo "not function with only command line tools. If you have a full installation, please"
        echo "set it first with "
        echo "    sudo xcode-select -s /path/to/XCode.app"
        echo "and re-run this script."
        echo ""
        exit 1
    #else
    #    echo "XCode found at $xpath"
    fi
}

check_brew_python3() {
    if command -v python3 &> /dev/null; then
        # Get the path of python3 command
        python3_path=$(command -v python3)

        echo "python3 detected at $python3_path"
        echo ""

        # Get the current installed Python3 version
        local current_version=$(python3 --version 2>&1 | awk '{print $2}')
        VERSION_COMP=$(compare_versions "$current_version" "$MIN_PYTHON_VERSION")
        if [ "$VERSION_COMP" == "lt" ]; then
            echo "Detected python3 version $current_version, which is less than required $MIN_PYTHON_VERSION"
            if ask_question  "Upgrade using brew now?"; then
                brew install python3 || exit $?
                # If your path is such that the brew python isn't first in your path, you could get
                # stuck in a loop here.
                check_brew_python3
                return
            else
                echo "Cannot continue without python3 >= $MIN_PYTHON_VERSION"
                exit 1
            fi
        fi
        return
    else
        echo "python3 not found."
    fi

    if ask_question "Would you like to install python3 from brew?"; then
        brew install python3 || exit $?
        check_brew_python3
    else
        echo "Cannot continue without python3"
        exit 1
    fi
}

check_brew() {
    # Once we have an install using minibrew, don't autodetect regular brew
    # (but allow user to force with USE_BREW=1
    if [ -e "MiniBrew/bin/python3" ] && [ -z "$USE_BREW" ]; then
        return
    fi
    # Check if 'brew' command is in PATH
    if command -v brew &> /dev/null; then
        :
    else
        # Check if brew executable is installed in standard locations
        if [[ -x "/opt/homebrew/bin/brew" ]]; then
            save_brew_env "Brew executable found in '/opt/homebrew/bin/' but 'brew' not on your PATH."
            eval "$(/opt/homebrew/bin/brew shellenv)"
        elif [[ -x "/usr/local/bin/brew" ]]; then
            save_brew_env "Brew executable found in '/usr/local/bin/' but 'brew' not on your PATH."
            eval "$(/usr/local/bin/brew shellenv)"
        elif [ ! -z "$USE_BREW" ]; then
            echo "USE_BREW is set, but Homebrew (https://brew.sh/) was not detected."
            if ask_question "Would you like to install Homebrew now?"; then
                /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || exit $?
                save_brew_env ""
                eval "$(/opt/homebrew/bin/brew shellenv)"
                bash "$0" # Should find brew on the second go-around
                exit $?
            else
                echo "Please install Homebrew before continuing (https://brew.sh/)"
                exit 1
            fi
        else
            # If USE_BREW not set, and brew not installed, just use minibrew, don't prompt
            return
        fi
    fi

    if command -v brew &> /dev/null && [ -z "$USE_BREW" ]; then
        brew_path=$(command -v brew)
        echo "Homebrew found on this machine at $brew_path"
        echo "Would you like to use your existing brew installation to install ControlFloor requirements?"
        echo "(Enter 'n' to install requirements in a seperate folder using minibrew)"
        if ask_question "Use existing brew installation?"; then
            USE_BREW=1
        else
            return
        fi
    fi

    # Install dependencies:
    brew install libimobiledevice ideviceinstaller libplist jq || exit $?
}

save_brew_env() {
    local comment="$1"

    local current_shell=$(basename "$SHELL")
    local config_file

    case "$current_shell" in
        "bash")
            config_file=".bash_profile"
            ;;
        "zsh")
            config_file=".zshrc"
            ;;
        "fish")
            config_file=".config/fish/config.fish"
            mkdir -p ~/.config/fish
            ;;
        "ksh")
            config_file=".profile"
            ;;
        "csh")
            config_file=".cshrc"
            ;;
        "dash")
            config_file=".profile"
            ;;
        "tcsh")
            config_file=".tcshrc"
            ;;
        *)
            echo "Unknown shell: $current_shell"
            echo "Be sure to manually add the following to your shell's configuration:"
            /opt/homebrew/bin/brew shellenv
            echo "Press [Enter] to continue..."
            return 1
            ;;
    esac

    # Only really an issue for re-running this script repeatedly without logging out/in
    # If the config file appears to have a "brew shellenv" call already, don't add another
    # one.
    if [[ -f "$HOME/$config_file" ]]; then
        # Look for "brew shellenv" in the first non-commented block of the file
        awk -F'#' '{print $1}' "$HOME/$config_file" | grep -q 'brew shellenv'
        if [[ $? -eq 0 ]]; then
            return
        fi
    fi

    if [ -n "$comment" ]; then
        echo "$comment"
    fi

    if ask_question "Would you like to like to add brew's config to $HOME/$config_file?"; then
        (echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> "$HOME/$config_file"
    else
        return 1
    fi
}

ask_question() {
    local question="$1"
    while true; do
        read -p "$question (y/n): " yn
        case $yn in
            [Yy]* ) return 0;;  # Return 0 for yes
            [Nn]* ) return 1;;  # Return 1 for no
            * )
        esac
    done
}

MINIBREW_VERSION=$(cat minibrew.version.txt)
MINIBREW_DIR=$(realpath "../../MiniBrew/$MINIBREW_VERSION")
install_minibrew() {
    cd $MINIBREW_DIR
    if [ ! -e "./bin/python3" ]; then
        ./deploy.pl
        ./bin/python3 -m ensurepip --upgrade >& /dev/null
    fi
    cd $SETUP_DIR
}

check_xcode_installation

IN_PLACE=no

# If there is a previous install, import local database/config/etc. before continuing
if [ -L "../current" ]; then
    PREVIOUS_INSTALL_DIR=$(readlink ../current)
    if [ "$PREVIOUS_INSTALL_DIR" != "$cwd" ]; then
        PREVIOUS_INSTALL_VERSION=$(basename "$PREVIOUS_INSTALL_DIR")
        VERSION_COMP=$(compare_versions "$CURRENT_INSTALL_VERSION" "$PREVIOUS_INSTALL_VERSION")
        if [ "$VERSION_COMP" == "eq" ]; then
            IN_PLACE=yes
        elif [ "$VERSION_COMP" == "gt" ]; then
            echo ""
            echo "This looks like an upgrade from version $PREVIOUS_INSTALL_VERSION to $CURRENT_INSTALL_VERSION."
            if ask_question  "Migrate settings?('n' for a clean installation')"; then
                rsync -av --prune-empty-dirs --no-times --ignore-existing --exclude DeviceLogs --exclude python --exclude MiniBrew --exclude venv --exclude apps/CFAUIInspector.app --exclude apps/CFAgent-Runner.app --exclude apps/vidstream.app --exclude cfagent.xctestrun --exclude server/tmpl ~/ControlFloor/* .
            fi
        else
            echo "Downgrade from $CURRENT_INSTALL_VERSION to $PREVIOUS_INSTALL_VERSION detected. After this installation is complete"
            echo "you will need to manually copy any configuration files to this directory."
            echo "Automatic database downgrading is not supported.  If the database version"
            echo "has changed, you may need to configure the system from scratch."
            echo ""
            if ask_question  "Continue installing this version?"; then
                :
            else
                exit 1
            fi
        fi
    fi
fi


# Now that we've upgraded (or not), update the 'current' pointer so we don't
# offer to upgrade again.
# Check that parent directory is ControlFloor just as a sanity check.
# If ../current already points at the current directory, IN_PLACE=yes, do nothing.
parent_dir=$(basename "$(dirname "$(pwd)")")
if [ "$parent_dir" = "ControlFloor" ] && [ "$IN_PLACE" = "no" ]; then
    # Delete the symlink "../current" if it exists
    pushd .. > /dev/null
    if [ -L "previous" ]; then
        rm "previous"
    fi
    # Swap current to previous
    if [ -L "current" ]; then
        mv "current" "previous"
    fi

    # Create a new symlink to the current working directory
    ln -s "$CURRENT_INSTALL_VERSION" "current"
    popd > /dev/null
fi

check_brew

if [ ! -z "$USE_BREW" ]; then
    check_brew_python3
    PYTHON_VERSION=$(python3 --version 2>&1 | awk '{print $2}')
    if [ -z "$PYTHON_VERSION" ]; then
        echo "Failed to read brew python3 version"
        exit 1
    fi
    PYTHON3="python3"
    # symlink ./MiniBrew will be included in the PATH ahead of system brew if it exists
    rm -f ./MiniBrew >& /dev/null
else
    install_minibrew
    PYTHON_VERSION=$($MINIBREW_DIR/bin/python3 --version 2>&1 | awk '{print $2}')
    if [ -z "$PYTHON_VERSION" ]; then
        echo "Failed to read MiniBrew python3 version"
        exit 1
    fi
    PYTHON3="$MINIBREW_DIR/bin/python3"

    # Set up simlinks for minibrew inside of provider directory
    if [ ! -e "./MiniBrew" ]; then
        ln -s $MINIBREW_DIR MiniBrew
    fi
fi

VENV_DIR="../venv/$PYTHON_VERSION"

if [ ! -d "$VENV_DIR" ]; then
    # This allows the setup script to rebuild the venv by deleting it.
    $PYTHON3 -m venv $VENV_DIR
fi


# Set up simlinks for minibrew and venv inside of provider directory
if [ ! -e "./venv" ]; then
    ln -s $VENV_DIR venv
fi

echo -n "Updating python packages..."
$VENV_DIR/bin/pip3 install -U -r ./provider/python/requirements.txt | grep -v 'already satisfied'
echo "complete."

if [ ! -f "./provider/config.json" ]; then
    cp ./provider/config.json.example ./provider/config.json
fi

if [ ! -f "./server/config.json" ]; then
    cp ./server/config.json.example ./server/config.json
fi

if [ ! -f "./server/server.key" ] && [ ! -f "./server/server.crt" ]; then
    pushd server > /dev/null
    ./gencert.pl >& /dev/null
    popd > /dev/null
fi

# These two commands just trigger database init/upgrade, which emits logging
# messages we'd like to ignore here. There is a "--silent" option, but we
# don't want to use later in case more interesting messages are emited.
# (This is cosmetic)
./provider/cfprovider env >& /dev/null
./server/cfserver list-devices >& /dev/null

# Pair the provider with the server only if no pairing has previously taken place.
# This only actually makes sense when the provider's config.json indicates that
# the ControlFloor host is somewhere on 'localhost'.
if [ -z "$(./provider/cfprovider listHosts)" ]; then
    ./server/cfserver modify-user -provider -username provider1 -random-password -create | ./provider/cfprovider register -import
fi

# This is a no-op if the user exists,, and exits(1) if the user does not exist
./server/cfserver modify-user -username admin >& /dev/null

if [ $? -eq 0 ]; then
    echo ""
    echo "The 'admin' user is already present for ControlFloor server.  If you wish to reset"
    echo "your admin password, you can do so with the command:"
    echo "    ./server/cfserver modify-user -username admin -change-password"
    echo ""
else
    ./server/cfserver create-user -username admin -admin
fi

# Last step.  SUID if necessary
SUID_EXECUTABLES=("./provider/bin/remotedtool" "./provider/bin/utunuds")
sudo_required=false

# Loop through each executable to check permissions
for EXECUTABLE in "${SUID_EXECUTABLES[@]}"; do
    # Check if the file exists
    if [ ! -f "$EXECUTABLE" ]; then
        echo "Error: file not found: $EXECUTABLE"
        exit 1
    fi

    # Check if the file is executable
    if ! [ -x "$EXECUTABLE" ]; then
        echo "Error: file not executable: $EXECUTABLE"
        error=true
    fi

    # Check if the file is already owned by root
    if [ "$(stat -f %Su "$EXECUTABLE")" != "root" ]; then
        sudo_required=true
    fi

    # Check if the file has the suid bit set
    if ! [ "$(stat -f %Lp "$EXECUTABLE")" -ne 0 ]; then
        sudo_required=true
    fi

done

CF_SHORTCUT_NAME=cf
CF_SHORTCUT=$HOME/$CF_SHORTCUT_NAME
if [ ! -e "$CF_SHORTCUT" ]; then
    #if ask_question "Symlink ControlFloor to $CF_SHORTCUT?"; then
    pushd $HOME > /dev/null
    ln -s $CONTROLFLOOR_CURRENT $CF_SHORTCUT_NAME
    popd > /dev/null
    echo "Created convenience shortcut ~/cf to $CONTROLFLOOR_CURRENT"
    #fi
elif [ -L "$CF_SHORTCUT" ]; then
    CURRENT_LINK=$(readlink $CF_SHORTCUT)
    if [ "$CURRENT_LINK" != "$CONTROLFLOOR_CURRENT" ]; then
        echo "$CF_SHORTCUT currently points at $CURRENT_LINK"
        if ask_question  "Would you like to update this link now?"; then
            unlink $CF_SHORTCUT
            pushd $HOME > /dev/null
            ln -s $CONTROLFLOOR_CURRENT $CF_SHORTCUT_NAME
            popd > /dev/null
        fi
    fi
else
    echo "======"
    echo "WARNING: $CF_SHORTCUT exists, but is not a link to $CONTROLFLOOR_CURRENT"
    echo "======"
fi

# If sudo is required, print message and run necessary commands
if [ "$sudo_required" = true ]; then
    echo "'sudo' is needed to set root permissions for the executables $(basename "${SUID_EXECUTABLES[0]}") and $(basename "${SUID_EXECUTABLES[1]}")"
    if ask_question  "Would you like to set them now?"; then
        echo "sudo chown root:admin ${SUID_EXECUTABLES[*]}"
        sudo chown root:admin ${SUID_EXECUTABLES[*]}
        echo "sudo chmod u+s ${SUID_EXECUTABLES[*]}"
        sudo chmod u+s ${SUID_EXECUTABLES[*]}
    else
        echo "You can set these permissions yourself later with the following commands:"
        echo "    sudo chown root:admin ${SUID_EXECUTABLES[*]}"
        echo "    sudo chmod u+s ${SUID_EXECUTABLES[*]}"
    fi
fi



cat << EOF

Initial installation is complete. Please see the README in the ControlFloor installation
directory for instructions on signing your apps and launching ControlFloor

EOF
