#!/usr/bin/env bash
# claude-profile — Launch Claude Code with a profile and engagement mode
#
# Usage:
#   claude-profile                              # interactive: profile + mode
#   claude-profile <profile>                    # profile fixed, mode interactive
#   claude-profile --mode <name>                # mode fixed, profile interactive
#   claude-profile --preset <name>              # all from presets.yaml
#   claude-profile <profile> --mode <name>      # profile + mode fixed
#   claude-profile --dryrun [...]               # show resolved values, do not launch
#   claude-profile [...] -- <claude-args>       # extra args passed through to claude
#
# Spec: specs/claude-profile.spec.md

set -euo pipefail

# === Constants ===

DEFAULT_PROFILE_DIR="$HOME/.claude"
PROFILE_PATTERN_PREFIX="$HOME/.claude-"
SCRIPT_PATH="$(readlink -f "$0")"
SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
MODES_DIR="$REPO_ROOT/data/claude-profile/modes"

# context-load is on PATH (symlinked into ~/sbin from claude-foundations).
# Fall back to a sibling location for testing isolation.
if command -v context-load >/dev/null 2>&1; then
    CONTEXT_LOADER="$(command -v context-load)"
else
    CONTEXT_LOADER="${SCRIPT_DIR}/context-load"
fi

# Driver logical name -> Anthropic model ID
driver_model_id() {
    case "$1" in
        haiku)   echo "claude-haiku-4-5-20251001" ;;
        sonnet)  echo "claude-sonnet-4-6" ;;
        opus)    echo "claude-opus-4-8" ;;
        opus-1m) echo "claude-opus-4-8[1m]" ;;
        fable)   echo "claude-fable-5" ;;
        *)       err "Unknown driver '$1'. Valid: haiku sonnet opus opus-1m fable" ;;
    esac
}

# === CLI parsing ===

PROFILE=""
MODE=""
PROJECT=""
PRESET=""
DRYRUN=0
CLAUDE_ARGS=()

usage() {
    sed -n '2,12p' "$0" | sed 's/^# \?//'
}

while [[ $# -gt 0 ]]; do
    case "$1" in
        --mode)     MODE="${2:-}"; shift 2 ;;
        --preset)   PRESET="${2:-}"; shift 2 ;;
        --project)  PROJECT="${2:-}"; shift 2 ;;
        --dryrun|-n) DRYRUN=1; shift ;;
        --help|-h)  usage; exit 0 ;;
        --)         shift; CLAUDE_ARGS=("$@"); break ;;
        -*)         echo "Unknown flag: $1" >&2; usage >&2; exit 1 ;;
        *)
            if [[ -z "$PROFILE" ]]; then
                PROFILE="$1"
            else
                echo "Unexpected positional argument: $1" >&2
                exit 1
            fi
            shift
            ;;
    esac
done

# Mutual exclusion
if [[ -n "$PRESET" && ( -n "$MODE" || -n "$PROJECT" ) ]]; then
    echo "Error: --preset is mutually exclusive with --mode and --project" >&2
    exit 1
fi

# === Helpers ===

err() { echo "Error: $*" >&2; exit 1; }

dryrun_log() { printf '[dryrun] %s\n' "$*"; }

# Extract a key from the YAML frontmatter of a markdown file.
# Frontmatter must be the first block, delimited by `---` lines.
parse_frontmatter() {
    local file=$1 key=$2
    awk -v key="$key" '
        BEGIN { in_fm=0 }
        NR==1 && /^---$/ { in_fm=1; next }
        in_fm && /^---$/ { exit }
        in_fm {
            if (match($0, "^"key":[[:space:]]*")) {
                value = substr($0, RLENGTH+1)
                gsub(/^"|"$/, "", value)
                print value
                exit
            }
        }
    ' "$file"
}

# Extract the body of a mode file (everything after the second `---`).
mode_body() {
    local file=$1
    awk '
        /^---$/ { fm++; next }
        fm >= 2 { print }
    ' "$file"
}

# Read a named preset block from a presets.yaml file.
# Output: KEY=value lines (one per field), keys uppercased and prefixed PRESET_.
read_preset_block() {
    local file=$1 name=$2
    awk -v target="$name" '
        /^[a-zA-Z][a-zA-Z0-9_-]*:[[:space:]]*$/ {
            block_name = $0
            sub(/:[[:space:]]*$/, "", block_name)
            in_block = (block_name == target)
            next
        }
        /^[a-zA-Z]/ { in_block = 0 }
        in_block && /^[[:space:]]+[a-zA-Z]/ {
            line = $0
            sub(/^[[:space:]]+/, "", line)
            key = line; value = line
            sub(/:.*$/, "", key)
            sub(/^[^:]+:[[:space:]]*/, "", value)
            gsub(/"/, "", value)
            up = toupper(key)
            print "PRESET_" up "=" value
        }
    ' "$file"
}

# List available modes (sorted), printing a numbered menu.
# Returns the array of mode names via the global MODE_LIST.
MODE_LIST=()
list_modes() {
    MODE_LIST=()
    local i=1
    for f in "$MODES_DIR"/*.md; do
        [[ -f "$f" ]] || continue
        local name purpose
        name=$(basename "$f" .md)
        purpose=$(awk '/^## Purpose$/{getline; getline; print; exit}' "$f")
        printf "  %d) %-7s — %s\n" "$i" "$name" "$purpose"
        MODE_LIST+=("$name")
        i=$((i + 1))
    done
}

resolve_profile_dir() {
    local name=$1
    if [[ "$name" == "default" ]]; then
        echo "$DEFAULT_PROFILE_DIR"
    else
        echo "${PROFILE_PATTERN_PREFIX}${name}"
    fi
}

# WezTerm theming (existing behaviour, unchanged)
set_wezterm_profile() {
    local profile_name="$1"
    printf '\e]1337;SetUserVar=%s=%s\a' CLAUDE_PROFILE "$(printf '%s' "$profile_name" | base64)"
}
reset_wezterm_profile() {
    printf '\e]1337;SetUserVar=%s=%s\a' CLAUDE_PROFILE "$(printf '%s' '_reset' | base64)"
}

# === Phase 1: Profile selection ===

# Sanity check on the modes directory before doing any picker work.
[[ -d "$MODES_DIR" ]] || err "Mode files directory not found at $MODES_DIR"

if [[ -n "$PRESET" ]]; then
    # Find the preset across all profile presets.yaml files.
    preset_profile_dir=""
    preset_lines=""
    for candidate in "$DEFAULT_PROFILE_DIR" ${PROFILE_PATTERN_PREFIX}*; do
        [[ -d "$candidate" ]] || continue
        local_presets="$candidate/presets.yaml"
        [[ -f "$local_presets" ]] || continue
        if grep -q "^${PRESET}:[[:space:]]*$" "$local_presets"; then
            preset_profile_dir="$candidate"
            preset_lines=$(read_preset_block "$local_presets" "$PRESET")
            break
        fi
    done
    [[ -n "$preset_profile_dir" ]] || err "Preset '$PRESET' not found in any presets.yaml"

    # Apply preset values
    while IFS='=' read -r k v; do
        [[ -z "$k" ]] && continue
        case "$k" in
            PRESET_PROFILE)      preset_profile_name="$v" ;;
            PRESET_MODE)         MODE="$v" ;;
            PRESET_PROJECT)      PROJECT="$v" ;;
            PRESET_ASYNC)        ASYNC_OVERRIDE="$v" ;;
            PRESET_AUTOLOOP)     AUTOLOOP_OVERRIDE="$v" ;;
        esac
    done <<< "$preset_lines"

    [[ -n "${preset_profile_name:-}" ]] || err "Preset '$PRESET' is missing required field: profile"

    # If user also gave a positional profile, it must match
    if [[ -n "$PROFILE" && "$PROFILE" != "$preset_profile_name" ]]; then
        err "Profile mismatch: positional '$PROFILE', preset specifies '$preset_profile_name'"
    fi
    PROFILE="$preset_profile_name"
    target=$(resolve_profile_dir "$PROFILE")
elif [[ -n "$PROFILE" ]]; then
    target=$(resolve_profile_dir "$PROFILE")
else
    # Interactive profile picker
    echo "Claude Code profiles:"
    echo ""
    echo "  1) default ($DEFAULT_PROFILE_DIR)"
    profiles=("default")
    i=2
    for dir in ${PROFILE_PATTERN_PREFIX}*; do
        [[ -d "$dir" ]] || continue
        name="${dir##*/.claude-}"
        echo "  $i) $name (~/.claude-$name)"
        profiles+=("$name")
        i=$((i + 1))
    done
    echo ""
    if [[ -t 0 ]]; then
        read -rp "Choose profile [1]: " choice
    else
        choice=""
    fi
    choice="${choice:-1}"
    if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#profiles[@]} )); then
        err "Invalid profile choice: $choice"
    fi
    PROFILE="${profiles[$((choice - 1))]}"
    target=$(resolve_profile_dir "$PROFILE")
fi

[[ -d "$target" ]] || err "Profile directory not found: $target"

# Apply WezTerm theme immediately so the terminal changes during the mode picker.
# Read theme name from profile's wezterm-theme file, falling back to the profile name.
if [[ -f "$target/wezterm-theme" ]]; then
    WEZTERM_THEME=$(cat "$target/wezterm-theme")
else
    WEZTERM_THEME="$PROFILE"
fi
trap reset_wezterm_profile EXIT
set_wezterm_profile "$WEZTERM_THEME"

# === Phase 2: Mode selection ===

if [[ -z "$MODE" ]]; then
    # Determine default from last-mode file
    last_mode_file="$target/last-mode"
    default_mode="quick"
    if [[ -f "$last_mode_file" ]]; then
        candidate=$(cat "$last_mode_file")
        if [[ -f "$MODES_DIR/$candidate.md" ]]; then
            default_mode="$candidate"
        fi
    fi

    echo ""
    echo "Engagement modes:"
    list_modes
    echo ""
    if [[ -t 0 ]]; then
        read -rp "Choose mode [$default_mode]: " mode_choice
    else
        mode_choice=""
    fi
    mode_choice="${mode_choice:-$default_mode}"

    if [[ "$mode_choice" =~ ^[0-9]+$ ]]; then
        if (( mode_choice < 1 || mode_choice > ${#MODE_LIST[@]} )); then
            err "Invalid mode choice: $mode_choice"
        fi
        MODE="${MODE_LIST[$((mode_choice - 1))]}"
    else
        MODE="$mode_choice"
    fi
fi

mode_file="$MODES_DIR/$MODE.md"
[[ -f "$mode_file" ]] || err "Mode file not found: $mode_file"

# Parse frontmatter
DRIVER=$(parse_frontmatter "$mode_file" "driver")
TAG=$(parse_frontmatter "$mode_file" "tag")
ASYNC_OK=$(parse_frontmatter "$mode_file" "async_ok")
AUTOLOOP=$(parse_frontmatter "$mode_file" "autoloop")
PLAN_MODE_AUTO=$(parse_frontmatter "$mode_file" "plan_mode_auto")
SPEC_DRIVEN=$(parse_frontmatter "$mode_file" "spec_driven")
ESCALATES_TO=$(parse_frontmatter "$mode_file" "escalates_to")

# Validate required frontmatter fields
for field_name in DRIVER TAG ASYNC_OK; do
    if [[ -z "${!field_name}" ]]; then
        err "Mode file $mode_file is missing required frontmatter field: $(echo "$field_name" | tr '[:upper:]' '[:lower:]')"
    fi
done

# Apply preset overrides for async/autoloop
[[ -n "${ASYNC_OVERRIDE:-}" ]] && ASYNC_OK="$ASYNC_OVERRIDE"
[[ -n "${AUTOLOOP_OVERRIDE:-}" ]] && {
    if [[ "$AUTOLOOP_OVERRIDE" == "yes" || "$AUTOLOOP_OVERRIDE" == "no" ]]; then
        [[ "$AUTOLOOP_OVERRIDE" == "no" ]] && AUTOLOOP="none"
    fi
}

# === Phase 2.5: Resolve model ID ===

# Default: Anthropic model from driver
MODEL_ID=$(driver_model_id "$DRIVER")

# Override from profile's provider.env if present
PROVIDER_ENV="$target/provider.env"
if [[ -f "$PROVIDER_ENV" ]]; then
    # Source provider.env into a subshell-safe set of variables.
    # We parse key=value lines rather than sourcing directly to avoid side effects.
    while IFS='=' read -r key value; do
        [[ -z "$key" || "$key" == \#* ]] && continue
        case "$key" in
            MODEL_ID)              MODEL_ID="$value" ;;
            ANTHROPIC_BASE_URL)    PROVIDER_BASE_URL="$value" ;;
            ANTHROPIC_API_KEY_FILE) PROVIDER_API_KEY_FILE="$value" ;;
            *)                     PROVIDER_EXTRA_ENV+=("$key=$value") ;;
        esac
    done < "$PROVIDER_ENV"
fi

# === Phase 3: Launch ===

# Validate context-load exists
[[ -x "$CONTEXT_LOADER" ]] || err "context-load not found at $CONTEXT_LOADER"

# Build active-mode.env content
active_env=$(cat <<EOF
CLAUDE_MODE=$MODE
CLAUDE_MODE_TAG=$TAG
CLAUDE_DRIVER=$DRIVER
CLAUDE_ESCALATES_TO=${ESCALATES_TO:-none}
CLAUDE_PROJECT=${PROJECT:-}
CLAUDE_ASYNC_OK=$ASYNC_OK
CLAUDE_AUTOLOOP=$AUTOLOOP
CLAUDE_PLAN_MODE_AUTO=$PLAN_MODE_AUTO
CLAUDE_SPEC_DRIVEN=$SPEC_DRIVEN
CLAUDE_MODE_FILE=$mode_file
EOF
)

# Mode body (everything after the frontmatter)
body=$(mode_body "$mode_file")
if [[ -z "$body" ]]; then
    err "Mode file has no body — nothing to load into the system prompt: $mode_file"
fi

# Pre-flight context-load (do this even in dryrun so length numbers are accurate,
# but suppress side effects — context-load is read-only).
context=$("$CONTEXT_LOADER")

combined_context="${context}

# === Engagement Mode: ${MODE} ===

${body}"

if (( DRYRUN )); then
    dryrun_log "Profile:         $PROFILE ($target)"
    dryrun_log "Mode:            $MODE"
    dryrun_log "Mode file:       $mode_file"
    dryrun_log "Driver:          $DRIVER"
    dryrun_log "Model ID:        $MODEL_ID"
    [[ -n "${PROVIDER_BASE_URL:-}" ]] && dryrun_log "Base URL:        $PROVIDER_BASE_URL"
    [[ -n "${PROVIDER_API_KEY_FILE:-}" ]] && dryrun_log "API key file:    $PROVIDER_API_KEY_FILE"
    [[ -f "$PROVIDER_ENV" ]] && dryrun_log "Provider env:    $PROVIDER_ENV"
    dryrun_log "Project:         ${PROJECT:-<not set, will be picked by CLAUDE.md>}"
    dryrun_log "Async OK:        $ASYNC_OK"
    dryrun_log "Autoloop:        $AUTOLOOP"
    dryrun_log "Escalates to:    ${ESCALATES_TO:-none}"
    dryrun_log "Plan mode auto:  $PLAN_MODE_AUTO"
    dryrun_log "Spec driven:     $SPEC_DRIVEN"
    dryrun_log "Would write:     $target/active-mode.env"
    dryrun_log "Would write:     $target/last-mode"
    if (( ${#CLAUDE_ARGS[@]} > 0 )); then
        dryrun_log "Would execute:   claude --model $MODEL_ID --append-system-prompt <context+mode> ${CLAUDE_ARGS[*]}"
    else
        dryrun_log "Would execute:   claude --model $MODEL_ID --append-system-prompt <context+mode>"
    fi
    dryrun_log "Context length:  ${#combined_context} chars (${#context} from context-load + ${#body} from mode body)"
    dryrun_log "No changes made."
    exit 0
fi

# === Real launch from here on ===

export CLAUDE_CONFIG_DIR="$target"

# Disable adaptive thinking (existing behaviour)
export CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1

# Ensure proper colour support
export COLORTERM=truecolor

# Export provider env vars from profile's provider.env
if [[ -n "${PROVIDER_BASE_URL:-}" ]]; then
    export ANTHROPIC_BASE_URL="$PROVIDER_BASE_URL"
fi
if [[ -n "${PROVIDER_API_KEY_FILE:-}" ]]; then
    expanded_key_file="${PROVIDER_API_KEY_FILE/#\~/$HOME}"
    [[ -f "$expanded_key_file" ]] || err "API key file not found: $expanded_key_file"
    api_key=$(cat "$expanded_key_file")
    export ANTHROPIC_API_KEY="$api_key"
fi
for entry in "${PROVIDER_EXTRA_ENV[@]+"${PROVIDER_EXTRA_ENV[@]}"}"; do
    [[ -n "$entry" ]] && export "$entry"
done

# Write state files
printf '%s\n' "$active_env" > "$target/active-mode.env"
printf '%s\n' "$MODE" > "$target/last-mode"

echo "Launching: $PROFILE / $MODE${PROJECT:+ / $PROJECT}"
exec claude --model "$MODEL_ID" --append-system-prompt "$combined_context" "${CLAUDE_ARGS[@]}"
