#!/usr/bin/env bash
# Acquire a client-credentials bearer token for a ComputerGuru MSP app tier.
# Usage: get-token.sh <tenant-id-or-domain> <tier>
#
# Tiers and their app + resource scope:
#   investigator       ComputerGuru Security Investigator -> Graph API  (read-only breach checks)
#   investigator-exo   ComputerGuru Security Investigator -> Exchange Online  (EXO read: Get-InboxRule, Get-Mailbox)
#   exchange-op        ComputerGuru Exchange Operator     -> Exchange Online  (EXO write: Set-Mailbox, Remove-InboxRule, revoke sessions)
#   user-manager       ComputerGuru User Manager          -> Graph API  (user create/update/disable, license assign, MFA reset)
#   tenant-admin       ComputerGuru Tenant Admin          -> Graph API  (app roles, CA policy, directory write — high privilege)
#   defender           ComputerGuru Defender Add-on       -> Defender ATP API  (MDE-licensed tenants only)
#
# Output (stdout): bearer token. Exits 0 on success.
# Cache: /tmp/remediation-tool/{tenant-id}/{tier}.jwt  TTL 55 minutes.
set -euo pipefail

TARGET="${1:?usage: get-token.sh <tenant-id|domain> <tier>}"
TIER="${2:?usage: get-token.sh <tenant-id|domain> <tier>}"

# Resolve domain to tenant GUID if needed
if [[ "$TARGET" =~ ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ ]]; then
  TENANT_ID="$TARGET"
else
  SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
  TENANT_ID=$("$SCRIPT_DIR/resolve-tenant.sh" "$TARGET")
fi

# Map tier -> client_id, vault SOPS path, resource scope
case "$TIER" in
  investigator)
    CLIENT_ID="bfbc12a4-f0dd-4e12-b06d-997e7271e10c"
    VAULT_PATH="msp-tools/computerguru-security-investigator.sops.yaml"
    SCOPE_URL="https://graph.microsoft.com/.default"
    ;;
  investigator-exo)
    CLIENT_ID="bfbc12a4-f0dd-4e12-b06d-997e7271e10c"
    VAULT_PATH="msp-tools/computerguru-security-investigator.sops.yaml"
    SCOPE_URL="https://outlook.office365.com/.default"
    ;;
  exchange-op)
    CLIENT_ID="b43e7342-5b4b-492f-890f-bb5a4f7f40e9"
    VAULT_PATH="msp-tools/computerguru-exchange-operator.sops.yaml"
    SCOPE_URL="https://outlook.office365.com/.default"
    ;;
  user-manager)
    CLIENT_ID="64fac46b-8b44-41ad-93ee-7da03927576c"
    VAULT_PATH="msp-tools/computerguru-user-manager.sops.yaml"
    SCOPE_URL="https://graph.microsoft.com/.default"
    ;;
  tenant-admin)
    CLIENT_ID="709e6eed-0711-4875-9c44-2d3518c47063"
    VAULT_PATH="msp-tools/computerguru-tenant-admin.sops.yaml"
    SCOPE_URL="https://graph.microsoft.com/.default"
    ;;
  tenant-admin-onboard)
    # Same app as tenant-admin; this alias signals the token is being used
    # during initial tenant onboarding (clearer error messages on failure).
    CLIENT_ID="709e6eed-0711-4875-9c44-2d3518c47063"
    VAULT_PATH="msp-tools/computerguru-tenant-admin.sops.yaml"
    SCOPE_URL="https://graph.microsoft.com/.default"
    ;;
  defender)
    CLIENT_ID="dbf8ad1a-54f4-4bb8-8a9e-ea5b9634635b"
    VAULT_PATH="msp-tools/computerguru-defender-addon.sops.yaml"
    SCOPE_URL="https://api.securitycenter.microsoft.com/.default"
    ;;
  intune-manager)
    CLIENT_ID="46986910-aa47-4e5e-b596-f65c6b485abb"
    VAULT_PATH="msp-tools/computerguru-intune-manager.sops.yaml"
    SCOPE_URL="https://graph.microsoft.com/.default"
    ;;
  *)
    echo "ERROR: unknown tier '$TIER'." >&2
    echo "Valid tiers: investigator | investigator-exo | exchange-op | user-manager | tenant-admin | tenant-admin-onboard | defender | intune-manager" >&2
    exit 2
    ;;
esac

CACHE_DIR="/tmp/remediation-tool/$TENANT_ID"
mkdir -p "$CACHE_DIR"
CACHE_FILE="$CACHE_DIR/${TIER}.jwt"

# Return cached token if < 55 minutes old
if [[ -f "$CACHE_FILE" ]] && [[ $(find "$CACHE_FILE" -mmin -55 2>/dev/null) ]]; then
  cat "$CACHE_FILE"
  exit 0
fi

# Locate vault repo via .claude/identity.json (per-machine, gitignored).
# Falls back to VAULT_PATH env var if set.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLAUDETOOLS_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
IDENTITY_FILE="$CLAUDETOOLS_ROOT/.claude/identity.json"

VAULT_ROOT="${VAULT_ROOT_ENV:-}"
if [[ -z "$VAULT_ROOT" && -f "$IDENTITY_FILE" ]]; then
  # Try jq first (handles Unix paths on Windows cleanly)
  if command -v jq >/dev/null 2>&1; then
    VAULT_ROOT=$(jq -r '.vault_path // empty' "$IDENTITY_FILE" 2>/dev/null)
  fi
  # Fall back to Python with Windows path conversion
  if [[ -z "$VAULT_ROOT" ]]; then
    IDENTITY_FILE_WIN=$(cygpath -w "$IDENTITY_FILE" 2>/dev/null || echo "$IDENTITY_FILE")
    for py in py python3 python; do
      if command -v "$py" >/dev/null 2>&1; then
        VAULT_ROOT=$("$py" -c "import json; print(json.load(open(r'${IDENTITY_FILE_WIN}')).get('vault_path',''))" 2>/dev/null) && break
      fi
    done
  fi
fi
[[ -z "$VAULT_ROOT" ]] && { echo "ERROR: vault_path not set in $IDENTITY_FILE and VAULT_ROOT_ENV env var not set" >&2; exit 3; }
[[ ! -d "$VAULT_ROOT" ]] && { echo "ERROR: vault not found at $VAULT_ROOT (check vault_path in $IDENTITY_FILE)" >&2; exit 3; }

SOPS_FILE="$VAULT_ROOT/$VAULT_PATH"
[[ ! -f "$SOPS_FILE" ]] && { echo "ERROR: vault file not found: $SOPS_FILE" >&2; exit 3; }

# Read client secret via vault.sh (fast path), falling back to raw sops+python
CLIENT_SECRET=""
if [[ -f "$VAULT_ROOT/scripts/vault.sh" ]]; then
  # Try both field names — new files use client_secret, legacy used credential
  CLIENT_SECRET=$(bash "$VAULT_ROOT/scripts/vault.sh" get-field "$VAULT_PATH" credentials.client_secret 2>/dev/null | tr -d '\r\n' || true)
  if [[ -z "$CLIENT_SECRET" ]]; then
    CLIENT_SECRET=$(bash "$VAULT_ROOT/scripts/vault.sh" get-field "$VAULT_PATH" credentials.credential 2>/dev/null | tr -d '\r\n' || true)
  fi
fi

if [[ -z "$CLIENT_SECRET" ]]; then
  PYTHON_BIN=""
  for p in py python python3; do command -v "$p" >/dev/null 2>&1 && PYTHON_BIN="$p" && break; done
  [[ -z "$PYTHON_BIN" ]] && { echo "ERROR: vault.sh failed and python unavailable for SOPS fallback" >&2; exit 3; }
  command -v sops >/dev/null 2>&1 || { echo "ERROR: sops not on PATH (needed for fallback decrypt)" >&2; exit 3; }

  CLIENT_SECRET=$(sops -d "$SOPS_FILE" 2>/dev/null | "$PYTHON_BIN" -c "
import sys, re
t = sys.stdin.read()
m = re.search(r'^credentials:\s*\n((?:[ \t]+.*\n)+)', t, re.MULTILINE)
if not m: sys.exit(1)
for line in m.group(1).splitlines():
    line = line.strip()
    if line.startswith('client_secret:') or line.startswith('credential:'):
        print(line.split(':', 1)[1].strip().strip('\"').strip(\"'\"))
        break
" | tr -d '\r\n')
fi

[[ -z "$CLIENT_SECRET" ]] && {
  echo "ERROR: could not read secret from $VAULT_PATH" >&2
  echo "  Check field: credentials.client_secret  (or credentials.credential for older entries)" >&2
  exit 4
}

# Request token
RESP=$(curl -s --max-time 15 -X POST \
  "https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token" \
  --data-urlencode "client_id=${CLIENT_ID}" \
  --data-urlencode "client_secret=${CLIENT_SECRET}" \
  --data-urlencode "scope=${SCOPE_URL}" \
  --data-urlencode "grant_type=client_credentials")

TOKEN=$(echo "$RESP" | jq -r '.access_token // empty')

if [[ -z "$TOKEN" ]]; then
  ERROR_CODE=$(echo "$RESP" | jq -r '.error_codes[0] // empty' 2>/dev/null || true)
  ERROR_DESC=$(echo "$RESP" | jq -r '.error_description // empty' 2>/dev/null || true)

  # AADSTS7000229 — service principal not found in tenant (not consented)
  if echo "$ERROR_DESC" | grep -qi "7000229\|AADSTS7000229" || [[ "$ERROR_CODE" == "7000229" ]]; then
    echo "ERROR: AADSTS7000229 — app not consented in tenant $TENANT_ID (tier=$TIER)" >&2
    echo "" >&2
    echo "  The '${TIER}' service principal has not been authorized in this tenant." >&2
    echo "  Send this consent URL to the customer Global Admin:" >&2
    echo "" >&2
    echo "  https://login.microsoftonline.com/${TENANT_ID}/adminconsent?client_id=${CLIENT_ID}&redirect_uri=https://azcomputerguru.com&prompt=consent" >&2
    echo "" >&2
    echo "  After the admin accepts, run onboard-tenant.sh to assign required directory roles:" >&2
    SCRIPT_DIR_ERR="$(dirname "${BASH_SOURCE[0]}")"
    echo "  bash ${SCRIPT_DIR_ERR}/onboard-tenant.sh ${TARGET}" >&2
    exit 5
  fi

  echo "ERROR: token request failed (tenant=$TENANT_ID tier=$TIER)" >&2
  echo "$RESP" >&2
  exit 5
fi

echo "$TOKEN" > "$CACHE_FILE"
chmod 600 "$CACHE_FILE" 2>/dev/null || true
echo "$TOKEN"
