#!/bin/bash
# agent-upgrade.sh — Non-destructively upgrade agent configuration in projects
#
# Usage:
#   agent-upgrade.sh <project-path>                    # Upgrade one project
#   agent-upgrade.sh --all                             # Upgrade all projects in ~/src/
#   agent-upgrade.sh --all --dry-run                   # Preview what would change
#   agent-upgrade.sh <path> --section conditional-loading  # Inject specific section
#   agent-upgrade.sh <path> --create-agents            # Create AGENTS.md if missing
#   agent-upgrade.sh <path> --monorepo-scan            # Create sub-AGENTS.md for monorepo dirs
#
# Safety:
#   - Creates .bak backup before any modification
#   - Append-only — never modifies existing sections
#   - --dry-run shows what would change without writing

set -euo pipefail

# Defaults
SRC_DIR="$HOME/src"
FRAGMENTS_DIR="$HOME/.config/mosaic/templates/agent/fragments"
TEMPLATES_DIR="$HOME/.config/mosaic/templates/agent"
DRY_RUN=false
ALL_PROJECTS=false
TARGET_PATH=""
SECTION_ONLY=""
CREATE_AGENTS=false
MONOREPO_SCAN=false

# Exclusion patterns (same as agent-lint.sh)
EXCLUDE_PATTERNS=(
  "_worktrees"
  ".backup"
  "_old"
  "_bak"
  "junk"
  "traefik"
  "infrastructure"
)

# Colors
GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[0;33m'
NC='\033[0m' BOLD='\033[1m' DIM='\033[2m'

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --all) ALL_PROJECTS=true; shift ;;
    --dry-run) DRY_RUN=true; shift ;;
    --section) SECTION_ONLY="$2"; shift 2 ;;
    --create-agents) CREATE_AGENTS=true; shift ;;
    --monorepo-scan) MONOREPO_SCAN=true; shift ;;
    --src-dir) SRC_DIR="$2"; shift 2 ;;
    -h|--help)
      echo "Usage: agent-upgrade.sh [<project-path>|--all] [--dry-run] [--section <name>] [--create-agents] [--monorepo-scan]"
      echo ""
      echo "Options:"
      echo "  --all              Upgrade all projects in ~/src/"
      echo "  --dry-run          Preview changes without writing"
      echo "  --section <name>   Inject only a specific fragment (conditional-loading, commit-format, secrets, multi-agent, code-review, campsite-rule)"
      echo "  --create-agents    Create AGENTS.md if missing"
      echo "  --monorepo-scan    Create sub-AGENTS.md for monorepo directories"
      exit 0
      ;;
    *)
      if [[ -d "$1" ]]; then
        TARGET_PATH="$1"
      else
        echo "Unknown option or invalid path: $1"
        exit 1
      fi
      shift
      ;;
  esac
done

if ! $ALL_PROJECTS && [[ -z "$TARGET_PATH" ]]; then
  echo "Error: Specify a project path or use --all"
  exit 1
fi

# Helpers
is_coding_project() {
  local dir="$1"
  [[ -f "$dir/package.json" ]] || \
  [[ -f "$dir/pyproject.toml" ]] || \
  [[ -f "$dir/Cargo.toml" ]] || \
  [[ -f "$dir/go.mod" ]] || \
  [[ -f "$dir/pom.xml" ]] || \
  [[ -f "$dir/build.gradle" ]]
}

is_excluded() {
  local dir_name
  dir_name=$(basename "$1")
  for pattern in "${EXCLUDE_PATTERNS[@]}"; do
    [[ "$dir_name" == *"$pattern"* ]] && return 0
  done
  return 1
}

is_monorepo() {
  local dir="$1"
  [[ -f "$dir/pnpm-workspace.yaml" ]] || \
  [[ -f "$dir/turbo.json" ]] || \
  [[ -f "$dir/lerna.json" ]] || \
  (grep -q '"workspaces"' "$dir/package.json" 2>/dev/null)
}

has_section() {
  local file="$1"
  local pattern="$2"
  [[ -f "$file" ]] && grep -qi "$pattern" "$file" 2>/dev/null
}

runtime_context_file() {
  local project_dir="$1"
  if [[ -f "$project_dir/CLAUDE.md" ]]; then
    echo "$project_dir/CLAUDE.md"
    return
  fi
  if [[ -f "$project_dir/RUNTIME.md" ]]; then
    echo "$project_dir/RUNTIME.md"
    return
  fi
  echo "$project_dir/CLAUDE.md"
}

backup_file() {
  local file="$1"
  if [[ -f "$file" ]] && ! $DRY_RUN; then
    cp "$file" "${file}.bak"
  fi
}

# Inject a fragment into CLAUDE.md if the section doesn't exist
inject_fragment() {
  local project_dir="$1"
  local fragment_name="$2"
  local ctx_file
  ctx_file="$(runtime_context_file "$project_dir")"
  local fragment_file="$FRAGMENTS_DIR/$fragment_name.md"

  if [[ ! -f "$fragment_file" ]]; then
    echo -e "    ${RED}Fragment not found: $fragment_file${NC}"
    return 1
  fi

  # Determine detection pattern for this fragment
  local detect_pattern
  case "$fragment_name" in
    conditional-loading) detect_pattern="agent-guides\|~/.config/mosaic/guides\|Conditional.*Loading\|Conditional.*Documentation\|Conditional.*Context" ;;
    commit-format) detect_pattern="<type>.*#issue\|Types:.*feat.*fix" ;;
    secrets) detect_pattern="NEVER hardcode secrets\|\.env.example.*committed" ;;
    multi-agent) detect_pattern="Multi-Agent Coordination\|pull --rebase.*before" ;;
    code-review) detect_pattern="codex-code-review\|codex-security-review\|Code Review" ;;
    campsite-rule) detect_pattern="Campsite Rule\|Touching it makes it yours\|was already there.*NEVER" ;;
    *) echo "Unknown fragment: $fragment_name"; return 1 ;;
  esac

  if [[ ! -f "$ctx_file" ]]; then
    echo -e "    ${YELLOW}No runtime context file (CLAUDE.md/RUNTIME.md) — skipping fragment injection${NC}"
    return 0
  fi

  if has_section "$ctx_file" "$detect_pattern"; then
    echo -e "    ${DIM}$fragment_name already present${NC}"
    return 0
  fi

  if $DRY_RUN; then
    echo -e "    ${GREEN}Would inject: $fragment_name${NC}"
  else
    backup_file "$ctx_file"
    echo "" >> "$ctx_file"
    cat "$fragment_file" >> "$ctx_file"
    echo "" >> "$ctx_file"
    echo -e "    ${GREEN}Injected: $fragment_name${NC}"
  fi
}

# Create AGENTS.md from template
create_agents_md() {
  local project_dir="$1"
  local agents_md="$project_dir/AGENTS.md"

  if [[ -f "$agents_md" ]]; then
    echo -e "    ${DIM}AGENTS.md already exists${NC}"
    return 0
  fi

  local project_name
  project_name=$(basename "$project_dir")

  # Detect project type for quality gates
  local quality_gates="# Add quality gate commands here"
  if [[ -f "$project_dir/package.json" ]]; then
    quality_gates="npm run lint && npm run typecheck && npm test"
    if grep -q '"pnpm"' "$project_dir/package.json" 2>/dev/null || [[ -f "$project_dir/pnpm-lock.yaml" ]]; then
      quality_gates="pnpm lint && pnpm typecheck && pnpm test"
    fi
  elif [[ -f "$project_dir/pyproject.toml" ]]; then
    quality_gates="uv run ruff check src/ tests/ && uv run mypy src/ && uv run pytest --cov"
  fi

  if $DRY_RUN; then
    echo -e "    ${GREEN}Would create: AGENTS.md${NC}"
  else
    # Use generic AGENTS.md template with substitutions
    sed -e "s/\${PROJECT_NAME}/$project_name/g" \
        -e "s/\${QUALITY_GATES}/$quality_gates/g" \
        -e "s/\${TASK_PREFIX}/${project_name^^}/g" \
        -e "s|\${SOURCE_DIR}|src|g" \
        "$TEMPLATES_DIR/AGENTS.md.template" > "$agents_md"
    echo -e "    ${GREEN}Created: AGENTS.md${NC}"
  fi
}

# Create sub-AGENTS.md for monorepo directories
create_sub_agents() {
  local project_dir="$1"

  if ! is_monorepo "$project_dir"; then
    echo -e "    ${DIM}Not a monorepo — skipping sub-AGENTS scan${NC}"
    return 0
  fi

  local created=0
  for subdir_type in apps packages services plugins; do
    if [[ -d "$project_dir/$subdir_type" ]]; then
      for subdir in "$project_dir/$subdir_type"/*/; do
        [[ -d "$subdir" ]] || continue
        # Only if it has its own manifest
        if [[ -f "$subdir/package.json" ]] || [[ -f "$subdir/pyproject.toml" ]]; then
          if [[ ! -f "$subdir/AGENTS.md" ]]; then
            local dir_name
            dir_name=$(basename "$subdir")
            if $DRY_RUN; then
              echo -e "    ${GREEN}Would create: $subdir_type/$dir_name/AGENTS.md${NC}"
            else
              sed -e "s/\${DIRECTORY_NAME}/$dir_name/g" \
                  -e "s/\${DIRECTORY_PURPOSE}/Part of the $subdir_type layer./g" \
                  "$TEMPLATES_DIR/sub-agents.md.template" > "${subdir}AGENTS.md"
              echo -e "    ${GREEN}Created: $subdir_type/$dir_name/AGENTS.md${NC}"
            fi
            ((created++)) || true
          fi
        fi
      done
    fi
  done

  if [[ $created -eq 0 ]]; then
    echo -e "    ${DIM}All monorepo sub-AGENTS.md present${NC}"
  fi
}

# Upgrade a single project
upgrade_project() {
  local dir="$1"
  local name
  name=$(basename "$dir")

  echo -e "\n${BOLD}$name${NC} ${DIM}($dir)${NC}"

  if [[ -n "$SECTION_ONLY" ]]; then
    inject_fragment "$dir" "$SECTION_ONLY"
    return
  fi

  # Always try conditional-loading (highest impact)
  inject_fragment "$dir" "conditional-loading"

  # Try other fragments if runtime context exists
  if [[ -f "$dir/CLAUDE.md" || -f "$dir/RUNTIME.md" ]]; then
    inject_fragment "$dir" "commit-format"
    inject_fragment "$dir" "secrets"
    inject_fragment "$dir" "multi-agent"
    inject_fragment "$dir" "code-review"
    inject_fragment "$dir" "campsite-rule"
  fi

  # Create AGENTS.md if missing (always unless --section was used)
  if $CREATE_AGENTS || [[ -z "$SECTION_ONLY" ]]; then
    create_agents_md "$dir"
  fi

  # Monorepo sub-AGENTS.md
  if $MONOREPO_SCAN || [[ -z "$SECTION_ONLY" ]]; then
    create_sub_agents "$dir"
  fi
}

# Main
main() {
  local projects=()

  if $ALL_PROJECTS; then
    for dir in "$SRC_DIR"/*/; do
      [[ -d "$dir" ]] || continue
      is_excluded "$dir" && continue
      is_coding_project "$dir" && projects+=("${dir%/}")
    done
  else
    projects=("$TARGET_PATH")
  fi

  if [[ ${#projects[@]} -eq 0 ]]; then
    echo "No coding projects found."
    exit 0
  fi

  local mode="LIVE"
  $DRY_RUN && mode="DRY RUN"

  echo -e "${BOLD}Agent Configuration Upgrade — $(date +%Y-%m-%d) [$mode]${NC}"
  echo "========================================================"

  for dir in "${projects[@]}"; do
    upgrade_project "$dir"
  done

  echo ""
  echo -e "${BOLD}Done.${NC}"
  if $DRY_RUN; then
    echo -e "${DIM}Run without --dry-run to apply changes.${NC}"
  else
    echo -e "${DIM}Backups saved as .bak files. Run agent-lint.sh to verify.${NC}"
  fi
}

main
