import {
  loadSignals,
  markSignalsAsRead
} from "./chunk-OMJD6A3S.js";
import {
  createWuPaths
} from "./chunk-6HO4GWJE.js";
import {
  CLAUDE_HOOKS,
  DIRECTORIES,
  getHookCommand
} from "./chunk-DWMLTXKQ.js";

// src/hooks/generators/enforce-worktree.ts
var DEFAULT_WORKTREES_DIR_SEGMENT = DIRECTORIES.WORKTREES.replace(/\/+$/g, "");
var DEFAULT_WU_ALLOWLIST_PREFIX = DIRECTORIES.WU_DIR;
function normalizeDirectorySegment(value, fallback) {
  const normalized = value.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
  return normalized.length > 0 ? normalized : fallback;
}
function ensureTrailingSlash(value) {
  return value.endsWith("/") ? value : `${value}/`;
}
function generateEnforceWorktreeScript(options = {}) {
  const projectRoot = options.projectRoot ?? process.cwd();
  let worktreesDirSegment = DEFAULT_WORKTREES_DIR_SEGMENT;
  let wuAllowlistPrefix = `${DEFAULT_WU_ALLOWLIST_PREFIX}/`;
  try {
    const wuPaths = createWuPaths({ projectRoot });
    worktreesDirSegment = normalizeDirectorySegment(
      wuPaths.WORKTREES_DIR(),
      DEFAULT_WORKTREES_DIR_SEGMENT
    );
    wuAllowlistPrefix = ensureTrailingSlash(
      normalizeDirectorySegment(wuPaths.WU_DIR(), DEFAULT_WU_ALLOWLIST_PREFIX)
    );
  } catch {
  }
  return `#!/bin/bash
#
# enforce-worktree.sh (WU-1367, WU-1501)
#
# PreToolUse hook that blocks Write/Edit on main checkout.
# WU-1501: Fail-closed - blocks even when no worktrees exist.
# Graceful degradation only when LumenFlow is NOT configured.
#
# Allowlist: ${wuAllowlistPrefix}, .lumenflow/, .claude/, plan/
# Branch-PR claimed_mode permits writes from main checkout.
#
# Exit codes:
#   0 = Allow operation
#   2 = Block operation (stderr shown to Claude as guidance)
#

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"

# Graceful degradation: if we can't determine state, allow the operation
graceful_allow() {
  local reason="$1"
  exit 0
}

# Derive repo paths from CLAUDE_PROJECT_DIR
if [[ -z "\${CLAUDE_PROJECT_DIR:-}" ]]; then
  graceful_allow "CLAUDE_PROJECT_DIR not set"
fi

MAIN_REPO_PATH="$CLAUDE_PROJECT_DIR"
WORKTREES_DIR="\${MAIN_REPO_PATH}/${worktreesDirSegment}"
LUMENFLOW_DIR="\${MAIN_REPO_PATH}/.lumenflow"

# Check if .lumenflow exists (LumenFlow is configured)
if [[ ! -d "$LUMENFLOW_DIR" ]]; then
  graceful_allow "No .lumenflow directory (LumenFlow not configured)"
fi

# Read JSON input from stdin
INPUT=$(cat)

if [[ -z "$INPUT" ]]; then
  graceful_allow "No input provided"
fi

# Parse JSON with Python
TMPFILE=$(mktemp)
echo "$INPUT" > "$TMPFILE"

PARSE_RESULT=$(python3 -c "
import json
import sys
try:
    with open('$TMPFILE', 'r') as f:
        data = json.load(f)
    tool_name = data.get('tool_name', '')
    tool_input = data.get('tool_input', {})
    if not isinstance(tool_input, dict):
        tool_input = {}
    file_path = tool_input.get('file_path', '')
    print('OK')
    print(tool_name if tool_name else '')
    print(file_path if file_path else '')
except Exception as e:
    print('ERROR')
    print(str(e))
    print('')
" 2>&1)

rm -f "$TMPFILE"

# Parse the result
PARSE_STATUS=$(echo "$PARSE_RESULT" | head -1)
TOOL_NAME=$(echo "$PARSE_RESULT" | sed -n '2p')
FILE_PATH=$(echo "$PARSE_RESULT" | sed -n '3p')

if [[ "$PARSE_STATUS" != "OK" ]]; then
  graceful_allow "JSON parse failed"
fi

# Only process Write and Edit tools
if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then
  exit 0
fi

if [[ -z "$FILE_PATH" ]]; then
  graceful_allow "No file_path in input"
fi

# Canonicalize tool path before resolution (e.g., "~/" -> "$HOME/")
CANONICAL_PATH="$FILE_PATH"
if [[ "$CANONICAL_PATH" == "~" ]]; then
  if [[ -n "\${HOME:-}" ]]; then
    CANONICAL_PATH="$HOME"
  fi
elif [[ "$CANONICAL_PATH" == "~/"* || "$CANONICAL_PATH" == "~\\\\"* ]]; then
  if [[ -n "\${HOME:-}" ]]; then
    CANONICAL_PATH="\${HOME}/\${CANONICAL_PATH:2}"
  fi
fi

# Resolve the canonicalized file path
RESOLVED_PATH=$(realpath -m "$CANONICAL_PATH" 2>/dev/null || echo "$CANONICAL_PATH")

# Allow if path is outside repo entirely
if [[ "$RESOLVED_PATH" != "\${MAIN_REPO_PATH}/"* && "$RESOLVED_PATH" != "\${MAIN_REPO_PATH}" ]]; then
  exit 0
fi

# Allow if path is inside a worktree
if [[ "$RESOLVED_PATH" == "\${WORKTREES_DIR}/"* ]]; then
  exit 0
fi

# Check if UnsafeAny active worktrees exist
WORKTREE_COUNT=0
if [[ -d "$WORKTREES_DIR" ]]; then
  WORKTREE_COUNT=$(find "$WORKTREES_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)
fi

# If worktrees exist, block writes to main repo (original behavior)
if [[ "$WORKTREE_COUNT" -gt 0 ]]; then
  ACTIVE_WORKTREES=$(find "$WORKTREES_DIR" -mindepth 1 -maxdepth 1 -type d -printf '%f\\n' 2>/dev/null | head -5 | tr '\\n' ', ' | sed 's/,$//')

  echo "" >&2
  echo "=== Worktree Enforcement ===" >&2
  echo "" >&2
  echo "BLOCKED: $TOOL_NAME to main repo" >&2
  echo "" >&2
  echo "Active worktrees: \${ACTIVE_WORKTREES:-none detected}" >&2
  echo "" >&2
  echo "USE INSTEAD:" >&2
  echo "  1. cd to your worktree: cd ${worktreesDirSegment}/<lane>-wu-<id>/" >&2
  echo "  2. Make your edits in the worktree" >&2
  echo "" >&2
  echo "See: LUMENFLOW.md for worktree discipline" >&2
  echo "==============================" >&2
  exit 2
fi

# WU-1501: Fail-closed on main when no active worktrees exist
# Check allowlist: paths that are always safe to write on main
RELATIVE_PATH="\${RESOLVED_PATH#\${MAIN_REPO_PATH}/}"

case "$RELATIVE_PATH" in
  ${wuAllowlistPrefix}*)          exit 0 ;;  # WU YAML specs
  .lumenflow/*)                   exit 0 ;;  # LumenFlow state/config
  .claude/*)                      exit 0 ;;  # Claude Code config
  plan/*)                         exit 0 ;;  # Plan/spec scaffolds
esac

# Check for branch-pr claimed_mode (allows main writes without worktree)
STATE_FILE="\${LUMENFLOW_DIR}/state/wu-events.jsonl"
if [[ -f "$STATE_FILE" ]]; then
  if grep -q '"claimed_mode":"branch-pr"' "$STATE_FILE" 2>/dev/null; then
    if grep -q '"status":"in_progress"' "$STATE_FILE" 2>/dev/null; then
      exit 0  # Branch-PR WU active - allow main writes
    fi
  fi
fi

# WU-1501: Fail-closed - no active claim context, block the write
echo "" >&2
echo "=== Worktree Enforcement ===" >&2
echo "" >&2
echo "BLOCKED: $TOOL_NAME on main (no active WU claim)" >&2
echo "" >&2
echo "No worktrees exist and no branch-pr WU is in progress." >&2
echo "" >&2
echo "WHAT TO DO:" >&2
echo "  1. Claim a WU: pnpm wu:claim --id WU-XXXX --lane \\"<Lane>\\"" >&2
echo "  2. cd ${worktreesDirSegment}/<lane>-wu-xxxx" >&2
echo "  3. Make your edits in the worktree" >&2
echo "" >&2
echo "See: LUMENFLOW.md for worktree discipline" >&2
echo "==============================" >&2
exit 2
`;
}

// src/hooks/generators/require-wu.ts
function generateRequireWuScript() {
  return `#!/bin/bash
#
# require-wu.sh (WU-1367)
#
# PreToolUse hook that blocks Write/Edit when no WU is claimed.
# Graceful degradation: allows operations if state cannot be determined.
#
# Exit codes:
#   0 = Allow operation
#   2 = Block operation (stderr shown to Claude as guidance)
#

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"

# Graceful degradation
graceful_allow() {
  local reason="$1"
  exit 0
}

if [[ -z "\${CLAUDE_PROJECT_DIR:-}" ]]; then
  graceful_allow "CLAUDE_PROJECT_DIR not set"
fi

MAIN_REPO_PATH="$CLAUDE_PROJECT_DIR"
WORKTREES_DIR="\${MAIN_REPO_PATH}/worktrees"
LUMENFLOW_DIR="\${MAIN_REPO_PATH}/.lumenflow"
STATE_FILE="\${LUMENFLOW_DIR}/state/wu-events.jsonl"

# Check if LumenFlow is configured
if [[ ! -d "$LUMENFLOW_DIR" ]]; then
  graceful_allow "No .lumenflow directory"
fi

# Read JSON input
INPUT=$(cat)
if [[ -z "$INPUT" ]]; then
  graceful_allow "No input"
fi

# Parse JSON
TMPFILE=$(mktemp)
echo "$INPUT" > "$TMPFILE"

PARSE_RESULT=$(python3 -c "
import json
try:
    with open('$TMPFILE', 'r') as f:
        data = json.load(f)
    tool_name = data.get('tool_name', '')
    tool_input = data.get('tool_input', {})
    if not isinstance(tool_input, dict):
        tool_input = {}
    file_path = tool_input.get('file_path', '')
    print('OK')
    print(tool_name if tool_name else '')
    print(file_path if file_path else '')
except:
    print('ERROR')
    print('')
    print('')
" 2>/dev/null || echo "ERROR")

rm -f "$TMPFILE"

# Parse result
PARSE_STATUS=$(echo "$PARSE_RESULT" | head -1)
TOOL_NAME=$(echo "$PARSE_RESULT" | sed -n '2p')
FILE_PATH=$(echo "$PARSE_RESULT" | sed -n '3p')

if [[ "$PARSE_STATUS" != "OK" ]]; then
  graceful_allow "JSON parse failed"
fi

# Only check Write and Edit
if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then
  exit 0
fi

if [[ -z "$FILE_PATH" ]]; then
  graceful_allow "No file_path in input"
fi

# Canonicalize tool path before resolution (e.g., "~/" -> "$HOME/")
CANONICAL_PATH="$FILE_PATH"
if [[ "$CANONICAL_PATH" == "~" ]]; then
  if [[ -n "\${HOME:-}" ]]; then
    CANONICAL_PATH="$HOME"
  fi
elif [[ "$CANONICAL_PATH" == "~/"* || "$CANONICAL_PATH" == "~\\\\"* ]]; then
  if [[ -n "\${HOME:-}" ]]; then
    CANONICAL_PATH="\${HOME}/\${CANONICAL_PATH:2}"
  fi
fi

# Resolve the canonicalized file path
RESOLVED_PATH=$(realpath -m "$CANONICAL_PATH" 2>/dev/null || echo "$CANONICAL_PATH")

# Only enforce WU requirement for writes targeting this repository
if [[ "$RESOLVED_PATH" != "\${MAIN_REPO_PATH}/"* && "$RESOLVED_PATH" != "\${MAIN_REPO_PATH}" ]]; then
  exit 0
fi

# Check for active worktrees (indicates claimed WU)
if [[ -d "$WORKTREES_DIR" ]]; then
  WORKTREE_COUNT=$(find "$WORKTREES_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)
  if [[ "$WORKTREE_COUNT" -gt 0 ]]; then
    exit 0  # Has worktrees = has claimed WU
  fi
fi

# Check state file for in_progress WUs
if [[ -f "$STATE_FILE" ]]; then
  # Look for UnsafeAny WU with in_progress status
  if grep -q '"status":"in_progress"' "$STATE_FILE" 2>/dev/null; then
    exit 0  # Has in_progress WU
  fi
fi

# No claimed WU found
echo "" >&2
echo "=== WU Enforcement ===" >&2
echo "" >&2
echo "BLOCKED: $TOOL_NAME without claimed WU" >&2
echo "" >&2
echo "You must claim a WU before making edits:" >&2
echo "  pnpm wu:claim --id WU-XXXX --lane <Lane>" >&2
echo "  cd worktrees/<lane>-wu-xxxx" >&2
echo "" >&2
echo "Or create a new WU:" >&2
echo "  pnpm wu:create --lane <Lane> --title "Description"" >&2
echo "" >&2
echo "See: LUMENFLOW.md for workflow details" >&2
echo "======================" >&2
exit 2
`;
}

// src/hooks/generators/warn-incomplete.ts
function generateWarnIncompleteScript() {
  return `#!/bin/bash
#
# warn-incomplete.sh (WU-1367)
#
# Stop hook that warns when session ends without wu:done.
# This is advisory only - never blocks session termination.
#
# Exit codes:
#   0 = Always (warnings only)
#

SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"

if [[ -z "\${CLAUDE_PROJECT_DIR:-}" ]]; then
  exit 0
fi

MAIN_REPO_PATH="$CLAUDE_PROJECT_DIR"
WORKTREES_DIR="\${MAIN_REPO_PATH}/worktrees"

# Check for active worktrees
if [[ ! -d "$WORKTREES_DIR" ]]; then
  exit 0
fi

WORKTREE_COUNT=$(find "$WORKTREES_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)
if [[ "$WORKTREE_COUNT" -eq 0 ]]; then
  exit 0
fi

# Get active worktree names
ACTIVE_WORKTREES=$(find "$WORKTREES_DIR" -mindepth 1 -maxdepth 1 -type d -printf '%f\\n' 2>/dev/null | head -5 | tr '\\n' ', ' | sed 's/,$//')

echo "" >&2
echo "=== Session Completion Reminder ===" >&2
echo "" >&2
echo "You have active worktrees: $ACTIVE_WORKTREES" >&2
echo "" >&2
echo "If your work is complete, remember to run:" >&2
echo "  pnpm wu:prep --id WU-XXXX  (from worktree)" >&2
echo "  pnpm wu:done --id WU-XXXX  (from main)" >&2
echo "" >&2
echo "If work is incomplete, it will be preserved in the worktree." >&2
echo "====================================" >&2

exit 0
`;
}

// src/hooks/generators/session-start-recovery.ts
function generateSessionStartRecoveryScript() {
  return `#!/bin/bash
#
# session-start-recovery.sh
#
# SessionStart hook - check for pending recovery and inject context (WU-1390)
#
# Fires after session start (on compact, resume, or clear) to:
# 1. Check for recovery-pending-*.md files written by pre-compact-checkpoint.sh
# 2. Display the recovery context to the agent
# 3. Remove the recovery file (one-time recovery)
#
# This completes the durable recovery pattern:
#   PreCompact writes file \u2192 SessionStart reads and deletes it
#
# Exit codes:
#   0 = Always allow (informational hook)
#

set -euo pipefail

# Derive repo paths from CLAUDE_PROJECT_DIR
if [[ -n "\${CLAUDE_PROJECT_DIR:-}" ]]; then
  REPO_PATH="$CLAUDE_PROJECT_DIR"
else
  REPO_PATH=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
  if [[ -z "$REPO_PATH" ]]; then
    exit 0
  fi
fi

RECOVERY_DIR="\${REPO_PATH}/.lumenflow/state"

# Check if recovery directory exists
if [[ ! -d "$RECOVERY_DIR" ]]; then
  exit 0
fi

# Find UnsafeAny pending recovery files
FOUND_RECOVERY=false

for recovery_file in "$RECOVERY_DIR"/recovery-pending-*.md; do
  # Check if glob matched UnsafeAny files (bash glob returns literal pattern if no match)
  [[ -f "$recovery_file" ]] || continue

  FOUND_RECOVERY=true

  # Extract WU ID from filename for display
  WU_ID=$(basename "$recovery_file" | sed 's/recovery-pending-\\(.*\\)\\.md/\\1/')

  echo "" >&2
  echo "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" >&2
  echo "\u26A0\uFE0F  POST-COMPACTION RECOVERY DETECTED" >&2
  echo "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" >&2
  echo "" >&2

  # Display the recovery context
  cat "$recovery_file" >&2

  echo "" >&2
  echo "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" >&2
  echo "" >&2

  # Remove after displaying (one-time recovery)
  rm -f "$recovery_file"
done

# Additional context if recovery was displayed
if [[ "$FOUND_RECOVERY" == "true" ]]; then
  echo "IMPORTANT: Your context was compacted. Review the recovery info above." >&2
  echo "Recommended: Run 'pnpm wu:brief --id $WU_ID --client \${LUMENFLOW_CLIENT:-claude-code}' for fresh full context." >&2
  echo "" >&2
fi

# WU-1473: Surface unread coordination signals for non-worktree orchestrators
# Even without recovery files, agents benefit from seeing recent inbox activity
# This supports orchestrators running from main checkout (not in a worktree)
pnpm mem:inbox --since 1h --unread-only --quiet 2>/dev/null >&2 || true

exit 0
`;
}

// src/hooks/generators/signal-utils.ts
async function surfaceUnreadSignals(baseDir) {
  try {
    const signals = await loadSignals(baseDir, { unreadOnly: true });
    return { count: signals.length, signals };
  } catch {
    return { count: 0, signals: [] };
  }
}
async function markCompletedWUSignalsAsRead(baseDir, wuId) {
  try {
    const signals = await loadSignals(baseDir, { wuId, unreadOnly: true });
    if (signals.length === 0) {
      return { markedCount: 0 };
    }
    const signalIds = signals.map((sig) => sig.id);
    return await markSignalsAsRead(baseDir, signalIds);
  } catch {
    return { markedCount: 0 };
  }
}

// src/hooks/enforcement-generator.ts
var HOOK_SCRIPTS = CLAUDE_HOOKS.SCRIPTS;
function generateEnforcementHooks(config) {
  const hooks = {};
  const preToolUseHooks = [];
  if (config.block_outside_worktree || config.require_wu_for_edits) {
    const writeEditHooks = [];
    if (config.block_outside_worktree) {
      writeEditHooks.push({
        type: "command",
        command: getHookCommand(HOOK_SCRIPTS.ENFORCE_WORKTREE)
      });
    }
    if (config.require_wu_for_edits) {
      writeEditHooks.push({
        type: "command",
        command: getHookCommand(HOOK_SCRIPTS.REQUIRE_WU)
      });
    }
    if (writeEditHooks.length > 0) {
      preToolUseHooks.push({
        matcher: CLAUDE_HOOKS.MATCHERS.WRITE_EDIT,
        hooks: writeEditHooks
      });
    }
  }
  if (preToolUseHooks.length > 0) {
    hooks.preToolUse = preToolUseHooks;
  }
  if (config.warn_on_stop_without_wu_done) {
    hooks.stop = [
      {
        matcher: CLAUDE_HOOKS.MATCHERS.ALL,
        hooks: [
          {
            type: "command",
            command: getHookCommand(HOOK_SCRIPTS.WARN_INCOMPLETE)
          }
        ]
      }
    ];
  }
  const postToolUseHooks = [];
  if (config.auto_checkpoint?.enabled) {
    const autoCheckpointHook = {
      type: "command",
      command: getHookCommand(HOOK_SCRIPTS.AUTO_CHECKPOINT)
    };
    postToolUseHooks.push({
      matcher: CLAUDE_HOOKS.MATCHERS.ALL,
      hooks: [autoCheckpointHook]
    });
    hooks.subagentStop = [
      {
        matcher: CLAUDE_HOOKS.MATCHERS.SUBAGENT_STOP,
        hooks: [autoCheckpointHook]
      }
    ];
  }
  if (postToolUseHooks.length > 0) {
    hooks.postToolUse = postToolUseHooks;
  }
  hooks.preCompact = [
    {
      matcher: CLAUDE_HOOKS.MATCHERS.ALL,
      hooks: [
        {
          type: "command",
          command: getHookCommand(HOOK_SCRIPTS.PRE_COMPACT_CHECKPOINT)
        }
      ]
    }
  ];
  hooks.sessionStart = [
    {
      matcher: CLAUDE_HOOKS.MATCHERS.COMPACT,
      hooks: [
        {
          type: "command",
          command: getHookCommand(HOOK_SCRIPTS.SESSION_START_RECOVERY)
        }
      ]
    },
    {
      matcher: CLAUDE_HOOKS.MATCHERS.RESUME,
      hooks: [
        {
          type: "command",
          command: getHookCommand(HOOK_SCRIPTS.SESSION_START_RECOVERY)
        }
      ]
    },
    {
      matcher: CLAUDE_HOOKS.MATCHERS.CLEAR,
      hooks: [
        {
          type: "command",
          command: getHookCommand(HOOK_SCRIPTS.SESSION_START_RECOVERY)
        }
      ]
    }
  ];
  return hooks;
}

export {
  generateEnforceWorktreeScript,
  generateRequireWuScript,
  generateWarnIncompleteScript,
  generateSessionStartRecoveryScript,
  surfaceUnreadSignals,
  markCompletedWUSignalsAsRead,
  generateEnforcementHooks
};
