#!/bin/zsh

set -eu

SCRIPT_NAME=${0:t}
SCRIPT_DIR=${0:A:h}

DEFAULT_PARENT_WS='ssh://pnyc@dabel.us.oracle.com//workspace/pnyc/solaris-reviews/on-sru'
DEFAULT_DEST_ROOT="${HOME}/PycharmProjects"
DEFAULT_FOLDER_PREFIX='PetrN/'
DEFAULT_POINT_OF_CONTACT='petr.nyc@oracle.com'
DEFAULT_SLACK_CHANNEL='@pnyc'
DEFAULT_PYTHON='/opt/homebrew/bin/python3.11'
OS_NAME="$(uname -s)"

export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin${PATH:+:$PATH}"

usage() {
    cat <<EOF
Usage: ${SCRIPT_NAME} [-r repo_url] [-d dest_dir] [-f folder_prefix] [-c point_of_contact] [-s slack_channel] [-p python_exe] [-j python_jenkins_path] [-h]

  -r repo_url             Mercurial repo URL to clone.
                          Default: ${DEFAULT_PARENT_WS}
  -d dest_dir             Local directory to clone into.
                          Default: ~/PycharmProjects/<repo_name>
  -f folder_prefix        Jenkins folder prefix.
                          Default: ${DEFAULT_FOLDER_PREFIX}
  -c point_of_contact     Contact email for generated config.
                          Default: ${DEFAULT_POINT_OF_CONTACT}
  -s slack_channel        Slack handle/channel for generated config.
                          Default: ${DEFAULT_SLACK_CHANNEL}
  -p python_exe           Python executable for virtualenv creation.
                          Default: ${DEFAULT_PYTHON}
  -j python_jenkins_path  Optional local checkout to inject into requirements.txt.
                          Default: disabled
  -h                      Show this help text.

Environment overrides:
  MRSHUGHES_PARENT_WS
  MRSHUGHES_DEST_DIR
  MRSHUGHES_FOLDER_PREFIX
  MRSHUGHES_POINT_OF_CONTACT
  MRSHUGHES_SLACK_CHANNEL
  MRSHUGHES_PYTHON
  MRSHUGHES_PYTHON_JENKINS_PATH

The script exits if dest_dir already exists.
EOF
}

die() {
    print -u2 -- "$@"
    exit 1
}

require_command() {
    command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"
}

resolve_executable() {
    local executable=${~1}

    if [[ "$executable" == */* ]]; then
        [[ -x "$executable" ]] || die "Executable not found or not executable: $executable"
        print -r -- "$executable"
        return
    fi

    command -v "$executable" >/dev/null 2>&1 || die "Command not found in PATH: $executable"
    command -v "$executable"
}

disable_proxy() {
    local proxy_script="${SCRIPT_DIR}/proxy"

    if [[ -f "$proxy_script" ]]; then
        source "$proxy_script" off >/dev/null
    fi
}

ensure_clone_identity_loaded() {
    local ssh_host=$1
    local identity_file
    local identity_pub

    identity_file=$(ssh -G "$ssh_host" 2>/dev/null | awk '/^identityfile / {print $2; exit}')
    [[ -n "$identity_file" ]] || return

    identity_file=${~identity_file}
    identity_pub="${identity_file}.pub"
    [[ -f "$identity_file" && -f "$identity_pub" ]] || return

    if ssh-add -T "$identity_pub" >/dev/null 2>&1; then
        return
    fi

    print -- "Loading SSH identity for ${ssh_host}: ${identity_file}"
    if [[ "$OS_NAME" == "Darwin" ]]; then
        ssh-add --apple-use-keychain "$identity_file" >/dev/null
    else
        ssh-add "$identity_file" >/dev/null
    fi
}

apply_python_jenkins_override() {
    local checkout_path=$1
    local tmp_requirements

    tmp_requirements=$(mktemp)
    sed -E "s|^git.*$|git+file://${checkout_path}|" requirements.txt > "$tmp_requirements"
    /bin/mv "$tmp_requirements" requirements.txt
}

yaml_escape() {
    local value=$1

    value=${value//\\/\\\\}
    value=${value//\"/\\\"}
    print -r -- "$value"
}

set_yaml_key() {
    local file=$1
    local key=$2
    local value
    local tmp_file

    value=$(yaml_escape "$3")
    tmp_file=$(mktemp)

    if ! awk -v key="$key" -v value="$value" '
        $0 ~ "^[[:space:]]*" key ":[[:space:]]*" {
            match($0, /^[[:space:]]*/)
            print substr($0, 1, RLENGTH) key ": \"" value "\""
            changed = 1
            next
        }
        { print }
        END { exit changed ? 0 : 1 }
    ' "$file" > "$tmp_file"; then
        /bin/rm -f "$tmp_file"
        die "Expected key not found in ${file}: ${key}"
    fi

    /bin/mv "$tmp_file" "$file"
}

insert_yaml_key_after() {
    local file=$1
    local anchor=$2
    local key=$3
    local value
    local tmp_file

    value=$(yaml_escape "$4")
    tmp_file=$(mktemp)

    if ! awk -v anchor="$anchor" -v key="$key" -v value="$value" '
        {
            print
            if (!inserted && $0 ~ "^[[:space:]]*" anchor ":[[:space:]]*") {
                match($0, /^[[:space:]]*/)
                print substr($0, 1, RLENGTH) key ": \"" value "\""
                inserted = 1
            }
        }
        END { exit inserted ? 0 : 1 }
    ' "$file" > "$tmp_file"; then
        /bin/rm -f "$tmp_file"
        die "Expected anchor not found in ${file}: ${anchor}"
    fi

    /bin/mv "$tmp_file" "$file"
}

configure_defaults_devel() {
    local file=$1

    set_yaml_key "$file" script_dir_base "$SCRIPT_DIR_BASE"
    if grep -Eq '^[[:space:]]*pipeline_workspace:[[:space:]]*' "$file"; then
        set_yaml_key "$file" pipeline_workspace "$JENKINS_CLONE_FROM"
    else
        insert_yaml_key_after "$file" script_dir_base pipeline_workspace "$JENKINS_CLONE_FROM"
    fi
    set_yaml_key "$file" folder_prefix "$FOLDER_PREFIX"
    set_yaml_key "$file" point_of_contact "$POINT_OF_CONTACT"
    set_yaml_key "$file" slack_channel "$SLACK_CHANNEL"
}

find_lint_dir() {
    if [[ -d "${DEST_DIR}/solaris/on/production" ]]; then
        print -r -- "${DEST_DIR}/solaris/on/production"
        return
    fi

    if [[ -d "${DEST_DIR}/solaris/userland/sru" ]]; then
        print -r -- "${DEST_DIR}/solaris/userland/sru"
        return
    fi

    die "Unable to determine lint directory under ${DEST_DIR}/solaris"
}

print_config() {
    print -- "Parent workspace: ${PARENT_WS}"
    print -- "Destination directory: ${DEST_DIR}"
    print -- "Folder prefix: ${FOLDER_PREFIX}"
    print -- "Point of contact: ${POINT_OF_CONTACT}"
    print -- "Slack channel: ${SLACK_CHANNEL}"
    print -- "Python executable: ${PYTHON_EXE}"
    if [[ -n "$PYTHON_JENKINS_PATH" ]]; then
        print -- "python-jenkins override: ${PYTHON_JENKINS_PATH}"
    else
        print -- "python-jenkins override: disabled"
    fi
}

PARENT_WS="${MRSHUGHES_PARENT_WS:-$DEFAULT_PARENT_WS}"
DEST_DIR="${MRSHUGHES_DEST_DIR:-}"
FOLDER_PREFIX="${MRSHUGHES_FOLDER_PREFIX:-$DEFAULT_FOLDER_PREFIX}"
POINT_OF_CONTACT="${MRSHUGHES_POINT_OF_CONTACT:-$DEFAULT_POINT_OF_CONTACT}"
SLACK_CHANNEL="${MRSHUGHES_SLACK_CHANNEL:-$DEFAULT_SLACK_CHANNEL}"
PYTHON_EXE="${MRSHUGHES_PYTHON:-$DEFAULT_PYTHON}"
PYTHON_JENKINS_PATH="${MRSHUGHES_PYTHON_JENKINS_PATH:-}"

while getopts ":r:d:f:c:s:p:j:h" opt; do
    case "$opt" in
        r) PARENT_WS="$OPTARG" ;;
        d) DEST_DIR="$OPTARG" ;;
        f) FOLDER_PREFIX="$OPTARG" ;;
        c) POINT_OF_CONTACT="$OPTARG" ;;
        s) SLACK_CHANNEL="$OPTARG" ;;
        p) PYTHON_EXE="$OPTARG" ;;
        j) PYTHON_JENKINS_PATH="$OPTARG" ;;
        h)
            usage
            exit 0
            ;;
        :)
            die "Missing argument for -$OPTARG"
            ;;
        \?)
            usage >&2
            die "Unknown option: -$OPTARG"
            ;;
    esac
done

shift $((OPTIND - 1))
[[ $# -eq 0 ]] || die "Unexpected positional arguments: $*"

for command_name in hg awk sed mktemp grep make; do
    require_command "$command_name"
done

PARENT_WS="${PARENT_WS%/}"
REPO=${PARENT_WS##*/}
[[ -n "$DEST_DIR" ]] || DEST_DIR="${DEFAULT_DEST_ROOT}/${REPO}"

DEST_DIR=${~DEST_DIR:A}
PYTHON_EXE=$(resolve_executable "$PYTHON_EXE")

if [[ -n "$PYTHON_JENKINS_PATH" ]]; then
    PYTHON_JENKINS_PATH=${~PYTHON_JENKINS_PATH:A}
    [[ -d "$PYTHON_JENKINS_PATH" ]] || die "Required directory not found: ${PYTHON_JENKINS_PATH}"
fi

[[ ! -e "$DEST_DIR" ]] || die "Destination already exists: ${DEST_DIR}"

JENKINS_CLONE_FROM=$PARENT_WS
if [[ "$PARENT_WS" == ssh://* ]]; then
    JENKINS_CLONE_FROM="ssh://${${PARENT_WS#ssh://}#*@}"
    ensure_clone_identity_loaded "${${PARENT_WS#ssh://}%%/*}"
fi

SCRIPT_DIR_BASE=$PARENT_WS
if [[ "$PARENT_WS" == *'//'* ]]; then
    SCRIPT_DIR_BASE="/${PARENT_WS##*//}"
fi

print_config

print -- "Validating Mercurial access to ${PARENT_WS}"
hg identify "$PARENT_WS" >/dev/null

print -- "Cloning ${PARENT_WS} into ${DEST_DIR}"
mkdir -p "${DEST_DIR:h}"
hg clone "$PARENT_WS" "$DEST_DIR"

cd "$DEST_DIR"

for required_path in \
    requirements.txt \
    Makefile.inc \
    common/tools/create_virtualenv \
    common/etc/passwd.template \
    common/jobs/defaults.devel.tmpl \
    common/jobs/defaults.stage.tmpl
do
    [[ -e "$required_path" ]] || die "Required path not found: ${DEST_DIR}/${required_path}"
done

[[ -x common/tools/create_virtualenv ]] || die "Required executable not found: ${DEST_DIR}/common/tools/create_virtualenv"

disable_proxy
/bin/rm -rf venv

if [[ -n "$PYTHON_JENKINS_PATH" ]]; then
    print -- "Applying local python-jenkins override from ${PYTHON_JENKINS_PATH}"
    apply_python_jenkins_override "$PYTHON_JENKINS_PATH"
fi

/usr/bin/env PATH="$PATH" /bin/bash -lc 'common/tools/create_virtualenv "$@"' bash "$PYTHON_EXE" requirements.txt venv

{
    print -- '[alias]'
    print -- 'ci = ci -X Makefile.inc'
    print -- 'st = st -X Makefile.inc'
} >> .hg/hgrc

tmp_makefile=$(mktemp)
sed 's:PYTHON3=python3.7:PYTHON3=python3.11:g' Makefile.inc > "$tmp_makefile"
/bin/mv "$tmp_makefile" Makefile.inc

/bin/cp common/etc/passwd.template common/etc/passwd
/bin/cp common/jobs/defaults.devel.tmpl common/jobs/defaults.devel.yml
/bin/cp common/jobs/defaults.stage.tmpl common/jobs/defaults.stage.yml

configure_defaults_devel common/jobs/defaults.devel.yml

LINT_DIR=$(find_lint_dir)
disable_proxy
cd "$LINT_DIR"
make FAKE_DEVEL_ENV=yes lint
