#!/usr/bin/env bash
# Tests for scrumMaster measurement methods (CMM4)
# Tests private parsing methods with synthetic pane content — no live tmux needed

level=$1
if [ -z "$level" ]; then
  level=1
fi
echo "starting: ${BASH_SOURCE[@]##*/} <LOG_LEVEL=$level>"

source this
source test.suite
source $OOSH_DIR/scrumMaster

log.level $level

# Test metrics directory (isolated)
TEST_METRICS_DIR="${TMPDIR:-/tmp}/scrumMaster_test_metrics_$$"
SCRUMMASTER_METRICS_DIR="$TEST_METRICS_DIR"

# Cleanup
cleanup_metrics() {
  rm -rf "$TEST_METRICS_DIR" 2>/dev/null
}
cleanup_metrics

# ============================================================================
# T1: normalize.k converts "4.6k" to 4600
# ============================================================================
test.case $level "T1: normalize.k converts 4.6k to 4600" true
local_result=$(private.scrumMaster.parse.normalize.k "4.6k")
if [ "$local_result" = "4600" ]; then
  expect.pass "4.6k → 4600"
else
  expect.fail "expected 4600, got $local_result"
fi

# ============================================================================
# T2: normalize.k passes through plain numbers
# ============================================================================
test.case $level "T2: normalize.k passes through 590" true
local_result=$(private.scrumMaster.parse.normalize.k "590")
if [ "$local_result" = "590" ]; then
  expect.pass "590 → 590"
else
  expect.fail "expected 590, got $local_result"
fi

# ============================================================================
# T3: parse.tokens extracts down tokens with k suffix
# ============================================================================
CONTENT_TOKENS="Some output
↓ 7.9k tokens  ↑ 20.3k tokens
More output"

test.case $level "T3: parse.tokens extracts down tokens (7.9k)" true
private.scrumMaster.parse.tokens "$CONTENT_TOKENS"
if [ "$METRIC_TOKENS_DOWN" = "7900" ]; then
  expect.pass "down=7900"
else
  expect.fail "expected down=7900, got $METRIC_TOKENS_DOWN"
fi

# ============================================================================
# T4: parse.tokens extracts up tokens with k suffix
# ============================================================================
test.case $level "T4: parse.tokens extracts up tokens (20.3k)" true
private.scrumMaster.parse.tokens "$CONTENT_TOKENS"
if [ "$METRIC_TOKENS_UP" = "20300" ]; then
  expect.pass "up=20300"
else
  expect.fail "expected up=20300, got $METRIC_TOKENS_UP"
fi

# ============================================================================
# T5: parse.tokens extracts plain number tokens
# ============================================================================
CONTENT_PLAIN="↓ 590 tokens  ↑ 120 tokens"

test.case $level "T5: parse.tokens plain numbers" true
private.scrumMaster.parse.tokens "$CONTENT_PLAIN"
if [ "$METRIC_TOKENS_DOWN" = "590" ] && [ "$METRIC_TOKENS_UP" = "120" ]; then
  expect.pass "down=590 up=120"
else
  expect.fail "expected down=590 up=120, got down=$METRIC_TOKENS_DOWN up=$METRIC_TOKENS_UP"
fi

# ============================================================================
# T6: parse.timing extracts think time
# ============================================================================
CONTENT_TIMING="Kneading… thought for 33s
(4m 9s, 14 tool uses, ↓ 7.9k tokens)"

test.case $level "T6: parse.timing extracts think time" true
private.scrumMaster.parse.timing "$CONTENT_TIMING"
if [ "$METRIC_THINK_TIME_S" = "33" ]; then
  expect.pass "think=33s"
else
  expect.fail "expected think=33, got $METRIC_THINK_TIME_S"
fi

# ============================================================================
# T7: parse.timing extracts wall time
# ============================================================================
test.case $level "T7: parse.timing extracts wall time" true
private.scrumMaster.parse.timing "$CONTENT_TIMING"
if [ "$METRIC_WALL_TIME_S" = "249" ]; then
  expect.pass "wall=249s (4m 9s)"
else
  expect.fail "expected wall=249, got $METRIC_WALL_TIME_S"
fi

# ============================================================================
# T8: parse.timing extracts tool uses
# ============================================================================
test.case $level "T8: parse.timing extracts tool uses" true
private.scrumMaster.parse.timing "$CONTENT_TIMING"
if [ "$METRIC_TOOL_USES" = "14" ]; then
  expect.pass "tools=14"
else
  expect.fail "expected tools=14, got $METRIC_TOOL_USES"
fi

# ============================================================================
# T9: parse.state detects active (present-tense spinner)
# ============================================================================
CONTENT_ACTIVE="Kneading… thought for 12s"

test.case $level "T9: parse.state detects active" true
private.scrumMaster.parse.state "$CONTENT_ACTIVE"
if [ "$METRIC_STATE" = "active" ]; then
  expect.pass "state=active"
else
  expect.fail "expected active, got $METRIC_STATE"
fi

# ============================================================================
# T10: parse.state detects completed (past-tense)
# ============================================================================
CONTENT_COMPLETED="Brewed for 2m 15s"

test.case $level "T10: parse.state detects completed" true
private.scrumMaster.parse.state "$CONTENT_COMPLETED"
if [ "$METRIC_STATE" = "completed" ]; then
  expect.pass "state=completed"
else
  expect.fail "expected completed, got $METRIC_STATE"
fi

# ============================================================================
# T11: parse.state detects idle prompt
# ============================================================================
CONTENT_IDLE="some output

> "

test.case $level "T11: parse.state detects idle" true
private.scrumMaster.parse.state "$CONTENT_IDLE"
if [ "$METRIC_STATE" = "idle" ]; then
  expect.pass "state=idle"
else
  expect.fail "expected idle, got $METRIC_STATE"
fi

# ============================================================================
# T12: parse.state detects permission prompt
# ============================================================================
CONTENT_PERM="Allow this tool call?
  Allow     Deny"

test.case $level "T12: parse.state detects permission" true
private.scrumMaster.parse.state "$CONTENT_PERM"
if [ "$METRIC_STATE" = "permission" ]; then
  expect.pass "state=permission"
else
  expect.fail "expected permission, got $METRIC_STATE"
fi

# ============================================================================
# T13: metrics.save creates file
# ============================================================================
METRIC_TOKENS_UP=500
METRIC_TOKENS_DOWN=1000
METRIC_STATE="active"
METRIC_ACTIVITY="Kneading"

test.case $level "T13: metrics.save creates file" true
private.scrumMaster.metrics.save "test-agent" "testSession:0.0"
if ls "$TEST_METRICS_DIR"/test-agent.*.env >/dev/null 2>&1; then
  expect.pass "file created in $TEST_METRICS_DIR"
else
  expect.fail "no metric file found in $TEST_METRICS_DIR"
fi

# ============================================================================
# T14: public measure functions exist
# ============================================================================
test.case $level "T14: measure.pane function exists" true
if [ "$(type -t scrumMaster.pane.capture)" = "function" ]; then
  expect.pass "pane.capture is a function"
else
  expect.fail "pane.capture not found"
fi

# ============================================================================
# T15: subscription.api.auth reads credentials from ~/.claude/.credentials.json
# ============================================================================
test.case $level "T15: subscription.api.auth extracts OAuth token" echo "checking"
AUTH_TOKEN=$(private.scrumMaster.subscription.auth 2>/dev/null)
AUTH_RC=$?
if [ $AUTH_RC -eq 0 ] && [ -n "$AUTH_TOKEN" ] && [ "$AUTH_TOKEN" != "null" ]; then
  expect.pass "T15: OAuth token extracted (${#AUTH_TOKEN} chars)"
else
  expect.fail "T15: auth failed (rc=$AUTH_RC, token length=${#AUTH_TOKEN})"
fi

# ============================================================================
# T16: subscription.api.auth fails gracefully without credentials
# ============================================================================
test.case $level "T16: auth fails gracefully with missing creds file" echo "checking"
REAL_CREDS="$HOME/.claude/.credentials.json"
if [ -f "$REAL_CREDS" ]; then
  mv "$REAL_CREDS" "${REAL_CREDS}.test_bak"
  AUTH_FAIL_OUTPUT=$(private.scrumMaster.subscription.auth 2>&1)
  AUTH_FAIL_RC=$?
  mv "${REAL_CREDS}.test_bak" "$REAL_CREDS"
  if [ $AUTH_FAIL_RC -ne 0 ]; then
    expect.pass "T16: returns non-zero when creds missing"
  else
    expect.fail "T16: should fail without credentials file"
  fi
else
  expect.pass "T16: SKIPPED (no credentials file to test with)"
fi

# ============================================================================
# T17: measure.subscription.api runs end-to-end and shows data
# ============================================================================
test.case $level "T17: subscription.status runs end-to-end (shortcut)" \
  scrumMaster.subscription.status
if [ "$RETURN_VALUE" -eq 0 ]; then
  expect.pass "T17: measure.subscription.api returned 0"
else
  expect.fail "T17: measure.subscription.api failed (rc=$RETURN_VALUE)"
fi

# ============================================================================
# T18: 5-hour utilization is a real number (not empty)
# ============================================================================
# Call subscription.status directly to populate SUBSCRIPTION_* vars in this shell
scrumMaster.subscription.status > /dev/null 2>&1
test.case $level "T18: SUBSCRIPTION_FIVE_HOUR_UTIL has value" echo "checking"
if [ -n "$SUBSCRIPTION_FIVE_HOUR_UTIL" ] && [ "$SUBSCRIPTION_FIVE_HOUR_UTIL" != "n/a" ]; then
  if echo "$SUBSCRIPTION_FIVE_HOUR_UTIL" | grep -qE '^[0-9]+\.?[0-9]*$'; then
    expect.pass "T18: 5h util = ${SUBSCRIPTION_FIVE_HOUR_UTIL}%"
  else
    expect.fail "T18: 5h util not a number: $SUBSCRIPTION_FIVE_HOUR_UTIL"
  fi
else
  expect.fail "T18: SUBSCRIPTION_FIVE_HOUR_UTIL empty or n/a"
fi

# ============================================================================
# T19: 7-day utilization is a real number
# ============================================================================
test.case $level "T19: SUBSCRIPTION_SEVEN_DAY_UTIL has value" echo "checking"
if [ -n "$SUBSCRIPTION_SEVEN_DAY_UTIL" ] && [ "$SUBSCRIPTION_SEVEN_DAY_UTIL" != "n/a" ]; then
  if echo "$SUBSCRIPTION_SEVEN_DAY_UTIL" | grep -qE '^[0-9]+\.?[0-9]*$'; then
    expect.pass "T19: 7d util = ${SUBSCRIPTION_SEVEN_DAY_UTIL}%"
  else
    expect.fail "T19: 7d util not a number: $SUBSCRIPTION_SEVEN_DAY_UTIL"
  fi
else
  expect.fail "T19: SUBSCRIPTION_SEVEN_DAY_UTIL empty or n/a"
fi

# ============================================================================
# T20: Metrics file persisted with subscription data
# ============================================================================
test.case $level "T20: metrics file persisted" echo "checking"
LATEST_METRIC=$(ls -t "${CONFIG_PATH:-$HOME/config}/metrics"/subscription.*.scenario.env 2>/dev/null | head -1)
if [ -n "$LATEST_METRIC" ] && [ -f "$LATEST_METRIC" ]; then
  if grep -q 'SUBSCRIPTION_FIVE_HOUR_UTIL' "$LATEST_METRIC"; then
    expect.pass "T20: metrics file has SUBSCRIPTION_FIVE_HOUR_UTIL"
  else
    expect.fail "T20: metrics file missing SUBSCRIPTION_FIVE_HOUR_UTIL"
  fi
else
  expect.fail "T20: no subscription metrics file found"
fi

# ============================================================================
# T21: Method is non-disruptive (doesn't send to other panes)
# ============================================================================
test.case $level "T21: method is non-disruptive (shell-only, no tmux send)" echo "checking"
FUNC_SOURCE=$(declare -f scrumMaster.subscription.status 2>/dev/null)
if echo "$FUNC_SOURCE" | grep -q "send-keys\|otmux send\|hiveMind send"; then
  expect.fail "T21: method contains tmux/otmux send commands"
else
  expect.pass "T21: no send commands — non-disruptive"
fi

# ============================================================================
# T22: DRY review — auth method uses python3 with jq fallback
# ============================================================================
test.case $level "T22: auth has python3-first, jq-fallback pattern" echo "checking"
AUTH_SOURCE=$(declare -f private.scrumMaster.subscription.auth 2>/dev/null)
if echo "$AUTH_SOURCE" | grep -q "python3" && echo "$AUTH_SOURCE" | grep -q "jq"; then
  expect.pass "T22: dual parser pattern (python3 primary, jq fallback)"
else
  expect.fail "T22: expected both python3 and jq paths in auth"
fi

# ============================================================================
# T23: parse method sets all expected SUBSCRIPTION_* variables
# ============================================================================
test.case $level "T23: parse sets all expected variables" echo "checking"
MISSING_VARS=""
for var in SUBSCRIPTION_FIVE_HOUR_UTIL SUBSCRIPTION_FIVE_HOUR_RESETS \
           SUBSCRIPTION_SEVEN_DAY_UTIL SUBSCRIPTION_SEVEN_DAY_RESETS \
           SUBSCRIPTION_EXTRA_ENABLED; do
  if [ -z "${!var+x}" ]; then
    MISSING_VARS="$MISSING_VARS $var"
  fi
done
if [ -z "$MISSING_VARS" ]; then
  expect.pass "T23: all SUBSCRIPTION_* variables set"
else
  expect.fail "T23: missing vars:$MISSING_VARS"
fi

# ============================================================================
# NEW METHOD TESTS — naming consolidation (T24+)
# Every new public scrumMaster method gets: existence, callable, mandatory checklist
# ============================================================================

# ── T24-T37: Function existence checks ──────────────────────────────────────

test.case $level "T24: context.read function exists" echo "checking"
if [ "$(type -t scrumMaster.context.read)" = "function" ]; then
  expect.pass "T24: context.read exists"
else
  expect.fail "T24: context.read missing"
fi

test.case $level "T25: speed.check function exists" echo "checking"
if [ "$(type -t scrumMaster.speed.check)" = "function" ]; then
  expect.pass "T25: speed.check exists"
else
  expect.fail "T25: speed.check missing"
fi

test.case $level "T26: team.capture function exists" echo "checking"
if [ "$(type -t scrumMaster.team.capture)" = "function" ]; then
  expect.pass "T26: team.capture exists"
else
  expect.fail "T26: team.capture missing"
fi

test.case $level "T27: subscription.check function exists" echo "checking"
if [ "$(type -t scrumMaster.subscription.check)" = "function" ]; then
  expect.pass "T27: subscription.check exists"
else
  expect.fail "T27: subscription.check missing"
fi

test.case $level "T28: health.check function exists" echo "checking"
if [ "$(type -t scrumMaster.health.check)" = "function" ]; then
  expect.pass "T28: health.check exists"
else
  expect.fail "T28: health.check missing"
fi

test.case $level "T29: health.evaluate function exists" echo "checking"
if [ "$(type -t scrumMaster.health.evaluate)" = "function" ]; then
  expect.pass "T29: health.evaluate exists"
else
  expect.fail "T29: health.evaluate missing"
fi

test.case $level "T30: velocity.check function exists" echo "checking"
if [ "$(type -t scrumMaster.velocity.check)" = "function" ]; then
  expect.pass "T30: velocity.check exists"
else
  expect.fail "T30: velocity.check missing"
fi

test.case $level "T31: velocity.target function exists" echo "checking"
if [ "$(type -t scrumMaster.velocity.target)" = "function" ]; then
  expect.pass "T31: velocity.target exists"
else
  expect.fail "T31: velocity.target missing"
fi

test.case $level "T32: metrics.collect function exists" echo "checking"
if [ "$(type -t scrumMaster.metrics.collect)" = "function" ]; then
  expect.pass "T32: metrics.collect exists"
else
  expect.fail "T32: metrics.collect missing"
fi

test.case $level "T33: subscription one-liner function exists" echo "checking"
if [ "$(type -t scrumMaster.subscription)" = "function" ]; then
  expect.pass "T33: subscription exists"
else
  expect.fail "T33: subscription missing"
fi

test.case $level "T34: velocity shortcut function exists" echo "checking"
if [ "$(type -t scrumMaster.velocity)" = "function" ]; then
  expect.pass "T34: velocity exists"
else
  expect.fail "T34: velocity missing"
fi

test.case $level "T35: cycle function exists" echo "checking"
if [ "$(type -t scrumMaster.cycle)" = "function" ]; then
  expect.pass "T35: cycle exists"
else
  expect.fail "T35: cycle missing"
fi

test.case $level "T36: dashboard function exists" echo "checking"
if [ "$(type -t scrumMaster.dashboard)" = "function" ]; then
  expect.pass "T36: dashboard exists"
else
  expect.fail "T36: dashboard missing"
fi

# ── T37-T39: Missing required params → error (mandatory checklist) ──────────

test.case $level "T37: context.read with no agent_name returns error" echo "checking"
OUTPUT=$(scrumMaster.context.read 2>&1)
RC=$?
if [ $RC -ne 0 ] || echo "$OUTPUT" | grep -qi "usage\|agent\|required\|no.*name"; then
  expect.pass "T37: context.read requires agent_name"
else
  expect.fail "T37: context.read should fail without agent_name (rc=$RC)"
fi

test.case $level "T38: pane.capture with no agent_name returns error" echo "checking"
OUTPUT=$(scrumMaster.pane.capture 2>&1)
RC=$?
if [ $RC -ne 0 ] || echo "$OUTPUT" | grep -qi "usage\|agent\|required\|no.*name"; then
  expect.pass "T38: pane.capture requires agent_name"
else
  expect.fail "T38: pane.capture should fail without agent_name (rc=$RC)"
fi

test.case $level "T39: speed.check with no agent_name returns error" echo "checking"
OUTPUT=$(scrumMaster.speed.check 2>&1)
RC=$?
if [ $RC -ne 0 ] || echo "$OUTPUT" | grep -qi "usage\|agent\|required\|no.*name"; then
  expect.pass "T39: speed.check requires agent_name"
else
  expect.fail "T39: speed.check should fail without agent_name (rc=$RC)"
fi

# ── T40-T46: Functional — methods return data (not empty) ───────────────────

test.case $level "T40: subscription one-liner returns data" \
  scrumMaster.subscription
OUTPUT=$(scrumMaster.subscription 2>/dev/null)
if echo "$OUTPUT" | grep -qE '[0-9]+.*%.*5h'; then
  expect.pass "T40: subscription shows X% 5h pattern"
else
  expect.fail "T40: subscription output missing expected pattern: $OUTPUT"
fi

test.case $level "T41: subscription.status returns detailed view" \
  scrumMaster.subscription.status
OUTPUT=$(scrumMaster.subscription.status 2>/dev/null)
if echo "$OUTPUT" | grep -q "5-hour usage"; then
  expect.pass "T41: subscription.status shows 5-hour usage"
else
  expect.fail "T41: subscription.status missing 5-hour usage field"
fi

test.case $level "T42: subscription.check returns YES/NO/CAUTION" \
  scrumMaster.subscription.check
OUTPUT=$(scrumMaster.subscription.check 2>/dev/null)
if echo "$OUTPUT" | grep -qiE 'YES|NO|CAUTION'; then
  expect.pass "T42: subscription.check returns verdict"
else
  expect.fail "T42: subscription.check missing YES/NO/CAUTION: $OUTPUT"
fi

test.case $level "T43: health.evaluate returns EVALUATE_RESULT" \
  scrumMaster.health.evaluate
OUTPUT=$(scrumMaster.health.evaluate 2>/dev/null)
if echo "$OUTPUT" | grep -q "EVALUATE_RESULT="; then
  expect.pass "T43: health.evaluate returns EVALUATE_RESULT"
else
  expect.fail "T43: health.evaluate missing EVALUATE_RESULT: $OUTPUT"
fi

test.case $level "T44: velocity.target returns burn assessment" \
  scrumMaster.velocity.target
OUTPUT=$(scrumMaster.velocity.target 2>/dev/null)
if echo "$OUTPUT" | grep -qE 'too_fast|on_target|too_slow'; then
  expect.pass "T44: velocity.target returns burn rate"
else
  expect.fail "T44: velocity.target missing burn rate: $OUTPUT"
fi

test.case $level "T45: velocity.check returns VELOCITY_ vars" \
  scrumMaster.velocity.check
OUTPUT=$(scrumMaster.velocity.check 2>/dev/null)
if echo "$OUTPUT" | grep -q "VELOCITY_"; then
  expect.pass "T45: velocity.check returns VELOCITY_ data"
else
  expect.fail "T45: velocity.check missing VELOCITY_ data: $OUTPUT"
fi

test.case $level "T46: metrics.collect logs KPIs" \
  scrumMaster.metrics.collect
OUTPUT=$(scrumMaster.metrics.collect 2>/dev/null)
if echo "$OUTPUT" | grep -qi "KPI\|logged\|velocity"; then
  expect.pass "T46: metrics.collect produces KPI output"
else
  expect.fail "T46: metrics.collect missing KPI output: $OUTPUT"
fi

# ── T47-T48: No public "measure" methods remain ────────────────────────────

test.case $level "T47: no public measure.* methods in completion" echo "checking"
MEASURE_COUNT=$(c2 function.completion $OOSH_DIR/scrumMaster 2>/dev/null | grep "^measure" | wc -l | tr -d ' ')
if [ "$MEASURE_COUNT" -eq 0 ]; then
  expect.pass "T47: zero public measure.* methods"
else
  expect.fail "T47: $MEASURE_COUNT public measure.* methods still visible"
fi

test.case $level "T48: no public measure.* function definitions" echo "checking"
MEASURE_FUNCS=$(grep -cE '^scrumMaster\.measure\.' /root/oosh/scrumMaster | head -1)
if [ "$MEASURE_FUNCS" -eq 0 ]; then
  expect.pass "T48: zero measure.* function definitions"
else
  expect.fail "T48: $MEASURE_FUNCS measure.* definitions remain"
fi

# ── T49-T50: Completion stubs exist for new methods ─────────────────────────

test.case $level "T49: completion stubs for parameterized methods" echo "checking"
MISSING=""
for method in subscription.status subscription.check health.check health.evaluate velocity.check velocity.target; do
  # Accept either method-level stub or parameter-level completion
  if ! declare -f "scrumMaster.${method}.completion" >/dev/null 2>&1; then
    has_param_completion=false
    for func in $(compgen -A function "scrumMaster.${method}.completion." 2>/dev/null); do
      has_param_completion=true
      break
    done
    if ! $has_param_completion; then
      MISSING="$MISSING $method"
    fi
  fi
done
if [ -z "$MISSING" ]; then
  expect.pass "T49: all parameterized methods have completion stubs"
else
  expect.fail "T49: missing completion stubs:$MISSING"
fi

test.case $level "T50: pane.capture has parameter completion" echo "checking"
if declare -f scrumMaster.pane.capture.completion.agent_name >/dev/null 2>&1; then
  expect.pass "T50: pane.capture.completion.agent_name exists"
else
  expect.fail "T50: pane.capture.completion.agent_name missing"
fi

# Cleanup
cleanup_metrics

test.suite.save.results
