#!/bin/bash
set -euo pipefail

KC_BASE="${KC_BASE:?}"
KC_REALM="${KC_REALM:?}"
KC_ADMIN_USER="${KC_ADMIN_USER:?}"
KC_ADMIN_PASS="${KC_ADMIN_PASS:?}"
MAILU_DOMAIN="${MAILU_DOMAIN:?}"
ONLY_USERNAME="${ONLY_USERNAME:-}"
POSTGRES_HOST="${POSTGRES_HOST:-}"
POSTGRES_DB="${POSTGRES_DB:-}"
POSTGRES_USER="${POSTGRES_USER:-}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-}"

if ! command -v jq >/dev/null 2>&1; then
  apt-get update && apt-get install -y jq curl >/dev/null
fi

ensure_psql() {
  if command -v psql >/dev/null 2>&1; then
    return 0
  fi
  apt-get update && apt-get install -y postgresql-client >/dev/null
}

set_editor_mode_richtext() {
  local ids=("$@")

  if [[ ${#ids[@]} -eq 0 ]]; then
    return 0
  fi

  if [[ -z "${POSTGRES_HOST}" || -z "${POSTGRES_DB}" || -z "${POSTGRES_USER}" || -z "${POSTGRES_PASSWORD}" ]]; then
    echo "WARN: missing postgres env; cannot update mail editor_mode" >&2
    return 0
  fi

  ensure_psql

  local ids_csv
  ids_csv=$(IFS=,; echo "${ids[*]}")

  PGPASSWORD="${POSTGRES_PASSWORD}" psql \
    -h "${POSTGRES_HOST}" \
    -U "${POSTGRES_USER}" \
    -d "${POSTGRES_DB}" \
    -v ON_ERROR_STOP=1 \
    -c "UPDATE oc_mail_accounts SET editor_mode='richtext' WHERE id IN (${ids_csv}) AND editor_mode <> 'richtext';" \
    >/dev/null
}

list_mail_accounts() {
  local user_id="${1}"
  local export_out

  # Nextcloud Mail does not provide a list command; export is safe (does not print passwords).
  if ! export_out=$(/usr/sbin/runuser -u www-data -- php occ mail:account:export "${user_id}"); then
    echo "WARN: unable to export mail accounts for ${user_id}; skipping sync for safety" >&2
    return 1
  fi

  awk -v OFS='\t' '
    BEGIN { IGNORECASE=1; id="" }
    $1 == "Account" { id=$2; sub(":", "", id); next }
    $1 == "-" && tolower($2) ~ /^e-?mail:$/ { if (id) print id, $3 }
  ' <<<"${export_out}" | sort -u
}

token=$(
  curl -fsS \
    --data-urlencode "grant_type=password" \
    --data-urlencode "client_id=admin-cli" \
    --data-urlencode "username=${KC_ADMIN_USER}" \
    --data-urlencode "password=${KC_ADMIN_PASS}" \
    "${KC_BASE}/realms/master/protocol/openid-connect/token" | jq -r '.access_token // empty'
)

if [[ -z "${token}" || "${token}" == "null" ]]; then
  echo "Failed to obtain admin token"
  exit 1
fi

cd /var/www/html

kc_users_url="${KC_BASE}/admin/realms/${KC_REALM}/users?max=2000&briefRepresentation=false"
if [[ -n "${ONLY_USERNAME}" ]]; then
  username_q=$(jq -nr --arg v "${ONLY_USERNAME}" '$v|@uri')
  kc_users_url="${KC_BASE}/admin/realms/${KC_REALM}/users?username=${username_q}&exact=true&max=1&briefRepresentation=false"
fi

users=$(curl -fsS -H "Authorization: Bearer ${token}" "${kc_users_url}")
if ! jq -e 'type == "array"' >/dev/null 2>&1 <<<"${users}"; then
  echo "ERROR: Keycloak user list is not an array; aborting sync" >&2
  exit 1
fi

kc_set_user_mail_meta() {
  local user_id="${1}"
  local primary_email="${2}"
  local mailu_account_count="${3}"
  local synced_at="${4}"

  # Fetch the full user representation so we don't accidentally clobber attributes.
  local user_json updated_json
  if ! user_json=$(curl -fsS -H "Authorization: Bearer ${token}" \
    "${KC_BASE}/admin/realms/${KC_REALM}/users/${user_id}"); then
    echo "WARN: unable to fetch Keycloak user ${user_id} for metadata writeback" >&2
    return 1
  fi

  updated_json=$(
    jq -c \
      --arg primary_email "${primary_email}" \
      --arg mailu_account_count "${mailu_account_count}" \
      --arg synced_at "${synced_at}" \
      '
        .attributes = (.attributes // {}) |
        .attributes.nextcloud_mail_primary_email = [$primary_email] |
        .attributes.nextcloud_mail_account_count = [$mailu_account_count] |
        .attributes.nextcloud_mail_synced_at = [$synced_at] |
        del(.access)
      ' <<<"${user_json}"
  )

  curl -fsS -X PUT \
    -H "Authorization: Bearer ${token}" \
    -H "Content-Type: application/json" \
    -d "${updated_json}" \
    "${KC_BASE}/admin/realms/${KC_REALM}/users/${user_id}" >/dev/null
}

while read -r user; do
  user_id=$(jq -r '.id' <<<"${user}")
  username=$(jq -r '.username' <<<"${user}")
  keycloak_email=$(echo "${user}" | jq -r '.email // empty')
  mailu_email=$(echo "${user}" | jq -r '(.attributes.mailu_email[0] // .attributes.mailu_email // empty)')
  app_pw=$(echo "${user}" | jq -r '(.attributes.mailu_app_password[0] // .attributes.mailu_app_password // empty)')

  if [[ -z "${mailu_email}" ]]; then
    if [[ -n "${keycloak_email}" && "${keycloak_email,,}" == *"@${MAILU_DOMAIN,,}" ]]; then
      mailu_email="${keycloak_email}"
    else
      mailu_email="${username}@${MAILU_DOMAIN}"
    fi
  fi

  [[ -z "${mailu_email}" || -z "${app_pw}" ]] && continue

  if ! accounts=$(list_mail_accounts "${username}"); then
    continue
  fi

  # Manage only internal Mailu-domain accounts; leave any external accounts untouched.
  mailu_accounts=$(awk -v d="${MAILU_DOMAIN,,}" 'tolower($2) ~ ("@" d "$") {print}' <<<"${accounts}" || true)

  desired_email="${mailu_email}"
  primary_id=""
  primary_email=""

  if [[ -n "${mailu_accounts}" ]]; then
    while IFS=$'\t' read -r account_id account_email; do
      if [[ -z "${primary_id}" ]]; then
        primary_id="${account_id}"
        primary_email="${account_email}"
      fi
      if [[ "${account_email,,}" == "${desired_email,,}" ]]; then
        primary_id="${account_id}"
        primary_email="${account_email}"
        break
      fi
    done <<<"${mailu_accounts}"

    echo "Updating ${username} mail account ${primary_id} (${primary_email})"
    /usr/sbin/runuser -u www-data -- php occ mail:account:update -q "${primary_id}" \
      --name "${username}" \
      --email "${desired_email}" \
      --imap-host mail.bstein.dev \
      --imap-port 993 \
      --imap-ssl-mode ssl \
      --imap-user "${desired_email}" \
      --imap-password "${app_pw}" \
      --smtp-host mail.bstein.dev \
      --smtp-port 587 \
      --smtp-ssl-mode tls \
      --smtp-user "${desired_email}" \
      --smtp-password "${app_pw}" \
      --auth-method password >/dev/null 2>&1 || true

    # Remove any extra Mailu-domain accounts for this user to prevent duplicates.
    while IFS=$'\t' read -r account_id account_email; do
      if [[ "${account_id}" == "${primary_id}" ]]; then
        continue
      fi
      echo "Deleting extra mail account ${account_id} (${account_email})"
      /usr/sbin/runuser -u www-data -- php occ mail:account:delete -q "${account_id}" >/dev/null 2>&1 || true
    done <<<"${mailu_accounts}"
  else
    echo "Creating mail account for ${username} (${desired_email})"
    /usr/sbin/runuser -u www-data -- php occ mail:account:create -q \
      "${username}" "${username}" "${desired_email}" \
      mail.bstein.dev 993 ssl "${desired_email}" "${app_pw}" \
      mail.bstein.dev 587 tls "${desired_email}" "${app_pw}" password >/dev/null 2>&1 || true
  fi

  # Write non-secret metadata back to Keycloak for UI introspection and onboarding gating.
  synced_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
  if accounts_after=$(list_mail_accounts "${username}"); then
    mailu_accounts_after=$(awk -v d="${MAILU_DOMAIN,,}" 'tolower($2) ~ ("@" d "$") {print}' <<<"${accounts_after}" || true)
    if [[ -n "${mailu_accounts_after}" ]]; then
      mailu_account_count=$(printf '%s\n' "${mailu_accounts_after}" | wc -l | tr -d ' ')
    else
      mailu_account_count="0"
    fi
    primary_email_after=""
    editor_mode_ids=()
    if [[ -n "${mailu_accounts_after}" ]]; then
      while IFS=$'\t' read -r _account_id account_email; do
        editor_mode_ids+=("${_account_id}")
        if [[ "${account_email,,}" == "${desired_email,,}" ]]; then
          primary_email_after="${account_email}"
          break
        fi
        if [[ -z "${primary_email_after}" ]]; then
          primary_email_after="${account_email}"
        fi
      done <<<"${mailu_accounts_after}"
    fi
    set_editor_mode_richtext "${editor_mode_ids[@]}"
  else
    mailu_account_count="0"
    primary_email_after=""
  fi

  kc_set_user_mail_meta "${user_id}" "${primary_email_after}" "${mailu_account_count}" "${synced_at}" || true
done < <(jq -c '.[]' <<<"${users}")
