#!/usr/bin/env bash

# Multi-Account Switcher for Claude Code
# Simple tool to manage and switch between multiple Claude Code accounts

set -euo pipefail

# Configuration
readonly BACKUP_DIR="$HOME/.claude-switch-backup"
readonly SEQUENCE_FILE="$BACKUP_DIR/sequence.json"
readonly USAGE_API_URL="https://api.anthropic.com/api/oauth/usage"
readonly TOKEN_REFRESH_URL="https://console.anthropic.com/v1/oauth/token"
readonly OAUTH_CLIENT_ID="9d1c250a-e61b-44d9-88ed-5944d1962f5e"
readonly TOKEN_REFRESH_BUFFER=300  # Refresh if expiring within 5 minutes

# Container detection
is_running_in_container() {
    # Check for Docker environment file
    if [[ -f /.dockerenv ]]; then
        return 0
    fi

    # Check cgroup for container indicators
    if [[ -f /proc/1/cgroup ]] && grep -q 'docker\|lxc\|containerd\|kubepods' /proc/1/cgroup 2>/dev/null; then
        return 0
    fi

    # Check mount info for container filesystems
    if [[ -f /proc/self/mountinfo ]] && grep -q 'docker\|overlay' /proc/self/mountinfo 2>/dev/null; then
        return 0
    fi

    # Check for common container environment variables
    if [[ -n "${CONTAINER:-}" ]] || [[ -n "${container:-}" ]]; then
        return 0
    fi

    return 1
}

# Platform detection
detect_platform() {
    case "$(uname -s)" in
        Darwin) echo "macos" ;;
        Linux)
            if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then
                echo "wsl"
            else
                echo "linux"
            fi
            ;;
        *) echo "unknown" ;;
    esac
}

# Get Claude configuration file path with fallback
get_claude_config_path() {
    local primary_config="$HOME/.claude/.claude.json"
    local fallback_config="$HOME/.claude.json"

    # Check primary location first
    if [[ -f "$primary_config" ]]; then
        # Verify it has valid oauthAccount structure
        if jq -e '.oauthAccount' "$primary_config" >/dev/null 2>&1; then
            echo "$primary_config"
            return
        fi
    fi

    # Fallback to standard location
    echo "$fallback_config"
}

# Basic validation that JSON is valid
validate_json() {
    local file="$1"
    if ! jq . "$file" >/dev/null 2>&1; then
        echo "Error: Invalid JSON in $file"
        return 1
    fi
}

# Email validation function
validate_email() {
    local email="$1"
    # Use robust regex for email validation
    if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
        return 0
    else
        return 1
    fi
}

# Account identifier resolution function
resolve_account_identifier() {
    local identifier="$1"
    if [[ "$identifier" =~ ^[0-9]+$ ]]; then
        echo "$identifier"  # It's a number
    else
        # Look up account number by email
        local account_num
        account_num=$(jq -r --arg email "$identifier" '.accounts | to_entries[] | select(.value.email == $email) | .key' "$SEQUENCE_FILE" 2>/dev/null)
        if [[ -n "$account_num" && "$account_num" != "null" ]]; then
            echo "$account_num"
        else
            echo ""
        fi
    fi
}

# Safe JSON write with validation
write_json() {
    local file="$1"
    local content="$2"
    local temp_file
    temp_file=$(mktemp "${file}.XXXXXX")

    echo "$content" > "$temp_file"
    if ! jq . "$temp_file" >/dev/null 2>&1; then
        rm -f "$temp_file"
        echo "Error: Generated invalid JSON"
        return 1
    fi

    mv "$temp_file" "$file"
    chmod 600 "$file"
}

# Check Bash version (4.4+ required)
check_bash_version() {
    local version
    version=$(bash --version | head -n1 | grep -oE '[0-9]+\.[0-9]+' | head -n1)
    if ! awk -v ver="$version" 'BEGIN { exit (ver >= 4.4 ? 0 : 1) }'; then
        echo "Error: Bash 4.4+ required (found $version)"
        exit 1
    fi
}

# Check dependencies
check_dependencies() {
    for cmd in jq curl; do
        if ! command -v "$cmd" >/dev/null 2>&1; then
            echo "Error: Required command '$cmd' not found"
            echo "Install with: apt install $cmd (Linux) or brew install $cmd (macOS)"
            exit 1
        fi
    done
}

# Setup backup directories
setup_directories() {
    mkdir -p "$BACKUP_DIR"/{configs,credentials}
    chmod 700 "$BACKUP_DIR"
    chmod 700 "$BACKUP_DIR"/{configs,credentials}
}

# Claude Code process detection
is_claude_running() {
    # Use pgrep for reliable process detection
    pgrep -x "claude" >/dev/null 2>&1
}

# Wait for Claude Code to close (no timeout - user controlled)
wait_for_claude_close() {
    if ! is_claude_running; then
        return 0
    fi

    echo "Claude Code is running. Please close it first."
    echo "Waiting for Claude Code to close..."

    while is_claude_running; do
        sleep 1
    done

    echo "Claude Code closed. Continuing..."
}

# Get current account info from .claude.json
get_current_account() {
    if [[ ! -f "$(get_claude_config_path)" ]]; then
        echo "none"
        return
    fi

    if ! validate_json "$(get_claude_config_path)"; then
        echo "none"
        return
    fi

    local email
    email=$(jq -r '.oauthAccount.emailAddress // empty' "$(get_claude_config_path)" 2>/dev/null)
    echo "${email:-none}"
}

# Read credentials based on platform
read_credentials() {
    local platform
    platform=$(detect_platform)

    case "$platform" in
        macos)
            security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null || echo ""
            ;;
        linux|wsl)
            if [[ -f "$HOME/.claude/.credentials.json" ]]; then
                cat "$HOME/.claude/.credentials.json"
            else
                echo ""
            fi
            ;;
    esac
}

# Get OAuth access token from credentials
get_access_token() {
    local creds
    creds=$(read_credentials)
    if [[ -z "$creds" ]]; then
        echo ""
        return
    fi
    echo "$creds" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null
}

# Get OAuth refresh token from credentials
get_refresh_token() {
    local creds
    creds=$(read_credentials)
    if [[ -z "$creds" ]]; then
        echo ""
        return
    fi
    echo "$creds" | jq -r '.claudeAiOauth.refreshToken // empty' 2>/dev/null
}

# Get token expiry timestamp from credentials
get_token_expiry() {
    local creds
    creds=$(read_credentials)
    if [[ -z "$creds" ]]; then
        echo ""
        return
    fi
    echo "$creds" | jq -r '.claudeAiOauth.expiresAt // empty' 2>/dev/null
}

# Check if token is expired or expiring soon
is_token_expired() {
    local expiry_timestamp
    expiry_timestamp=$(get_token_expiry)

    if [[ -z "$expiry_timestamp" || "$expiry_timestamp" == "null" ]]; then
        # No expiry info stored, assume valid (will fail on API call if not)
        return 1
    fi

    # expiresAt is stored as milliseconds since epoch
    local now_ms expiry_ms
    now_ms=$(($(date +%s) * 1000))
    expiry_ms="$expiry_timestamp"

    # Token is expired if current time + buffer >= expiry time
    # Convert buffer from seconds to milliseconds
    local buffer_ms=$((TOKEN_REFRESH_BUFFER * 1000))
    if [[ $((now_ms + buffer_ms)) -ge $expiry_ms ]]; then
        return 0  # Token is expired or expiring soon
    fi

    return 1  # Token is still valid
}

# Refresh the OAuth token via API
# Returns: JSON response on success, error message prefixed with "ERROR:" on failure, empty on network error
refresh_oauth_token_api() {
    local refresh_token="$1"

    if [[ -z "$refresh_token" ]]; then
        echo "ERROR:No refresh token provided"
        return 1
    fi

    # Use the same format as Claude Code CLI
    local request_body
    request_body=$(jq -n \
        --arg grant_type "refresh_token" \
        --arg refresh_token "$refresh_token" \
        --arg client_id "$OAUTH_CLIENT_ID" \
        --arg scope "user:inference user:profile" \
        '{grant_type: $grant_type, refresh_token: $refresh_token, client_id: $client_id, scope: $scope}')

    local response
    response=$(curl -s -X POST "$TOKEN_REFRESH_URL" \
        -H "Content-Type: application/json" \
        -H "Accept: application/json" \
        -d "$request_body" 2>/dev/null)

    if [[ -z "$response" ]]; then
        echo "ERROR:No response from server"
        return 1
    fi

    # Check for error in response or HTML response (Cloudflare block)
    if echo "$response" | grep -q '<!DOCTYPE html>'; then
        echo "ERROR:Cloudflare blocked the request"
        return 1
    fi

    # Check for OAuth error response
    local error_type error_desc
    error_type=$(echo "$response" | jq -r '.error // empty' 2>/dev/null)
    if [[ -n "$error_type" ]]; then
        error_desc=$(echo "$response" | jq -r '.error_description // "Unknown error"' 2>/dev/null)
        echo "ERROR:$error_type - $error_desc"
        return 1
    fi

    echo "$response"
    return 0
}

# Refresh the OAuth token
# Returns: JSON response on success, "ERROR:..." message on failure
refresh_oauth_token() {
    local refresh_token="$1"
    local response
    response=$(refresh_oauth_token_api "$refresh_token")

    # Pass through the response (either JSON or ERROR:message)
    echo "$response"

    # Check if it was an error
    if [[ "$response" == ERROR:* ]]; then
        return 1
    fi

    return 0
}

# Update credentials with new token data
update_credentials_with_token() {
    local new_access_token="$1"
    local new_refresh_token="$2"
    local expires_in="$3"

    local creds
    creds=$(read_credentials)

    if [[ -z "$creds" ]]; then
        return 1
    fi

    # Calculate expiry timestamp in MILLISECONDS (not seconds!)
    # Claude Code stores expiresAt as a number in milliseconds
    local now_ms expiry_ms
    now_ms=$(($(date +%s) * 1000))
    expiry_ms=$((now_ms + (expires_in * 1000)))

    # Update credentials JSON with numeric expiresAt
    # IMPORTANT: Use -c for compact output (no newlines) for keychain compatibility
    local updated_creds
    updated_creds=$(echo "$creds" | jq -c \
        --arg access "$new_access_token" \
        --arg refresh "$new_refresh_token" \
        --argjson expiry "$expiry_ms" \
        '.claudeAiOauth.accessToken = $access | .claudeAiOauth.refreshToken = $refresh | .claudeAiOauth.expiresAt = $expiry')

    write_credentials "$updated_creds"
    return 0
}

# Get a valid access token, refreshing if necessary
get_valid_access_token() {
    local access_token refresh_token
    access_token=$(get_access_token)

    if [[ -z "$access_token" ]]; then
        echo ""
        return 1
    fi

    # Check if token needs refresh
    if is_token_expired; then
        refresh_token=$(get_refresh_token)

        if [[ -z "$refresh_token" ]]; then
            # No refresh token available, return current token and hope for the best
            echo "$access_token"
            return 0
        fi

        echo "Token expired, refreshing..." >&2

        local refresh_response
        refresh_response=$(refresh_oauth_token "$refresh_token")

        if [[ -n "$refresh_response" ]]; then
            local new_access new_refresh expires_in
            new_access=$(echo "$refresh_response" | jq -r '.access_token // empty')
            new_refresh=$(echo "$refresh_response" | jq -r '.refresh_token // empty')
            expires_in=$(echo "$refresh_response" | jq -r '.expires_in // 28800')

            if [[ -n "$new_access" ]]; then
                # Use existing refresh token if new one not provided
                if [[ -z "$new_refresh" ]]; then
                    new_refresh="$refresh_token"
                fi

                update_credentials_with_token "$new_access" "$new_refresh" "$expires_in"
                echo "Token refreshed successfully" >&2
                echo "$new_access"
                return 0
            fi
        fi

        echo "Token refresh failed, using existing token" >&2
    fi

    echo "$access_token"
    return 0
}

# Get account credentials (internal helper)
get_account_credentials() {
    local account_num="$1"
    local email="$2"
    local platform creds
    platform=$(detect_platform)

    case "$platform" in
        macos)
            creds=$(security find-generic-password -s "Claude Code-Account-${account_num}-${email}" -w 2>/dev/null || echo "")
            ;;
        linux|wsl)
            local cred_file="$BACKUP_DIR/credentials/.claude-credentials-${account_num}-${email}.json"
            if [[ -f "$cred_file" ]]; then
                creds=$(cat "$cred_file")
            else
                creds=""
            fi
            ;;
    esac

    # Check if creds is hex-encoded (broken format from old bug)
    # and try to decode it
    if [[ -n "$creds" ]] && [[ "$creds" =~ ^[0-9a-fA-F]+$ ]] && [[ ${#creds} -gt 100 ]]; then
        # Try to decode hex
        local decoded
        decoded=$(echo "$creds" | xxd -r -p 2>/dev/null || echo "")
        if [[ -n "$decoded" ]] && echo "$decoded" | jq '.' >/dev/null 2>&1; then
            creds="$decoded"
        fi
    fi

    # Validate JSON - return empty if invalid
    if [[ -n "$creds" ]] && ! echo "$creds" | jq '.' >/dev/null 2>&1; then
        echo ""
        return
    fi

    echo "$creds"
}

# Get OAuth access token for a specific account
get_account_access_token() {
    local account_num="$1"
    local email="$2"
    local creds
    creds=$(get_account_credentials "$account_num" "$email")

    if [[ -z "$creds" ]]; then
        echo ""
        return
    fi
    echo "$creds" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null
}

# Get refresh token for a specific account
get_account_refresh_token() {
    local account_num="$1"
    local email="$2"
    local creds
    creds=$(get_account_credentials "$account_num" "$email")

    if [[ -z "$creds" ]]; then
        echo ""
        return
    fi
    echo "$creds" | jq -r '.claudeAiOauth.refreshToken // empty' 2>/dev/null
}

# Get token expiry for a specific account
get_account_token_expiry() {
    local account_num="$1"
    local email="$2"
    local creds
    creds=$(get_account_credentials "$account_num" "$email")

    if [[ -z "$creds" ]]; then
        echo ""
        return
    fi
    echo "$creds" | jq -r '.claudeAiOauth.expiresAt // empty' 2>/dev/null
}

# Check if account token is expired
is_account_token_expired() {
    local account_num="$1"
    local email="$2"
    local expiry_timestamp
    expiry_timestamp=$(get_account_token_expiry "$account_num" "$email")

    if [[ -z "$expiry_timestamp" || "$expiry_timestamp" == "null" ]]; then
        return 1
    fi

    local now_ms expiry_ms

    # Handle both numeric (milliseconds) and string (ISO 8601) formats
    if [[ "$expiry_timestamp" =~ ^[0-9]+$ ]]; then
        # Numeric format (milliseconds since epoch)
        now_ms=$(($(date +%s) * 1000))
        expiry_ms="$expiry_timestamp"
    else
        # String format (ISO 8601) - convert to milliseconds
        # This handles legacy credentials with string expiresAt
        now_ms=$(($(date +%s) * 1000))
        local expiry_epoch
        if date --version >/dev/null 2>&1; then
            # GNU date
            expiry_epoch=$(date -d "$expiry_timestamp" +%s 2>/dev/null || echo "0")
        else
            # BSD date (macOS)
            local formatted_date
            formatted_date=$(echo "$expiry_timestamp" | sed 's/T/ /; s/[Zz]$//; s/[+-][0-9][0-9]:[0-9][0-9]$//')
            expiry_epoch=$(TZ=UTC date -j -f "%Y-%m-%d %H:%M:%S" "$formatted_date" +%s 2>/dev/null || echo "0")
        fi
        expiry_ms=$((expiry_epoch * 1000))
    fi

    # Token is expired if current time + buffer >= expiry time
    local buffer_ms=$((TOKEN_REFRESH_BUFFER * 1000))
    if [[ $((now_ms + buffer_ms)) -ge $expiry_ms ]]; then
        return 0
    fi

    return 1
}

# Update account credentials with new token data
update_account_credentials_with_token() {
    local account_num="$1"
    local email="$2"
    local new_access_token="$3"
    local new_refresh_token="$4"
    local expires_in="$5"

    local creds
    creds=$(get_account_credentials "$account_num" "$email")

    if [[ -z "$creds" ]]; then
        return 1
    fi

    # Calculate expiry timestamp in MILLISECONDS (not seconds!)
    # Claude Code stores expiresAt as a number in milliseconds
    local now_ms expiry_ms
    now_ms=$(($(date +%s) * 1000))
    expiry_ms=$((now_ms + (expires_in * 1000)))

    # Update credentials JSON with numeric expiresAt
    # IMPORTANT: Use -c for compact output (no newlines) for keychain compatibility
    local updated_creds
    updated_creds=$(echo "$creds" | jq -c \
        --arg access "$new_access_token" \
        --arg refresh "$new_refresh_token" \
        --argjson expiry "$expiry_ms" \
        '.claudeAiOauth.accessToken = $access | .claudeAiOauth.refreshToken = $refresh | .claudeAiOauth.expiresAt = $expiry')

    write_account_credentials "$account_num" "$email" "$updated_creds"
    return 0
}

# Get valid access token for a specific account, refreshing if necessary
get_valid_account_access_token() {
    local account_num="$1"
    local email="$2"
    local access_token refresh_token

    access_token=$(get_account_access_token "$account_num" "$email")

    if [[ -z "$access_token" ]]; then
        echo ""
        return 1
    fi

    if is_account_token_expired "$account_num" "$email"; then
        refresh_token=$(get_account_refresh_token "$account_num" "$email")

        if [[ -z "$refresh_token" ]]; then
            echo "$access_token"
            return 0
        fi

        echo "Token expired for account $account_num, refreshing..." >&2

        local refresh_response
        refresh_response=$(refresh_oauth_token "$refresh_token")

        if [[ -n "$refresh_response" ]]; then
            local new_access new_refresh expires_in
            new_access=$(echo "$refresh_response" | jq -r '.access_token // empty')
            new_refresh=$(echo "$refresh_response" | jq -r '.refresh_token // empty')
            expires_in=$(echo "$refresh_response" | jq -r '.expires_in // 28800')

            if [[ -n "$new_access" ]]; then
                if [[ -z "$new_refresh" ]]; then
                    new_refresh="$refresh_token"
                fi

                update_account_credentials_with_token "$account_num" "$email" "$new_access" "$new_refresh" "$expires_in"
                echo "Token refreshed successfully for account $account_num" >&2
                echo "$new_access"
                return 0
            fi
        fi

        echo "Token refresh failed for account $account_num, using existing token" >&2
    fi

    echo "$access_token"
    return 0
}

# Fetch usage data from Anthropic API
fetch_usage() {
    local token="$1"
    if [[ -z "$token" ]]; then
        echo ""
        return
    fi

    curl -s -X GET "$USAGE_API_URL" \
        -H "Accept: application/json" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $token" \
        -H "anthropic-beta: oauth-2025-04-20" 2>/dev/null
}

# Format time remaining until reset
format_time_remaining() {
    local reset_at="$1"
    if [[ -z "$reset_at" || "$reset_at" == "null" ]]; then
        echo "N/A"
        return
    fi

    local reset_epoch now_epoch diff_seconds
    # Parse ISO 8601 date - handle both GNU and BSD date
    if date --version >/dev/null 2>&1; then
        # GNU date
        reset_epoch=$(date -d "$reset_at" +%s 2>/dev/null || echo "0")
    else
        # BSD date (macOS)
        # Convert ISO 8601 to format BSD date understands
        # The API returns UTC times, so we need to parse as UTC
        local formatted_date
        formatted_date=$(echo "$reset_at" | sed 's/T/ /; s/[Zz]$//; s/[+-][0-9][0-9]:[0-9][0-9]$//')
        # Parse the datetime as UTC by temporarily setting TZ
        reset_epoch=$(TZ=UTC date -j -f "%Y-%m-%d %H:%M:%S" "$formatted_date" +%s 2>/dev/null || echo "0")
    fi

    now_epoch=$(date +%s)
    diff_seconds=$((reset_epoch - now_epoch))

    if [[ $diff_seconds -le 0 ]]; then
        echo "now"
        return
    fi

    local days hours minutes
    days=$((diff_seconds / 86400))
    hours=$(((diff_seconds % 86400) / 3600))
    minutes=$(((diff_seconds % 3600) / 60))

    if [[ $days -gt 0 ]]; then
        echo "${days}d ${hours}h ${minutes}m"
    elif [[ $hours -gt 0 ]]; then
        echo "${hours}h ${minutes}m"
    else
        echo "${minutes}m"
    fi
}

# Format reset time for display
format_reset_time() {
    local reset_at="$1"
    if [[ -z "$reset_at" || "$reset_at" == "null" ]]; then
        echo "N/A"
        return
    fi

    # Parse and format the date in local timezone
    if date --version >/dev/null 2>&1; then
        # GNU date
        date -d "$reset_at" "+%Y-%m-%d %H:%M %Z" 2>/dev/null || echo "$reset_at"
    else
        # BSD date (macOS)
        # The API returns UTC times, so we need to parse as UTC then display in local time
        local formatted_date
        formatted_date=$(echo "$reset_at" | sed 's/T/ /; s/[Zz]$//; s/[+-][0-9][0-9]:[0-9][0-9]$//')
        local epoch
        # Parse the datetime as UTC by temporarily setting TZ
        epoch=$(TZ=UTC date -j -f "%Y-%m-%d %H:%M:%S" "$formatted_date" +%s 2>/dev/null || echo "0")
        if [[ "$epoch" != "0" ]]; then
            # Display in local timezone
            date -j -f "%s" "$epoch" "+%Y-%m-%d %H:%M %Z" 2>/dev/null || echo "$reset_at"
        else
            echo "$reset_at"
        fi
    fi
}

# Create a progress bar
progress_bar() {
    local utilization="$1"
    local width=20
    local filled empty

    # Handle null or empty
    if [[ -z "$utilization" || "$utilization" == "null" ]]; then
        printf '[%s]' "$(printf '%*s' $width '' | tr ' ' '-')"
        return
    fi

    # Round to integer
    local pct
    pct=$(printf "%.0f" "$utilization")

    filled=$((pct * width / 100))
    empty=$((width - filled))

    # Use different colors based on utilization
    local bar_char="█"
    local empty_char="░"

    printf '['
    if [[ $filled -gt 0 ]]; then
        printf '%*s' "$filled" '' | tr ' ' "$bar_char"
    fi
    if [[ $empty -gt 0 ]]; then
        printf '%*s' "$empty" '' | tr ' ' "$empty_char"
    fi
    printf ']'
}

# Display usage for a single account
display_account_usage() {
    local account_num="$1"
    local email="$2"
    local token="$3"
    local is_active="$4"

    local active_marker=""
    if [[ "$is_active" == "true" ]]; then
        active_marker=" (active)"
    fi

    echo ""
    echo "Account $account_num: $email$active_marker"
    echo "$(printf '%*s' 60 '' | tr ' ' '-')"

    if [[ -z "$token" ]]; then
        echo "  Error: No access token available"
        return
    fi

    local usage_data
    usage_data=$(fetch_usage "$token")

    if [[ -z "$usage_data" ]]; then
        echo "  Error: Failed to fetch usage data"
        return
    fi

    # Check for API error
    if echo "$usage_data" | jq -e '.error' >/dev/null 2>&1; then
        local error_msg
        error_msg=$(echo "$usage_data" | jq -r '.error.message // .error // "Unknown error"')
        echo "  Error: $error_msg"
        return
    fi

    # Parse usage data
    local five_hour_util five_hour_reset
    local seven_day_util seven_day_reset
    local seven_day_sonnet_util seven_day_sonnet_reset
    local seven_day_opus_util seven_day_opus_reset
    local extra_usage_enabled extra_usage_limit extra_usage_used extra_usage_util

    five_hour_util=$(echo "$usage_data" | jq -r '.five_hour.utilization // "null"')
    five_hour_reset=$(echo "$usage_data" | jq -r '.five_hour.resets_at // "null"')

    seven_day_util=$(echo "$usage_data" | jq -r '.seven_day.utilization // "null"')
    seven_day_reset=$(echo "$usage_data" | jq -r '.seven_day.resets_at // "null"')

    seven_day_sonnet_util=$(echo "$usage_data" | jq -r '.seven_day_sonnet.utilization // "null"')
    seven_day_sonnet_reset=$(echo "$usage_data" | jq -r '.seven_day_sonnet.resets_at // "null"')

    seven_day_opus_util=$(echo "$usage_data" | jq -r '.seven_day_opus.utilization // "null"')
    seven_day_opus_reset=$(echo "$usage_data" | jq -r '.seven_day_opus.resets_at // "null"')

    extra_usage_enabled=$(echo "$usage_data" | jq -r '.extra_usage.is_enabled // "null"')
    extra_usage_limit=$(echo "$usage_data" | jq -r '.extra_usage.monthly_limit // "null"')
    extra_usage_used=$(echo "$usage_data" | jq -r '.extra_usage.used_credits // "null"')
    extra_usage_util=$(echo "$usage_data" | jq -r '.extra_usage.utilization // "null"')

    # Display 5-hour session usage
    if [[ "$five_hour_util" != "null" ]]; then
        printf "  Session (5h):  %s %5.1f%%  resets in %s\n" \
            "$(progress_bar "$five_hour_util")" \
            "$five_hour_util" \
            "$(format_time_remaining "$five_hour_reset")"
        printf "                 Reset: %s\n" "$(format_reset_time "$five_hour_reset")"
    fi

    # Display 7-day weekly usage
    if [[ "$seven_day_util" != "null" ]]; then
        printf "  Weekly (7d):   %s %5.1f%%  resets in %s\n" \
            "$(progress_bar "$seven_day_util")" \
            "$seven_day_util" \
            "$(format_time_remaining "$seven_day_reset")"
        printf "                 Reset: %s\n" "$(format_reset_time "$seven_day_reset")"
    fi

    # Display 7-day Sonnet usage if available
    if [[ "$seven_day_sonnet_util" != "null" && "$seven_day_sonnet_util" != "0" ]]; then
        printf "  Sonnet (7d):   %s %5.1f%%  resets in %s\n" \
            "$(progress_bar "$seven_day_sonnet_util")" \
            "$seven_day_sonnet_util" \
            "$(format_time_remaining "$seven_day_sonnet_reset")"
    fi

    # Display 7-day Opus usage if available
    if [[ "$seven_day_opus_util" != "null" && "$seven_day_opus_util" != "0" ]]; then
        printf "  Opus (7d):     %s %5.1f%%  resets in %s\n" \
            "$(progress_bar "$seven_day_opus_util")" \
            "$seven_day_opus_util" \
            "$(format_time_remaining "$seven_day_opus_reset")"
    fi

    # Display extra usage if enabled
    if [[ "$extra_usage_enabled" == "true" ]]; then
        echo ""
        printf "  Extra Usage:   %s %5.1f%%  (\$%.2f / \$%.2f)\n" \
            "$(progress_bar "$extra_usage_util")" \
            "$extra_usage_util" \
            "$(echo "$extra_usage_used / 100" | bc -l)" \
            "$(echo "$extra_usage_limit / 100" | bc -l)"
    fi
}

# Show usage for all accounts
cmd_usage() {
    if [[ ! -f "$SEQUENCE_FILE" ]]; then
        # No managed accounts, show usage for current account only
        local current_email
        current_email=$(get_current_account)

        if [[ "$current_email" == "none" ]]; then
            echo "Error: No active Claude account found"
            exit 1
        fi

        local token
        token=$(get_valid_access_token)

        echo "Claude Code Usage"
        echo "================="
        display_account_usage "0" "$current_email" "$token" "true"
        exit 0
    fi

    # Get current active account
    local current_email
    current_email=$(get_current_account)

    echo "Claude Code Usage"
    echo "================="

    # Iterate through all managed accounts
    local account_nums
    account_nums=$(jq -r '.sequence[]' "$SEQUENCE_FILE")

    for account_num in $account_nums; do
        local email is_active token
        email=$(jq -r --arg num "$account_num" '.accounts[$num].email' "$SEQUENCE_FILE")

        if [[ "$email" == "$current_email" ]]; then
            is_active="true"
            token=$(get_valid_access_token)
        else
            is_active="false"
            token=$(get_valid_account_access_token "$account_num" "$email")
        fi

        display_account_usage "$account_num" "$email" "$token" "$is_active"
    done

    echo ""
}

# Write credentials based on platform
write_credentials() {
    local credentials="$1"
    local platform
    platform=$(detect_platform)

    case "$platform" in
        macos)
            security add-generic-password -U -s "Claude Code-credentials" -a "$USER" -w "$credentials" 2>/dev/null
            ;;
        linux|wsl)
            mkdir -p "$HOME/.claude"
            printf '%s' "$credentials" > "$HOME/.claude/.credentials.json"
            chmod 600 "$HOME/.claude/.credentials.json"
            ;;
    esac
}

# Read account credentials from backup
read_account_credentials() {
    local account_num="$1"
    local email="$2"
    local platform
    platform=$(detect_platform)

    case "$platform" in
        macos)
            security find-generic-password -s "Claude Code-Account-${account_num}-${email}" -w 2>/dev/null || echo ""
            ;;
        linux|wsl)
            local cred_file="$BACKUP_DIR/credentials/.claude-credentials-${account_num}-${email}.json"
            if [[ -f "$cred_file" ]]; then
                cat "$cred_file"
            else
                echo ""
            fi
            ;;
    esac
}

# Write account credentials to backup
write_account_credentials() {
    local account_num="$1"
    local email="$2"
    local credentials="$3"
    local platform
    platform=$(detect_platform)

    case "$platform" in
        macos)
            security add-generic-password -U -s "Claude Code-Account-${account_num}-${email}" -a "$USER" -w "$credentials" 2>/dev/null
            ;;
        linux|wsl)
            local cred_file="$BACKUP_DIR/credentials/.claude-credentials-${account_num}-${email}.json"
            printf '%s' "$credentials" > "$cred_file"
            chmod 600 "$cred_file"
            ;;
    esac
}

# Read account config from backup
read_account_config() {
    local account_num="$1"
    local email="$2"
    local config_file="$BACKUP_DIR/configs/.claude-config-${account_num}-${email}.json"

    if [[ -f "$config_file" ]]; then
        cat "$config_file"
    else
        echo ""
    fi
}

# Write account config to backup
write_account_config() {
    local account_num="$1"
    local email="$2"
    local config="$3"
    local config_file="$BACKUP_DIR/configs/.claude-config-${account_num}-${email}.json"

    echo "$config" > "$config_file"
    chmod 600 "$config_file"
}

# Initialize sequence.json if it doesn't exist
init_sequence_file() {
    if [[ ! -f "$SEQUENCE_FILE" ]]; then
        local init_content='{"activeAccountNumber":null,"lastUpdated":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","sequence":[],"accounts":{}}'
        write_json "$SEQUENCE_FILE" "$init_content"
    fi
}

# Get next account number
get_next_account_number() {
    if [[ ! -f "$SEQUENCE_FILE" ]]; then
        echo "1"
        return
    fi

    local max_num
    max_num=$(jq -r '.accounts | keys | map(tonumber) | max // 0' "$SEQUENCE_FILE")
    echo $((max_num + 1))
}

# Check if account exists by email
account_exists() {
    local email="$1"
    if [[ ! -f "$SEQUENCE_FILE" ]]; then
        return 1
    fi

    jq -e --arg email "$email" '.accounts[] | select(.email == $email)' "$SEQUENCE_FILE" >/dev/null 2>&1
}

# Add account
cmd_add_account() {
    setup_directories
    init_sequence_file

    local current_email
    current_email=$(get_current_account)

    if [[ "$current_email" == "none" ]]; then
        echo "Error: No active Claude account found. Please log in first."
        exit 1
    fi

    # Get current credentials and config
    local current_creds current_config account_uuid
    current_creds=$(read_credentials)
    current_config=$(cat "$(get_claude_config_path)")

    if [[ -z "$current_creds" ]]; then
        echo "Error: No credentials found for current account"
        exit 1
    fi

    account_uuid=$(jq -r '.oauthAccount.accountUuid' "$(get_claude_config_path)")

    # Check if account already exists
    if account_exists "$current_email"; then
        # Update existing account credentials
        local account_num
        account_num=$(resolve_account_identifier "$current_email")

        if [[ -z "$account_num" ]]; then
            echo "Error: Failed to resolve account number for $current_email"
            exit 1
        fi

        # Update stored credentials and config
        write_account_credentials "$account_num" "$current_email" "$current_creds"
        write_account_config "$account_num" "$current_email" "$current_config"

        # Update sequence.json with new timestamp
        local updated_sequence
        updated_sequence=$(jq --arg num "$account_num" --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '
            .accounts[$num].updated = $now |
            .activeAccountNumber = ($num | tonumber) |
            .lastUpdated = $now
        ' "$SEQUENCE_FILE")

        write_json "$SEQUENCE_FILE" "$updated_sequence"

        echo "Updated Account $account_num: $current_email (credentials refreshed)"
        exit 0
    fi

    # Add new account
    local account_num
    account_num=$(get_next_account_number)

    # Store backups
    write_account_credentials "$account_num" "$current_email" "$current_creds"
    write_account_config "$account_num" "$current_email" "$current_config"

    # Update sequence.json
    local updated_sequence
    updated_sequence=$(jq --arg num "$account_num" --arg email "$current_email" --arg uuid "$account_uuid" --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '
        .accounts[$num] = {
            email: $email,
            uuid: $uuid,
            added: $now
        } |
        .sequence += [$num | tonumber] |
        .activeAccountNumber = ($num | tonumber) |
        .lastUpdated = $now
    ' "$SEQUENCE_FILE")

    write_json "$SEQUENCE_FILE" "$updated_sequence"

    echo "Added Account $account_num: $current_email"
}

# Remove account
cmd_remove_account() {
    if [[ $# -eq 0 ]]; then
        echo "Usage: $0 --remove-account <account_number|email>"
        exit 1
    fi

    local identifier="$1"
    local account_num

    if [[ ! -f "$SEQUENCE_FILE" ]]; then
        echo "Error: No accounts are managed yet"
        exit 1
    fi

    # Handle email vs numeric identifier
    if [[ "$identifier" =~ ^[0-9]+$ ]]; then
        account_num="$identifier"
    else
        # Validate email format
        if ! validate_email "$identifier"; then
            echo "Error: Invalid email format: $identifier"
            exit 1
        fi

        # Resolve email to account number
        account_num=$(resolve_account_identifier "$identifier")
        if [[ -z "$account_num" ]]; then
            echo "Error: No account found with email: $identifier"
            exit 1
        fi
    fi

    local account_info
    account_info=$(jq -r --arg num "$account_num" '.accounts[$num] // empty' "$SEQUENCE_FILE")

    if [[ -z "$account_info" ]]; then
        echo "Error: Account-$account_num does not exist"
        exit 1
    fi

    local email
    email=$(echo "$account_info" | jq -r '.email')

    local active_account
    active_account=$(jq -r '.activeAccountNumber' "$SEQUENCE_FILE")

    if [[ "$active_account" == "$account_num" ]]; then
        echo "Warning: Account-$account_num ($email) is currently active"
    fi

    echo -n "Are you sure you want to permanently remove Account-$account_num ($email)? [y/N] "
    read -r confirm

    if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
        echo "Cancelled"
        exit 0
    fi

    # Remove backup files
    local platform
    platform=$(detect_platform)
    case "$platform" in
        macos)
            security delete-generic-password -s "Claude Code-Account-${account_num}-${email}" 2>/dev/null || true
            ;;
        linux|wsl)
            rm -f "$BACKUP_DIR/credentials/.claude-credentials-${account_num}-${email}.json"
            ;;
    esac
    rm -f "$BACKUP_DIR/configs/.claude-config-${account_num}-${email}.json"

    # Update sequence.json
    local updated_sequence
    updated_sequence=$(jq --arg num "$account_num" --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '
        del(.accounts[$num]) |
        .sequence = (.sequence | map(select(. != ($num | tonumber)))) |
        .lastUpdated = $now
    ' "$SEQUENCE_FILE")

    write_json "$SEQUENCE_FILE" "$updated_sequence"

    echo "Account-$account_num ($email) has been removed"
}

# First-run setup workflow
first_run_setup() {
    local current_email
    current_email=$(get_current_account)

    if [[ "$current_email" == "none" ]]; then
        echo "No active Claude account found. Please log in first."
        return 1
    fi

    echo -n "No managed accounts found. Add current account ($current_email) to managed list? [Y/n] "
    read -r response

    if [[ "$response" == "n" || "$response" == "N" ]]; then
        echo "Setup cancelled. You can run '$0 --add-account' later."
        return 1
    fi

    cmd_add_account
    return 0
}

# List accounts
cmd_list() {
    if [[ ! -f "$SEQUENCE_FILE" ]]; then
        echo "No accounts are managed yet."
        first_run_setup
        exit 0
    fi

    # Get current active account from .claude.json
    local current_email
    current_email=$(get_current_account)

    # Find which account number corresponds to the current email
    local active_account_num=""
    if [[ "$current_email" != "none" ]]; then
        active_account_num=$(jq -r --arg email "$current_email" '.accounts | to_entries[] | select(.value.email == $email) | .key' "$SEQUENCE_FILE" 2>/dev/null)
    fi

    echo "Accounts:"
    jq -r --arg active "$active_account_num" '
        .sequence[] as $num |
        .accounts["\($num)"] |
        if "\($num)" == $active then
            "  \($num): \(.email) (active)"
        else
            "  \($num): \(.email)"
        end
    ' "$SEQUENCE_FILE"
}

# Switch to next account
cmd_switch() {
    if [[ ! -f "$SEQUENCE_FILE" ]]; then
        echo "Error: No accounts are managed yet"
        exit 1
    fi

    local current_email
    current_email=$(get_current_account)

    if [[ "$current_email" == "none" ]]; then
        echo "Error: No active Claude account found"
        exit 1
    fi

    # Check if current account is managed
    if ! account_exists "$current_email"; then
        echo "Notice: Active account '$current_email' was not managed."
        cmd_add_account
        local account_num
        account_num=$(jq -r '.activeAccountNumber' "$SEQUENCE_FILE")
        echo "It has been automatically added as Account-$account_num."
        echo "Please run './ccswitch.sh --switch' again to switch to the next account."
        exit 0
    fi

    # wait_for_claude_close

    local active_account sequence
    active_account=$(jq -r '.activeAccountNumber' "$SEQUENCE_FILE")
    sequence=($(jq -r '.sequence[]' "$SEQUENCE_FILE"))

    # Find next account in sequence
    local next_account current_index=0
    for i in "${!sequence[@]}"; do
        if [[ "${sequence[i]}" == "$active_account" ]]; then
            current_index=$i
            break
        fi
    done

    next_account="${sequence[$(((current_index + 1) % ${#sequence[@]}))]}"

    perform_switch "$next_account"
}

# Switch to specific account
cmd_switch_to() {
    if [[ $# -eq 0 ]]; then
        echo "Usage: $0 --switch-to <account_number|email>"
        exit 1
    fi

    local identifier="$1"
    local target_account

    if [[ ! -f "$SEQUENCE_FILE" ]]; then
        echo "Error: No accounts are managed yet"
        exit 1
    fi

    # Handle email vs numeric identifier
    if [[ "$identifier" =~ ^[0-9]+$ ]]; then
        target_account="$identifier"
    else
        # Validate email format
        if ! validate_email "$identifier"; then
            echo "Error: Invalid email format: $identifier"
            exit 1
        fi

        # Resolve email to account number
        target_account=$(resolve_account_identifier "$identifier")
        if [[ -z "$target_account" ]]; then
            echo "Error: No account found with email: $identifier"
            exit 1
        fi
    fi

    local account_info
    account_info=$(jq -r --arg num "$target_account" '.accounts[$num] // empty' "$SEQUENCE_FILE")

    if [[ -z "$account_info" ]]; then
        echo "Error: Account-$target_account does not exist"
        exit 1
    fi

    # wait_for_claude_close
    perform_switch "$target_account"
}

# Perform the actual account switch
perform_switch() {
    local target_account="$1"

    # Get current and target account info
    local current_account target_email current_email
    current_account=$(jq -r '.activeAccountNumber' "$SEQUENCE_FILE")
    target_email=$(jq -r --arg num "$target_account" '.accounts[$num].email' "$SEQUENCE_FILE")
    current_email=$(get_current_account)

    # Step 1: Backup current account
    local current_creds current_config
    current_creds=$(read_credentials)
    current_config=$(cat "$(get_claude_config_path)")

    write_account_credentials "$current_account" "$current_email" "$current_creds"
    write_account_config "$current_account" "$current_email" "$current_config"

    # Step 2: Retrieve target account
    local target_creds target_config
    target_creds=$(read_account_credentials "$target_account" "$target_email")
    target_config=$(read_account_config "$target_account" "$target_email")

    if [[ -z "$target_creds" || -z "$target_config" ]]; then
        echo "Error: Missing backup data for Account-$target_account"
        exit 1
    fi

    # Step 3: Activate target account
    write_credentials "$target_creds"

    # Extract oauthAccount from backup and validate
    local oauth_section
    oauth_section=$(echo "$target_config" | jq '.oauthAccount' 2>/dev/null)
    if [[ -z "$oauth_section" || "$oauth_section" == "null" ]]; then
        echo "Error: Invalid oauthAccount in backup"
        exit 1
    fi

    # Merge with current config and validate
    local merged_config
    merged_config=$(jq --argjson oauth "$oauth_section" '.oauthAccount = $oauth' "$(get_claude_config_path)" 2>/dev/null)
    if [[ $? -ne 0 ]]; then
        echo "Error: Failed to merge config"
        exit 1
    fi

    # Use existing safe write_json function
    write_json "$(get_claude_config_path)" "$merged_config"

    # Step 4: Update state
    local updated_sequence
    updated_sequence=$(jq --arg num "$target_account" --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '
        .activeAccountNumber = ($num | tonumber) |
        .lastUpdated = $now
    ' "$SEQUENCE_FILE")

    write_json "$SEQUENCE_FILE" "$updated_sequence"

    echo "Switched to Account-$target_account ($target_email)"
    # Display updated account list
    cmd_list
    echo ""
    echo "Please restart Claude Code to use the new authentication."
    echo ""

}

# Refresh tokens for all accounts
cmd_refresh() {
    echo "Refreshing OAuth tokens..."
    echo ""

    local refreshed=0
    local failed=0
    local skipped=0

    # Check if Claude Code is running
    local claude_is_running=false
    if is_claude_running; then
        claude_is_running=true
        echo "Warning: Claude Code is running. Will skip refreshing the active account."
        echo "         (Refreshing would invalidate tokens that Claude Code is using)"
        echo ""
    fi

    # Refresh current account
    local current_email
    current_email=$(get_current_account)

    if [[ "$current_email" != "none" ]]; then
        local refresh_token
        refresh_token=$(get_refresh_token)

        if [[ -n "$refresh_token" ]]; then
            # Skip refreshing current account if Claude is running
            if [[ "$claude_is_running" == "true" ]]; then
                echo "Skipping current account ($current_email): Claude Code is running"
                echo "         To refresh this account, close Claude Code first and run --refresh again"
                ((skipped++))
            else
                echo -n "Refreshing current account ($current_email)... "
                local refresh_response
                refresh_response=$(refresh_oauth_token "$refresh_token")

                if [[ "$refresh_response" == ERROR:* ]]; then
                    # Extract error message after "ERROR:"
                    local error_msg="${refresh_response#ERROR:}"
                    echo "failed ($error_msg)"
                    ((failed++))
                elif [[ -n "$refresh_response" ]]; then
                    local new_access new_refresh expires_in
                    new_access=$(echo "$refresh_response" | jq -r '.access_token // empty')
                    new_refresh=$(echo "$refresh_response" | jq -r '.refresh_token // empty')
                    expires_in=$(echo "$refresh_response" | jq -r '.expires_in // 28800')

                    if [[ -n "$new_access" ]]; then
                        if [[ -z "$new_refresh" ]]; then
                            new_refresh="$refresh_token"
                        fi
                        update_credentials_with_token "$new_access" "$new_refresh" "$expires_in"
                        echo "done (valid for $((expires_in / 3600))h)"
                        ((refreshed++))
                    else
                        echo "failed (invalid response)"
                        ((failed++))
                    fi
                else
                    echo "failed (no response)"
                    ((failed++))
                fi
            fi
        else
            echo "Skipping current account ($current_email): no refresh token stored"
            ((skipped++))
        fi
    fi

    # Refresh managed accounts (if any)
    if [[ -f "$SEQUENCE_FILE" ]]; then
        local account_nums
        account_nums=$(jq -r '.sequence[]' "$SEQUENCE_FILE")

        for account_num in $account_nums; do
            local email
            email=$(jq -r --arg num "$account_num" '.accounts[$num].email' "$SEQUENCE_FILE")

            # Skip if this is the current account (already refreshed)
            if [[ "$email" == "$current_email" ]]; then
                continue
            fi

            local refresh_token
            refresh_token=$(get_account_refresh_token "$account_num" "$email")

            if [[ -n "$refresh_token" ]]; then
                echo -n "Refreshing account $account_num ($email)... "
                local refresh_response
                refresh_response=$(refresh_oauth_token "$refresh_token")

                if [[ "$refresh_response" == ERROR:* ]]; then
                    local error_msg="${refresh_response#ERROR:}"
                    echo "failed ($error_msg)"
                    ((failed++))
                elif [[ -n "$refresh_response" ]]; then
                    local new_access new_refresh expires_in
                    new_access=$(echo "$refresh_response" | jq -r '.access_token // empty')
                    new_refresh=$(echo "$refresh_response" | jq -r '.refresh_token // empty')
                    expires_in=$(echo "$refresh_response" | jq -r '.expires_in // 28800')

                    if [[ -n "$new_access" ]]; then
                        if [[ -z "$new_refresh" ]]; then
                            new_refresh="$refresh_token"
                        fi
                        update_account_credentials_with_token "$account_num" "$email" "$new_access" "$new_refresh" "$expires_in"
                        echo "done (valid for $((expires_in / 3600))h)"
                        ((refreshed++))
                    else
                        echo "failed (invalid response)"
                        ((failed++))
                    fi
                else
                    echo "failed (no response)"
                    ((failed++))
                fi
            else
                echo "Skipping account $account_num ($email): no refresh token stored"
                ((skipped++))
            fi
        done
    fi

    echo ""
    echo "Refresh complete: $refreshed succeeded, $failed failed, $skipped skipped"

    if [[ $failed -gt 0 ]]; then
        echo ""
        echo "Note: If refresh failed with 'invalid_grant', run 'claude /login' to re-authenticate."
    fi
}

# Show usage
show_usage() {
    echo "Multi-Account Switcher for Claude Code"
    echo "Usage: $0 [COMMAND]"
    echo ""
    echo "Commands:"
    echo "  --add-account                    Add current account to managed accounts"
    echo "  --remove-account <num|email>     Remove account by number or email"
    echo "  --list                           List all managed accounts"
    echo "  --switch                         Rotate to next account in sequence"
    echo "  --switch-to <num|email>          Switch to specific account number or email"
    echo "  --usage                          Show usage limits for all accounts"
    echo "  --refresh                        Refresh OAuth tokens for all accounts"
    echo "  --help                           Show this help message"
    echo ""
    echo "Examples:"
    echo "  $0 --add-account"
    echo "  $0 --list"
    echo "  $0 --switch"
    echo "  $0 --switch-to 2"
    echo "  $0 --switch-to user@example.com"
    echo "  $0 --remove-account user@example.com"
    echo "  $0 --usage"
    echo "  $0 --refresh"
}

# Main script logic
main() {
    # Basic checks - allow root execution in containers
    if [[ $EUID -eq 0 ]] && ! is_running_in_container; then
        echo "Error: Do not run this script as root (unless running in a container)"
        exit 1
    fi

    check_bash_version
    check_dependencies

    case "${1:-}" in
        --add-account)
            cmd_add_account
            ;;
        --remove-account)
            shift
            cmd_remove_account "$@"
            ;;
        --list)
            cmd_list
            ;;
        --switch)
            cmd_switch
            ;;
        --switch-to)
            shift
            cmd_switch_to "$@"
            ;;
        --usage)
            cmd_usage
            ;;
        --refresh)
            cmd_refresh
            ;;
        --help)
            show_usage
            ;;
        "")
            show_usage
            ;;
        *)
            echo "Error: Unknown command '$1'"
            show_usage
            exit 1
            ;;
    esac
}

# Check if script is being sourced or executed
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi
