#!/bin/sh
#
# dirctl — global directory management command for the Active Directory (Samba) test container.
#
#   dirctl seed                     seed groups + users, save credentials
#   dirctl create [opts]            create a user (all fields optional / auto-generated)
#   dirctl list                     list users
#   dirctl show <username>          show a user
#   dirctl passwd <username> [pw]   set/reset a user password (random if omitted)
#   dirctl update <username> [opts] update user attributes
#
# create/update opts (long or short):
#   -u|--username  -p|--password  -e|--email  -f|--first  -l|--last  -g|--group
# create with no options generates a random username, email and password.
#
set -eu

BASE_DN="${AD_BASE_DN:-DC=ad,DC=example,DC=test}"
DOMAIN="${AD_DOMAIN:-ad.example.test}"
SAM_DB="${SAM_DB:-/var/lib/samba/private/sam.ldb}"
CRED_FILE="${CRED_FILE:-/credentials/ad-users.txt}"

random_string() { head -c 24 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | cut -c "1-${1:-16}"; }
random_password() { random_string 16; }

user_exists() { samba-tool user list | grep -qx "$1"; }
user_dn() { ldbsearch -H "$SAM_DB" "(sAMAccountName=$1)" dn 2>/dev/null | awk '/^dn:/{sub(/^dn: /,""); print; exit}'; }

base_username() {
    b="$(printf '%s%s' "$(printf '%s' "$1" | cut -c1)" "$2" | tr '[:upper:]' '[:lower:]' | tr -dc 'a-z0-9')"
    [ -n "$b" ] || b="user$(random_string 6 | tr 'A-Z' 'a-z')"
    printf '%s' "$b"
}

derive_username() {
    base="$(base_username "$1" "$2")"; candidate="$base"; n=1
    while user_exists "$candidate"; do n=$((n + 1)); candidate="${base}${n}"; done
    printf '%s' "$candidate"
}

record_credentials() {
    mkdir -p "$(dirname "$CRED_FILE")"
    [ -f "$CRED_FILE" ] || printf '# Active Directory test users (%s)\n# username | password | email | group | dn\n' "$DOMAIN" > "$CRED_FILE"
    printf '%s | %s | %s | %s | %s\n' "$1" "$2" "$3" "$4" "$(user_dn "$1")" >> "$CRED_FILE"
}

print_summary() {
    printf '\n  user created\n    username : %s\n    password : %s\n    email    : %s\n    group    : %s\n    dn       : %s\n\n' \
        "$1" "$2" "$3" "$4" "$(user_dn "$1")"
}

cmd_create() {
    username=""; password=""; email=""; first=""; last=""; group=""
    while [ $# -gt 0 ]; do
        case "$1" in
            -u|--username) username="$2"; shift 2;;
            -p|--password) password="$2"; shift 2;;
            -e|--email)    email="$2"; shift 2;;
            -f|--first)    first="$2"; shift 2;;
            -l|--last)     last="$2"; shift 2;;
            -g|--group)    group="$2"; shift 2;;
            -*)            echo "unknown option: $1" >&2; exit 2;;
            *) [ -z "$username" ] && { username="$1"; shift; } || { echo "unexpected argument: $1" >&2; exit 2; };;
        esac
    done
    [ -n "$username" ] || username="$(derive_username "$first" "$last")"
    [ -n "$first" ] || first="$username"
    [ -n "$last" ] || last="User"
    [ -n "$password" ] || password="$(random_password)"

    set -- samba-tool user create "$username" "$password" --given-name="$first" --surname="$last"
    if [ "${NO_MAIL:-0}" = "1" ]; then
        email="(none)"
    else
        [ -n "$email" ] || email="${username}@${DOMAIN}"
        set -- "$@" --mail-address="$email"
    fi
    "$@" >/dev/null

    [ -n "$group" ] && samba-tool group addmembers "$group" "$username" >/dev/null 2>&1 || true
    record_credentials "$username" "$password" "$email" "${group:-}"
    print_summary "$username" "$password" "$email" "${group:-none}"
}

cmd_list() {
    samba-tool user list | sort | while read -r u; do
        [ -n "$u" ] || continue
        mail=$(ldbsearch -H "$SAM_DB" "(sAMAccountName=$u)" mail 2>/dev/null | awk '/^mail:/{print $2}')
        printf '%-18s %s\n' "$u" "${mail:-(no mail)}"
    done
}

cmd_show() { samba-tool user show "$1"; }

cmd_passwd() {
    uid="$1"; password="${2:-}"
    [ -n "$password" ] || password="$(random_password)"
    samba-tool user setpassword "$uid" --newpassword="$password" >/dev/null
    echo "password for $uid set to: $password"
}

cmd_update() {
    uid="$1"; shift
    dn="$(user_dn "$uid")"
    [ -n "$dn" ] || { echo "user not found: $uid" >&2; exit 1; }
    body="dn: $dn
changetype: modify
"
    while [ $# -gt 0 ]; do
        case "$1" in
            -e|--email) body="${body}replace: mail
mail: $2
-
"; shift 2;;
            -f|--first) body="${body}replace: givenName
givenName: $2
-
"; shift 2;;
            -l|--last)  body="${body}replace: sn
sn: $2
-
"; shift 2;;
            *) echo "unknown option: $1" >&2; exit 2;;
        esac
    done
    printf '%s' "$body" | ldbmodify -H "$SAM_DB" >/dev/null
    echo "updated $uid"
}

# Create the group, tolerating an already-existing one. Note: AD ships a built-in
# "Administrators" group, so the "administrators" mapping reuses that built-in group.
ensure_group() { samba-tool group add "$1" >/dev/null 2>&1 || true; }

cmd_seed() {
    for g in administrators employees sales support clients; do ensure_group "$g"; done

    # roster (unique to the AD directory): First Last group
    printf '%s\n' \
        "John Doe administrators" \
        "Anna Smith administrators" \
        "Michael Brown administrators" \
        "Karen Wilson administrators" \
        "Robert Jones employees" \
        "Laura Garcia employees" \
        "Thomas Martin employees" \
        "Patricia Clark employees" \
        "David Lee sales" \
        "Sophia Turner sales" \
        "James White sales" \
        "Emma Harris sales" \
        "Daniel King support" \
        "Olivia Scott support" \
        "William Green support" \
        "Ava Baker support" \
        "Henry Adams clients" \
        "Mia Nelson clients" \
        "Jack Carter clients" \
        "Grace Mitchell clients" \
    | while read -r first last group; do
        uname="$(base_username "$first" "$last")"
        user_exists "$uname" && { echo "skip existing $uname"; continue; }
        cmd_create -u "$uname" --first "$first" --last "$last" --group "$group" || echo "warn: create failed for $first $last" >&2
    done

    # Edge-case users for the addon tests: a disabled account and a mail-less account (in employees)
    if ! user_exists "disabled"; then
        cmd_create --username disabled --first Dis --last Abled --group employees
        samba-tool user disable disabled >/dev/null 2>&1 || true
    fi
    if ! user_exists "nomail"; then
        NO_MAIL=1 cmd_create --username nomail --first No --last Mail --group employees
    fi
    echo "seed complete. credentials -> $CRED_FILE"
}

sub="${1:-}"; [ $# -gt 0 ] && shift || true
case "$sub" in
    seed)   cmd_seed "$@";;
    create) cmd_create "$@";;
    list)   cmd_list "$@";;
    show)   cmd_show "$@";;
    passwd) cmd_passwd "$@";;
    update) cmd_update "$@";;
    *) echo "usage: dirctl {seed|create|list|show|passwd|update} ..." >&2; exit 2;;
esac
