#!/usr/bin/env bash

# ─────────────────────────────────────────────────────────────────────────────
# CONTEXT FILE SCHEMA (v1.0)
# ─────────────────────────────────────────────────────────────────────────────
#
# Agent context files live at: session/agents/<role>.context.md
# They enable recovery after /compact by preserving agent state.
#
# Full schema documentation: docs/context-schema.md
#
# Title (line 1):
#   # <Role Name> — Session Context
#
# Required metadata (lines 2-6, bold key-value):
#   **Updated**: ISO 8601 date or datetime (YYYY-MM-DD or YYYY-MM-DDTHH:MMZ)
#   **Role**: role name (optional description)
#   **Pane**: pane location
#
# Required sections:
#   ## Recovery Steps       — numbered list of recovery actions (≥1 item)
#   ## Completed Work       — tasks done this session (prefix match)
#
# Recommended sections:
#   ## Pending              — outstanding/blocked work (prefix match)
#   ## Key Files            — important file paths
#
# Optional sections:
#   ## Current Team Layout, ## Current State, ## Key Rules,
#   ## Key Architecture Decisions, ## Git Status, ## Next Tasks,
#   ## Test Suites
# ─────────────────────────────────────────────────────────────────────────────

context.schema() # # display the context file schema
{
  console.log "Agent Context File Schema (v1.0)"
  console.log "================================"
  console.log ""
  console.log "Location: session/agents/<role>.context.md"
  console.log "Full docs: docs/context-schema.md"
  console.log ""
  console.log "TITLE (line 1, required):"
  console.log "  # <Role Name> — Session Context"
  console.log ""
  console.log "METADATA (lines 2-6, required):"
  console.log "  **Updated**: <YYYY-MM-DD or YYYY-MM-DDTHH:MMZ>"
  console.log "  **Role**: <role-name> (<description>)"
  console.log "  **Pane**: <pane-id>"
  console.log ""
  console.log "REQUIRED SECTIONS:"
  console.log "  ## Recovery Steps       — numbered list (≥1 item)"
  console.log "  ## Completed Work       — tasks done (prefix match)"
  console.log ""
  console.log "RECOMMENDED SECTIONS:"
  console.log "  ## Pending              — outstanding/blocked work"
  console.log "  ## Key Files            — important file paths"
  console.log ""
  console.log "OPTIONAL SECTIONS:"
  console.log "  ## Current Team Layout  — pane/agent layout"
  console.log "  ## Current State        — status summary"
  console.log "  ## Key Rules            — operational rules"
  console.log "  ## Key Architecture Decisions"
  console.log "  ## Git Status"
  console.log "  ## Next Tasks"
  console.log "  ## Test Suites"
}

context.validate() # <file> # validate a context file against the schema
{
  local file="$1"

  if [ -z "$file" ]; then
    error.log "Usage: context validate <file>"
    create.result 1 "no file specified"
    return $(result)
  fi

  if [ ! -f "$file" ]; then
    error.log "File not found: $file"
    create.result 1 "file not found: $file"
    return $(result)
  fi

  local errors=0
  local warnings=0
  local header
  header=$(head -6 "$file")

  # ── Title (line 1 only) ──
  local title
  title=$(head -1 "$file")
  if ! echo "$title" | grep -qE '^# .+ — .+Context$'; then
    # Also accept en dash and hyphen as separator
    if echo "$title" | grep -qiE '^# .+Context'; then
      echo "  ERROR: TITLE: must use '# <Role> — Session Context' format (line 1: $title)"
    else
      echo "  ERROR: TITLE: line 1 must be '# <Role Name> — Session Context' (got: $title)"
    fi
    errors=$((errors + 1))
  fi

  # ── Required metadata (in header region, lines 1-6) ──

  # **Updated**: with ISO date
  if ! echo "$header" | grep -q '^\*\*Updated\*\*:'; then
    echo "  ERROR: MISSING: **Updated** metadata field (required in lines 1-6)"
    errors=$((errors + 1))
  else
    if ! echo "$header" | grep '^\*\*Updated\*\*:' | grep -qE '[0-9]{4}-[0-9]{2}-[0-9]{2}'; then
      echo "  ERROR: FORMAT: **Updated** must contain ISO date (YYYY-MM-DD)"
      errors=$((errors + 1))
    fi
  fi

  # **Role**:
  if ! echo "$header" | grep -q '^\*\*Role\*\*:'; then
    echo "  ERROR: MISSING: **Role** metadata field (required in lines 1-6)"
    errors=$((errors + 1))
  fi

  # **Pane**:
  if ! echo "$header" | grep -q '^\*\*Pane\*\*:'; then
    echo "  ERROR: MISSING: **Pane** metadata field (required in lines 1-6)"
    errors=$((errors + 1))
  fi

  # ── Required sections ──

  # ## Recovery Steps — must exist and contain ≥1 numbered item
  if ! grep -q '^## Recovery' "$file"; then
    echo "  ERROR: MISSING: ## Recovery Steps section"
    errors=$((errors + 1))
  else
    if ! grep -q '^## Recovery Steps' "$file"; then
      echo "  ERROR: FORMAT: section must be '## Recovery Steps' (not '## Recovery')"
      errors=$((errors + 1))
    fi
    # Check for at least one numbered item after the Recovery heading
    local recoveryHasItems
    recoveryHasItems=$(awk '/^## Recovery/{found=1; next} /^## /{found=0} found && /^[0-9]+\./' "$file")
    if [ -z "$recoveryHasItems" ]; then
      echo "  ERROR: CONTENT: ## Recovery Steps must contain at least one numbered item (1. ...)"
      errors=$((errors + 1))
    fi
  fi

  # ## Completed Work (prefix match)
  if ! grep -q '^## Completed' "$file"; then
    echo "  ERROR: MISSING: ## Completed Work section"
    errors=$((errors + 1))
  else
    if ! grep -q '^## Completed Work' "$file"; then
      local actual
      actual=$(grep '^## Completed' "$file" | head -1)
      echo "  ERROR: FORMAT: section must start with '## Completed Work' (got: $actual)"
      errors=$((errors + 1))
    fi
  fi

  # ── Recommended sections (warnings) ──

  if ! grep -q '^## Pending' "$file"; then
    echo "  WARN:  RECOMMENDED: ## Pending section"
    warnings=$((warnings + 1))
  fi

  if ! grep -q '^## Key Files' "$file"; then
    echo "  WARN:  RECOMMENDED: ## Key Files section"
    warnings=$((warnings + 1))
  fi

  # ── Result ──
  if [ $errors -eq 0 ]; then
    echo "  PASS: $(basename "$file") ($warnings warnings)"
    create.result 0 "valid ($warnings warnings)"
  else
    echo "  FAIL: $(basename "$file") ($errors errors, $warnings warnings)"
    create.result 1 "invalid ($errors errors, $warnings warnings)"
  fi
  return $(result)
}

context.validate.completion.file() # # completion for file parameter
{
  ls session/agents/*.context.md 2>/dev/null
}

context.validate.all() # # validate all agent context files
{
  local dir="session/agents"
  local total=0
  local pass=0
  local fail=0

  if [ ! -d "$dir" ]; then
    error.log "Directory not found: $dir"
    create.result 1 "directory not found: $dir"
    return $(result)
  fi

  for file in "$dir"/*.context.md; do
    [ -f "$file" ] || continue
    total=$((total + 1))
    echo "--- $(basename "$file") ---"
    if context.validate "$file"; then
      pass=$((pass + 1))
    else
      fail=$((fail + 1))
    fi
    echo ""
  done

  echo "Results: $pass/$total passed ($fail failed)"
  if [ $fail -eq 0 ]; then
    create.result 0 "$pass/$total passed"
  else
    create.result 1 "$pass/$total passed ($fail failed)"
  fi
  return $(result)
}

# ─────────────────────────────────────────────────────────────────────────────
# LIFECYCLE — automated save-before-compact state machine
# ─────────────────────────────────────────────────────────────────────────────
#
# States: active → saving → saved → compacting → recovering → active
#
# Per-role state machine stored at:
#   $CONFIG_PATH/stateMachines/<machine_name>.states.env
#
# Machine name: role with [-./] replaced by _ (e.g., oosh-expert → oosh_expert)
# ─────────────────────────────────────────────────────────────────────────────

private.context.role.to.machine() # <role> # convert role name to valid state machine name
{
  local role="$1"
  echo "$role" | tr -- '-.' '__'
}

private.context.role.list() # # list available roles from context files
{
  local workspace
  workspace="$(git rev-parse --show-toplevel 2>/dev/null)"
  ls "$workspace/session/agents/"*.context.md 2>/dev/null \
    | while read f; do basename "$f" .context.md; done
}

context.lifecycle.init() # <role> # create lifecycle state machine for a role
{
  local role="$1"
  if [ -z "$role" ]; then
    error.log "Usage: context lifecycle.init <role>"
    create.result 1 "no role specified"
    return $(result)
  fi

  local machine
  machine=$(private.context.role.to.machine "$role")

  # Check if already exists
  if [ -f "$CONFIG_PATH/stateMachines/${machine}.states.env" ]; then
    console.log "Lifecycle machine '$machine' already exists"
    create.result 0 "already exists"
    return $(result)
  fi

  # Create machine via state script
  "$OOSH_DIR/state" machine.create "$machine" >/dev/null 2>&1

  # Add lifecycle states: active, saving, saved, compacting, recovering
  "$OOSH_DIR/state" of "$machine" add active >/dev/null 2>&1
  "$OOSH_DIR/state" of "$machine" add saving >/dev/null 2>&1
  "$OOSH_DIR/state" of "$machine" add saved >/dev/null 2>&1
  "$OOSH_DIR/state" of "$machine" add compacting >/dev/null 2>&1
  "$OOSH_DIR/state" of "$machine" add recovering >/dev/null 2>&1

  # Finalize: transition past setup to started, then set to active
  "$OOSH_DIR/state" of "$machine" set active >/dev/null 2>&1

  console.log "Lifecycle machine created for role '$role' (machine: $machine)"
  create.result 0 "created"
  return $(result)
}

context.lifecycle.init.completion.role() # # completion for role parameter
{
  private.context.role.list
}

context.lifecycle.save() # <role> # validate context file and mark state as saved
{
  local role="$1"
  if [ -z "$role" ]; then
    error.log "Usage: context lifecycle.save <role>"
    create.result 1 "no role specified"
    return $(result)
  fi

  local machine
  machine=$(private.context.role.to.machine "$role")
  local workspace
  workspace="$(git rev-parse --show-toplevel 2>/dev/null)"
  local file="$workspace/session/agents/${role}.context.md"

  # Check machine exists
  if [ ! -f "$CONFIG_PATH/stateMachines/${machine}.states.env" ]; then
    error.log "No lifecycle machine for role '$role'. Run: context lifecycle.init $role"
    create.result 1 "machine not found"
    return $(result)
  fi

  # Validate the context file
  if [ ! -f "$file" ]; then
    error.log "Context file not found: $file"
    create.result 1 "context file not found"
    return $(result)
  fi

  echo "--- Validating ${role}.context.md ---"
  if context.validate "$file"; then
    # Transition: saving → saved
    "$OOSH_DIR/state" of "$machine" set saving >/dev/null 2>&1
    "$OOSH_DIR/state" of "$machine" set saved >/dev/null 2>&1
    console.log "Context saved and validated for role '$role'"
    create.result 0 "saved"
  else
    # Transition to saving but not saved (validation failed)
    "$OOSH_DIR/state" of "$machine" set saving >/dev/null 2>&1
    error.log "Context validation failed for role '$role' — state is 'saving' (not 'saved')"
    create.result 1 "validation failed"
  fi
  return $(result)
}

context.lifecycle.save.completion.role() # # completion for role parameter
{
  private.context.role.list
}

context.lifecycle.status() # <?role> # show lifecycle state for a role (or all roles)
{
  local role="$1"

  if [ -z "$role" ]; then
    # Show all lifecycle machines
    local found=0
    local workspace
    workspace="$(git rev-parse --show-toplevel 2>/dev/null)"
    for file in "$workspace/session/agents/"*.context.md; do
      [ -f "$file" ] || continue
      local r
      r=$(basename "$file" .context.md)
      local m
      m=$(private.context.role.to.machine "$r")
      if [ -f "$CONFIG_PATH/stateMachines/${m}.states.env" ]; then
        found=$((found + 1))
        local stateName
        stateName=$("$OOSH_DIR/state" of "$m" name 2>/dev/null)
        echo "  $r: $stateName"
      fi
    done
    if [ $found -eq 0 ]; then
      echo "  No lifecycle machines found. Use: context lifecycle.init <role>"
    fi
    create.result 0 "$found machines"
    return $(result)
  fi

  local machine
  machine=$(private.context.role.to.machine "$role")

  if [ ! -f "$CONFIG_PATH/stateMachines/${machine}.states.env" ]; then
    error.log "No lifecycle machine for role '$role'"
    create.result 1 "machine not found"
    return $(result)
  fi

  local stateName
  stateName=$("$OOSH_DIR/state" of "$machine" name 2>/dev/null)
  echo "  $role: $stateName"
  create.result 0 "$stateName"
  return $(result)
}

context.lifecycle.status.completion.role() # # completion for role parameter
{
  private.context.role.list
}

context.lifecycle.pre.compact() # # pre-compact check (called by hook or manually)
{
  local role="${HIVEMIND_ROLE:-}"
  if [ -z "$role" ]; then
    echo "SKIP: HIVEMIND_ROLE not set — no lifecycle check"
    create.result 0 "skipped (no role)"
    return $(result)
  fi

  local machine
  machine=$(private.context.role.to.machine "$role")
  local workspace
  workspace="$(git rev-parse --show-toplevel 2>/dev/null)"
  local file="$workspace/session/agents/${role}.context.md"

  # Check context file exists
  if [ ! -f "$file" ]; then
    echo "WARNING: Context file missing: $file"
    echo "  Save your context before compacting!"
    create.result 1 "context file missing"
    return $(result)
  fi

  # Quick validation: check required sections
  local missing=0
  if ! grep -q '^## Recovery' "$file"; then
    echo "WARNING: Missing ## Recovery Steps in $file"
    missing=$((missing + 1))
  fi
  if ! grep -q '^## Completed' "$file"; then
    echo "WARNING: Missing ## Completed Work in $file"
    missing=$((missing + 1))
  fi
  if ! head -6 "$file" | grep -q '^\*\*Updated\*\*:'; then
    echo "WARNING: Missing **Updated** metadata in $file"
    missing=$((missing + 1))
  fi

  if [ $missing -gt 0 ]; then
    echo "WARNING: Context file has $missing issues — update before compacting!"
    create.result 1 "$missing issues"
    return $(result)
  fi

  # Auto-mark as saved if machine exists and context passes
  if [ -f "$CONFIG_PATH/stateMachines/${machine}.states.env" ]; then
    "$OOSH_DIR/state" of "$machine" set saved >/dev/null 2>&1
    echo "Context validated — state set to 'saved' for role '$role'"
  else
    echo "Context validated (no lifecycle machine — run: context lifecycle.init $role)"
  fi

  create.result 0 "validated"
  return $(result)
}

context.lifecycle.recover() # <role> # transition back to active after compact
{
  local role="$1"
  if [ -z "$role" ]; then
    error.log "Usage: context lifecycle.recover <role>"
    create.result 1 "no role specified"
    return $(result)
  fi

  local machine
  machine=$(private.context.role.to.machine "$role")

  if [ ! -f "$CONFIG_PATH/stateMachines/${machine}.states.env" ]; then
    error.log "No lifecycle machine for role '$role'. Run: context lifecycle.init $role"
    create.result 1 "machine not found"
    return $(result)
  fi

  # Transition: recovering → active
  "$OOSH_DIR/state" of "$machine" set recovering >/dev/null 2>&1
  "$OOSH_DIR/state" of "$machine" set active >/dev/null 2>&1

  console.log "Lifecycle recovered for role '$role' — state is 'active'"
  create.result 0 "active"
  return $(result)
}

context.lifecycle.recover.completion.role() # # completion for role parameter
{
  private.context.role.list
}

# ─────────────────────────────────────────────────────────────────────────────
# RECOVERY — deterministic post-compact recovery for any agent
# ─────────────────────────────────────────────────────────────────────────────

context.recover() # <role> # deterministic post-compact recovery — outputs context and recovery checklist
{
  local role="$1"
  # Fall back to HIVEMIND_ROLE if no role argument
  if [ -z "$role" ]; then
    role="${HIVEMIND_ROLE:-}"
  fi
  if [ -z "$role" ]; then
    error.log "Usage: context recover <role>"
    create.result 1 "no role specified"
    return $(result)
  fi

  local workspace
  workspace="$(git rev-parse --show-toplevel 2>/dev/null)"
  local contextFile="$workspace/session/agents/${role}.context.md"
  local skillFile="$workspace/.claude/agents/${role}/SKILL.md"

  # Validate context file exists
  if [ ! -f "$contextFile" ]; then
    error.log "Context file not found: $contextFile"
    echo "  Create context file before recovery is possible."
    create.result 1 "context file not found"
    return $(result)
  fi

  # Output deterministic recovery sequence
  echo "╔══════════════════════════════════════════════════════════════╗"
  echo "║           RECOVERY — $role"
  echo "╠══════════════════════════════════════════════════════════════╣"
  echo "║  1. Read context below (displayed inline)                   ║"
  echo "║  2. Read SKILL.md (path shown below)                        ║"
  echo "║  3. Read docs/oosh-architecture.md                          ║"
  echo "║  4. Check current tasks with orchestrator                   ║"
  echo "║  5. Report ready via: ./hiveMind send orchestrator 'ready'  ║"
  echo "╚══════════════════════════════════════════════════════════════╝"
  echo ""

  # SKILL.md path
  if [ -f "$skillFile" ]; then
    echo "SKILL.md: $skillFile"
  else
    echo "SKILL.md: NOT FOUND at $skillFile"
  fi
  echo "Architecture: $OOSH_DIR/docs/oosh-architecture.md"
  echo ""

  # Display full context file inline
  echo "── Context File: $(basename "$contextFile") ──"
  cat "$contextFile"
  echo ""
  echo "── End Context ──"
  echo ""

  # Lifecycle transition (if machine exists)
  local machine
  machine=$(private.context.role.to.machine "$role")
  if [ -f "$CONFIG_PATH/stateMachines/${machine}.states.env" ]; then
    "$OOSH_DIR/state" of "$machine" set recovering >/dev/null 2>&1
    "$OOSH_DIR/state" of "$machine" set active >/dev/null 2>&1
    echo "Lifecycle: $role → active"
  fi

  echo "Recovery complete. Follow steps 2-5 above."
  create.result 0 "recovered"
  return $(result)
}

context.recover.completion.role() { private.context.role.list; }

context.usage()
{
  local this=${0##*/}
  echo "
  $this — Agent context file management and validation

  SCHEMA:
    schema              display the context file schema
    validate <file>     validate a context file against the schema
    validate.all        validate all agent context files

  LIFECYCLE:
    lifecycle.init <role>       create lifecycle state machine for a role
    lifecycle.save <role>       validate context file + transition to saved
    lifecycle.status <?role>    show lifecycle state (or all roles)
    lifecycle.pre.compact       pre-compact check (uses HIVEMIND_ROLE)
    lifecycle.recover <role>    transition back to active after compact

  RECOVERY:
    recover <role>              deterministic post-compact recovery (outputs
                                context inline + recovery checklist)
                                Falls back to HIVEMIND_ROLE if no role given.

  Examples:
    ./$this schema
    ./$this validate session/agents/oosh-expert.context.md
    ./$this validate.all
    ./$this lifecycle.init oosh-expert
    ./$this lifecycle.save oosh-expert
    ./$this lifecycle.status
    ./$this lifecycle.recover oosh-expert
    ./$this recover oosh-expert

  Documentation: docs/context-schema.md
  "
}

context.start() # <method> <?args> # context file management
{
  source this
  this.start "$@"
}

context.start "$@"
