#!/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>              # both from presets.yaml
#   claude-profile <profile> --mode <name>      # both 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 -> full Claude model ID
declare -A DRIVER_MAP=(
    [haiku]="claude-haiku-4-5-20251001"
    [sonnet]="claude-sonnet-4-6"
    [opus]="claude-opus-4-6"
    [opus-1m]="claude-opus-4-6[1m]"
)

# === 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++))
    done
}

driver_to_model() {
    local d=$1
    local valid="${!DRIVER_MAP[*]}"
    if [[ -z "${DRIVER_MAP[$d]:-}" ]]; then
        err "Unknown driver '$d'. Valid: $valid"
    fi
    echo "${DRIVER_MAP[$d]}"
}

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 (PRESET_PROFILE, PRESET_MODE, etc.)
    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_TIME_HORIZON) TIME_HORIZON="$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++))
    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"

# === 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

# Mode-specific interactive questions (skipped if preset)
if [[ -z "$PRESET" ]]; then
    case "$MODE" in
        deep|hybrid|orch)
            if [[ -z "${TIME_HORIZON:-}" ]]; then
                if [[ -t 0 ]]; then
                    read -rp "Time horizon (minutes/hours/overnight) [hours]: " th
                else
                    th=""
                fi
                TIME_HORIZON="${th:-hours}"
            fi
            ;;
    esac
fi

# Apply preset overrides for async/autoloop
[[ -n "${ASYNC_OVERRIDE:-}" ]] && ASYNC_OK="$ASYNC_OVERRIDE"
[[ -n "${AUTOLOOP_OVERRIDE:-}" ]] && {
    if [[ "$AUTOLOOP_OVERRIDE" == "yes" || "$AUTOLOOP_OVERRIDE" == "no" ]]; then
        : # leave AUTOLOOP as the mode default unless explicitly disabled
        [[ "$AUTOLOOP_OVERRIDE" == "no" ]] && AUTOLOOP="none"
    fi
}

# === Phase 3: Launch ===

MODEL_ID=$(driver_to_model "$DRIVER")

# 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_TIME_HORIZON=${TIME_HORIZON:-}
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 "Driver model:    $MODEL_ID"
    dryrun_log "Project:         ${PROJECT:-<not set, will be picked by CLAUDE.md>}"
    dryrun_log "Time horizon:    ${TIME_HORIZON:-<not asked in this mode>}"
    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 ===

# Set up WezTerm theming and reset trap
trap reset_wezterm_profile EXIT
export CLAUDE_CONFIG_DIR="$target"
set_wezterm_profile "$PROFILE"

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

# 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[@]}"
