#!/usr/bin/env bash
# Update a user's username and/or password in the Docker (production) SQLite DB.
# Run this on the prod server next to docker-compose.yml.
#
# Usage:
#   ./scripts/xdocker-update-user.sh <current-username> --username <new>
#   ./scripts/xdocker-update-user.sh <current-username> --password <new>
#   ./scripts/xdocker-update-user.sh <current-username> --username <new> --password <new>
#   ./scripts/xdocker-update-user.sh <current-username> --username <new> --password <new> --yes
#
# Password is hashed with bcrypt (cost 10), same as the app.
# Username rules match registration: 3–32 chars, [a-zA-Z0-9_], unique.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [[ -f "$SCRIPT_DIR/../docker-compose.yml" ]]; then
  ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
elif [[ -f "$SCRIPT_DIR/docker-compose.yml" ]]; then
  ROOT="$SCRIPT_DIR"
else
  echo "Error: docker-compose.yml not found near $SCRIPT_DIR" >&2
  exit 1
fi
cd "$ROOT"

ENV_FILE="${ENV_FILE:-.env.prod}"
COMPOSE=(docker compose)
if [[ -f "$ENV_FILE" ]]; then
  COMPOSE=(docker compose --env-file "$ENV_FILE")
fi

SERVICE="${SERVICE:-casegambling}"
CURRENT_USERNAME="${1:-}"
NEW_USERNAME=""
NEW_PASSWORD=""
ASSUME_YES=0

usage() {
  echo "Usage: $0 <current-username> [--username <new>] [--password <new>] [--yes]" >&2
  echo "Example: $0 oldname --username newname --password 's3cret'" >&2
  echo "         $0 oldname --password 's3cret' --yes" >&2
  exit 1
}

if [[ -z "$CURRENT_USERNAME" || "$CURRENT_USERNAME" == "-h" || "$CURRENT_USERNAME" == "--help" ]]; then
  usage
fi

shift || true
while [[ $# -gt 0 ]]; do
  case "$1" in
    --username|-u)
      [[ $# -ge 2 ]] || { echo "Error: $1 needs a value" >&2; exit 1; }
      NEW_USERNAME="$2"
      shift 2
      ;;
    --password|-p)
      [[ $# -ge 2 ]] || { echo "Error: $1 needs a value" >&2; exit 1; }
      NEW_PASSWORD="$2"
      shift 2
      ;;
    --yes|-y)
      ASSUME_YES=1
      shift
      ;;
    *)
      echo "Error: unknown argument: $1" >&2
      usage
      ;;
  esac
done

if [[ -z "$NEW_USERNAME" && -z "$NEW_PASSWORD" ]]; then
  echo "Error: provide at least --username and/or --password" >&2
  usage
fi

if ! command -v docker >/dev/null 2>&1; then
  echo "Error: docker not found" >&2
  exit 1
fi

if [[ -z "$("${COMPOSE[@]}" ps -q --status running "$SERVICE" 2>/dev/null || true)" ]]; then
  echo "Error: compose service '$SERVICE' is not running" >&2
  echo "Tip: docker compose --env-file .env.prod up -d" >&2
  exit 1
fi

run_node() {
  local mode="$1"
  "${COMPOSE[@]}" exec -T \
    -e UPD_CURRENT="$CURRENT_USERNAME" \
    -e UPD_NEW_USERNAME="$NEW_USERNAME" \
    -e UPD_NEW_PASSWORD="$NEW_PASSWORD" \
    -e UPD_MODE="$mode" \
    "$SERVICE" node <<'NODE'
const { PrismaClient } = require('@prisma/client');
const bcrypt = require('bcrypt');
const prisma = new PrismaClient();

(async () => {
  const currentUsername = process.env.UPD_CURRENT;
  const newUsername = (process.env.UPD_NEW_USERNAME || '').trim();
  const newPassword = process.env.UPD_NEW_PASSWORD || '';
  const mode = process.env.UPD_MODE;

  const user = await prisma.user.findUnique({
    where: { username: currentUsername },
    select: {
      id: true,
      username: true,
      role: true,
      balance: true,
      createdAt: true,
    },
  });

  if (!user) {
    console.error(`Error: no user with username '${currentUsername}'`);
    process.exit(2);
  }

  if (mode === 'info') {
    console.log(`id:        ${user.id}`);
    console.log(`username:  ${user.username}`);
    console.log(`role:      ${user.role}`);
    console.log(`balance:   ${user.balance.toString()}`);
    console.log(`createdAt: ${user.createdAt.toISOString()}`);
    if (newUsername) console.log(`new user:  ${newUsername}`);
    if (newPassword) console.log(`password:  (will be changed)`);
    process.exit(0);
  }

  if (mode !== 'update') {
    console.error(`Unknown mode: ${mode}`);
    process.exit(1);
  }

  const data = {};

  if (newUsername) {
    if (newUsername.length < 3 || newUsername.length > 32) {
      console.error('Error: username must be 3–32 characters');
      process.exit(1);
    }
    if (!/^[a-zA-Z0-9_]+$/.test(newUsername)) {
      console.error('Error: username may only contain letters, numbers, and underscores');
      process.exit(1);
    }
    if (newUsername !== user.username) {
      const taken = await prisma.user.findUnique({ where: { username: newUsername } });
      if (taken) {
        console.error(`Error: username '${newUsername}' already taken`);
        process.exit(1);
      }
      data.username = newUsername;
    }
  }

  if (newPassword) {
    if (newPassword.length < 6) {
      console.error('Error: password must be at least 6 characters');
      process.exit(1);
    }
    data.passwordHash = await bcrypt.hash(newPassword, 10);
  }

  if (Object.keys(data).length === 0) {
    console.log('Nothing to change (same username, no password).');
    process.exit(0);
  }

  const updated = await prisma.user.update({
    where: { id: user.id },
    data,
    select: { id: true, username: true },
  });

  const parts = [];
  if (data.username) parts.push(`username → ${updated.username}`);
  if (data.passwordHash) parts.push('password updated');
  console.log(`Updated user id=${updated.id}: ${parts.join(', ')}`);
})().catch((e) => {
  console.error(e);
  process.exit(1);
}).finally(() => prisma.$disconnect());
NODE
}

echo "==> Looking up user '$CURRENT_USERNAME' …"
set +e
INFO_OUT="$(run_node info 2>&1)"
INFO_RC=$?
set -e
echo "$INFO_OUT"
if [[ "$INFO_RC" -ne 0 ]]; then
  exit "$INFO_RC"
fi

if [[ "$ASSUME_YES" -ne 1 ]]; then
  echo ""
  printf "Apply changes to '%s'? [y/N] " "$CURRENT_USERNAME"
  read -r REPLY
  case "$REPLY" in
    y|Y|yes|YES) ;;
    *)
      echo "Aborted."
      exit 0
      ;;
  esac
fi

echo "==> Updating …"
run_node update
echo "==> Done."
