#!/usr/bin/env bash
# pi-epic-init <slug> [--from <design-file>] [--title "<title>"] [--base <branch>] [--no-planner]
#
# Bootstraps an epic:
#   - .pi/epics/<NNNN>-<slug>/ from template
#   - epic/<slug> branch off the default branch (override with --base)
#   - Sets STATE.md
#
# Does NOT push the branch (push happens at pi-epic-complete).

set -euo pipefail
# Resolve script dir through symlinks so we can source siblings reliably.
__src="${BASH_SOURCE[0]}"
while [ -L "$__src" ]; do
    __dir="$(cd -P "$(dirname "$__src")" && pwd)"
    __src="$(readlink "$__src")"
    [[ $__src != /* ]] && __src="$__dir/$__src"
done
__SCRIPT_DIR="$(cd -P "$(dirname "$__src")" && pwd)"
source "$__SCRIPT_DIR/_common.sh"

[[ $# -ge 1 ]] || { echo "usage: pi-epic-init <slug> [--from <file>] [--title \"<title>\"] [--base <branch>] [--no-planner]" >&2; exit 1; }

slug=$(slugify "$1"); shift
from_file=""
title=""
base_branch=""
no_planner=0

while [[ $# -gt 0 ]]; do
    case "$1" in
        --from) from_file=$2; shift 2 ;;
        --title) title=$2; shift 2 ;;
        --base) base_branch=$2; shift 2 ;;
        --no-planner) no_planner=1; shift ;;
        *) echo "unknown arg: $1" >&2; exit 1 ;;
    esac
done

repo=$(repo_root)
cd "$repo"

# Initialize per-machine user lessons file if missing (v0.6.2 / L-036).
# Privacy fix: distilled lessons accumulate in $HOME/.pi/epicflow/user-lessons.md
# rather than the framework's lessons.md (which would leak project specifics on
# pi-epicflow PRs). Agents read both files at decompose time; user wins.
ensure_user_lessons

# Version-drift warning (v0.6.2 / L-038 sibling): if the installed pi-epicflow
# clone is more than 7 days behind, surface it now so the operator can pull
# updates BEFORE committing to a multi-hour epic. Quiet on first-day installs.
_pe_age=$(pi_epicflow_age_days)
_pe_ver=$(pi_epicflow_version)
if [[ "$_pe_age" =~ ^[0-9]+$ ]] && (( _pe_age > 7 )); then
    log "⚠  pi-epicflow $_pe_ver is ${_pe_age} days old. Consider \`pi update pi-epicflow\` before starting a long epic."
fi

# Pre-add the well-known pi runtime + per-feature ephemeral patterns to
# .gitignore BEFORE the clean-tree check, so users who installed
# pi-subagents / pi-intercom locally (`pi install -l`) aren't blocked by the
# resulting `.pi/npm/` and `.pi/settings.json` untracked files.
gitignore_ensure() {
    local path=$1
    local gi="$repo/.gitignore"
    [[ -f "$gi" ]] || touch "$gi"
    grep -qxF "$path" "$gi" || echo "$path" >> "$gi"
}
gitignore_ensure ".pi/STATE.md"
gitignore_ensure ".pi/npm/"
gitignore_ensure ".pi/settings.json"
gitignore_ensure ".pi/epics/*/run-log.jsonl"
gitignore_ensure ".pi/epics/*/features/*/worker-report.md"
gitignore_ensure ".pi/epics/*/features/*/review-report.md"
gitignore_ensure ".pi/epics/*/features/*/progress.md"
gitignore_ensure ".pi/epics/*/features/done/*/worker-report.md"
gitignore_ensure ".pi/epics/*/features/done/*/review-report.md"
gitignore_ensure ".pi/epics/*/features/done/*/progress.md"
# L-012 belt: halt reports are signals to the human, NOT branch history.
# pi-feature-start, the orchestrator's closeout, and pi-epic-complete all
# carry their own `git reset HEAD halt-*.md` suspenders, but a top-level
# .gitignore rule is the only thing that survives a stray `git add` typo.
gitignore_ensure ".pi/epics/*/halt-*.md"
gitignore_ensure ".pi/epics/done/*/halt-*.md"
# L-026 (v0.5.1): glob patterns above match only `.pi/epics/<id>/...`
# (one segment after `epics/`). After `pi-epic-complete` archives the epic
# into `.pi/epics/done/<id>/...`, the path gains an extra segment and the
# original patterns stop matching — so worker/review/progress reports + the
# run-log become un-ignored and any `git add -A` tracks them. Add the
# deeper patterns explicitly. See smoke epic 0001-smoke epic-review note.
gitignore_ensure ".pi/epics/done/*/run-log.jsonl"
gitignore_ensure ".pi/epics/done/*/features/done/*/worker-report.md"
gitignore_ensure ".pi/epics/done/*/features/done/*/review-report.md"
gitignore_ensure ".pi/epics/done/*/features/done/*/progress.md"
# L-026 (v0.5.1): bytecode caches universally — cheap noise on non-Python
# repos, real hygiene win on Python ones (smoke epic shipped 11 .pyc files
# into the epic branch because the template didn't cover this).
gitignore_ensure "__pycache__/"
gitignore_ensure "*.pyc"
# L-040 (v0.6.2): catch tracked-symlink leak (`node_modules_main`, etc.) that
# slipped past a bare `node_modules` ignore. Gen-ui 0001-gen-ui F01 lost ~5
# minutes to `git rm --cached` recovery; ignore the whole `node_modules*`
# family up front. Harmless on non-node repos.
gitignore_ensure "node_modules*"
# Commit the .gitignore update if it actually changed something OR if it's
# brand-new and untracked. (`git diff --quiet` reports clean for untracked
# files, so we need to check status, not diff.)
if [[ -n $(git status --porcelain -- .gitignore) ]]; then
    git add .gitignore
    git commit --quiet --no-verify -m "chore: ignore pi runtime state (epic-feature-workflow)"
fi

# Refuse if STILL dirty after the gitignore pass.
if [[ -n $(git status --porcelain) ]]; then
    echo "ERROR: working tree not clean. Commit or stash first." >&2
    git status --short >&2
    exit 1
fi

def=$(default_branch)
if [[ -n "$base_branch" ]]; then
    # Resolve --base: prefer existing local branch, then origin/<name>.
    # If only origin/<name> exists, fetch it so a later checkout can create
    # a tracking branch cleanly.
    if git rev-parse --verify --quiet "refs/heads/$base_branch" >/dev/null; then
        :  # local branch exists, use as-is
    elif git rev-parse --verify --quiet "refs/remotes/origin/$base_branch" >/dev/null; then
        git fetch --quiet origin "$base_branch" 2>/dev/null || true
    else
        echo "ERROR: --base branch '$base_branch' not found locally or on origin/." >&2
        echo "  Checked: refs/heads/$base_branch and refs/remotes/origin/$base_branch" >&2
        exit 1
    fi
    def="$base_branch"
    log "base branch (--base override): $def"
else
    log "default branch: $def"
fi

# Update default branch to latest — only if we're currently on it.
# In a bare-repo+worktrees layout the user may already have a dedicated
# epic worktree checked out; in that case skip the checkout/pull.
current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
if [[ "$current_branch" == "$def" ]]; then
    git fetch --quiet origin "$def" 2>/dev/null || true
    git pull --ff-only --quiet 2>/dev/null || log "(pull skipped — no upstream or offline)"
elif [[ "$current_branch" == "epic/$slug" ]]; then
    log "on dedicated epic worktree (epic/$slug); skipping default-branch refresh"
else
    git checkout "$def" --quiet
    git pull --ff-only --quiet 2>/dev/null || log "(pull skipped — no upstream or offline)"
fi

epic_id="$(next_epic_id)-$slug"
epic_dir="$repo/.pi/epics/$epic_id"
[[ ! -d "$epic_dir" ]] || { echo "ERROR: $epic_dir already exists" >&2; exit 1; }

skill=$(skill_root)
mkdir -p "$epic_dir/features"
cp "$skill/templates/design.md"          "$epic_dir/design.md"
cp "$skill/templates/meta.yaml"          "$epic_dir/meta.yaml"
cp "$skill/templates/epic-config.yaml"   "$epic_dir/epic-config.yaml"
cp "$skill/templates/decomposition.yaml" "$epic_dir/decomposition.yaml"
cp "$skill/templates/deviations.md"      "$epic_dir/deviations.md"
touch "$epic_dir/run-log.jsonl"

# Seed design.md from --from file if given (replaces template content)
if [[ -n "$from_file" ]]; then
    [[ -f "$from_file" ]] || { echo "ERROR: --from file not found: $from_file" >&2; exit 1; }
    cp "$from_file" "$epic_dir/design.md"
    log "seeded design.md from $from_file"
fi

# Fill meta.yaml
today=$(date -u +%Y-%m-%d)
owner=$(git config user.name 2>/dev/null || echo "unknown")
title_use=${title:-$slug}
sed -i.bak \
    -e "s|^id:.*|id: $epic_id|" \
    -e "s|^title:.*|title: $title_use|" \
    -e "s|^status:.*|status: design|" \
    -e "s|^branch:.*|branch: epic/$slug|" \
    -e "s|^default_branch:.*|default_branch: $def|" \
    -e "s|^owner:.*|owner: $owner|" \
    -e "s|^started:.*|started: $today|" \
    -e "s|^updated:.*|updated: $today|" \
    "$epic_dir/meta.yaml"
rm -f "$epic_dir/meta.yaml.bak"

# --no-planner: persist disable_planner: true into epic meta.yaml so that
# the orchestrator's planning gate (epic-run-auto §3.5) skips the planner
# subagent for every feature in this epic, regardless of needs_planner.
if [[ $no_planner -eq 1 ]]; then
    if grep -q '^disable_planner:' "$epic_dir/meta.yaml"; then
        sed -i.bak 's|^disable_planner:.*|disable_planner: true|' "$epic_dir/meta.yaml"
        rm -f "$epic_dir/meta.yaml.bak"
    else
        printf '\ndisable_planner: true\n' >> "$epic_dir/meta.yaml"
    fi
    log "--no-planner: disabled feature-planner subagent for this epic"
fi

# Create epic branch (or reuse if already on it — supports the
# bare-repo + worktrees pattern where the user creates a dedicated
# epic worktree first: `git worktree add ../epic-<slug> -b epic/<slug> main`)
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [[ "$current_branch" == "epic/$slug" ]]; then
    log "already on epic/$slug (dedicated worktree); reusing"
elif git rev-parse --verify --quiet "epic/$slug" >/dev/null; then
    echo "ERROR: branch epic/$slug already exists in another worktree." >&2
    echo "  cd into that worktree and re-run, OR delete the branch first." >&2
    exit 1
else
    git checkout -b "epic/$slug" --quiet
    log "created branch: epic/$slug"
fi

# Update STATE.md
cat > "$repo/.pi/STATE.md" <<EOF
# Active epic

\`.pi/epics/$epic_id/\`

Branch: \`epic/$slug\`
Default (PR target): \`$def\`

Design: [\`design.md\`](epics/$epic_id/design.md)
Decomposition: [\`decomposition.yaml\`](epics/$epic_id/decomposition.yaml)
Status: see [\`meta.yaml\`](epics/$epic_id/meta.yaml)

No active feature yet. Run \`pi-epic-decompose\` next.
EOF

runlog_append "$epic_dir" "\"event\":\"epic-init\",\"epic\":\"$epic_id\",\"branch\":\"epic/$slug\""

# Commit the scaffolding to the epic branch so the working tree is clean
# before the first pi-feature-start. (gitignore was already committed above.)
git add ".pi/epics/$epic_id"
git commit --quiet --no-verify -m "chore(epic): scaffold $epic_id" || true

cat <<EOF

✓ Epic initialized: $epic_id
  Folder: $epic_dir
  Branch: epic/$slug (off $def)

Next steps (in pi):
  1. /epic-design          → co-author $epic_dir/design.md with pi (or edit directly)
  2. /epic-review-design   → (optional) unbiased critic pass before decomposition
  3. /epic-decompose       → pi proposes $epic_dir/decomposition.yaml
  4. /epic-run-auto        → ship every feature, open the PR

EOF
