#!/usr/bin/env bash
# scripts/run_postapply.sh — post-Terraform steps for the Nova platform pipeline.
#
# Performs steps 5–9 of run_platform.sh (after terraform apply/destroy):
#   3c. Checkov policy scan on static code (fail-fast, in run_codegen.sh)
#   5.  Runtime policy scan on the terraform plan (Wiz-or-Checkov, never both)
#   6.  Policy scan adapter → PolicyCheckResult (compliance details)
#   7.  Confidence signal compute
#   7b. HITL attestation gate (qa/prod/dr only)
#   8.  Write evidence event to DynamoDB outbox
#   9.  Publish outputs to SSM + GitHub PR comment
#   9b. Uptime monitoring sub-deploy (sourced from run_uptime.sh)
#
# Expects the work dir ($NOVA_WORK_DIR) to already contain:
#   - tf/*.tf (from run_codegen.sh)
#   - stack.json (from run_codegen.sh)
# And terraform to have already run (init/validate/plan/apply) in $WORK/tf/.
#
# Usage:
#   run_postapply.sh <contract.yml> [--environment <name>] [--quiet] [--deploy-uptime]
#
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CALLER_CWD="$(pwd)"
cd "$ROOT"

QUIET=0
DEPLOY_UPTIME=0
ENVIRONMENT_OVERRIDE=""
CONTRACT=""

while [ $# -gt 0 ]; do
  case "$1" in
    --quiet) QUIET=1; shift ;;
    --deploy-uptime) DEPLOY_UPTIME=1; shift ;;
    --environment) shift; ENVIRONMENT_OVERRIDE="$1"; shift ;;
    --environment=*) ENVIRONMENT_OVERRIDE="${1#--environment=}"; shift ;;
    -h|--help)
      echo "Usage: run_postapply.sh <contract.yml> [--environment <name>] [--quiet] [--deploy-uptime]"
      exit 0 ;;
    *) CONTRACT="$1"; shift ;;
  esac
done

[ -n "$CONTRACT" ] || { echo "FAIL: no contract file specified" >&2; exit 1; }
if ! [[ "$CONTRACT" = /* ]]; then
  CONTRACT="$CALLER_CWD/$CONTRACT"
fi
[ -f "$CONTRACT" ] || { echo "FAIL: contract not found: $CONTRACT" >&2; exit 1; }

if [ -n "$ENVIRONMENT_OVERRIDE" ]; then
  export NOVA_ENVIRONMENT_OVERRIDE="$ENVIRONMENT_OVERRIDE"
fi

CONTRACT_ID="${NOVA_CONTRACT_ID:-11111111-1111-1111-1111-111111111111}"
WORK="${NOVA_WORK_DIR:-/tmp/nova_platform_run}"
TF_DIR="$WORK/tf"

[ -d "$TF_DIR" ] || { echo "FAIL: work dir $TF_DIR not found (run run_codegen.sh first)" >&2; exit 1; }

stream() {
  local log="$1"; shift
  if [ "$QUIET" = "1" ]; then
    "$@" > "$log" 2>&1
  else
    "$@" 2>&1 | tee "$log"
  fi
}

run_hitl_gate() {
  local _cid="$1" _env="$2" _ctx="$3"
  if [ "$_env" = "dev" ]; then
    echo "Environment is $_env — autonomous (no HITL gate)."
    return 0
  fi
  echo "Environment is $_env — HITL attestation gate required$_ctx."
  local _approver="${GITHUB_ACTOR:-${FORGE_ACTOR:-}}"
  if [ -z "$_approver" ]; then
    echo "WARNING: no approver identity (GITHUB_ACTOR/FORGE_ACTOR unset)" >&2
    echo "         the gate would block in a real CI run. Passing for local." >&2
  fi
  python3 -c "
import os, sys
sys.path.insert(0, '.')
from core.hitl_gates import attest
from core import env as _envhelper
ok, reason = attest('$_cid', '$_env', '$_approver')
if ok:
    print(f'HITL: {reason}')
else:
    print(f'HITL BLOCK: {reason}', file=sys.stderr)
    sys.exit(1)
" || { echo "FAIL: HITL gate blocked" >&2; return 1; }
}

echo "=== Step 5: runtime policy scan on the terraform plan (Wiz-or-Checkov, never both) ==="
# REQ-250 (v1.21): after terraform plan, run Wiz against the plan when
# configured; otherwise run Checkov against the plan as a drop-in
# replacement. Wiz and Checkov are NEVER both run on the plan. The
# static-code Checkov already ran in run_codegen.sh Step 3c (fail-fast).
RUNTIME_SCAN_ENGINE=""
if [ -n "${WIZ_API_TOKEN:-}" ] || [ -n "${WIZ_API_URL:-}" ]; then
  RUNTIME_SCAN_ENGINE="wiz"
  echo "--- Wiz configured (WIZ_API_TOKEN + WIZ_API_URL) → Wiz on the plan ---"
  python3 adapters/wiz/wiz_adapter.py --plan "$TF_DIR/tfplan" --contract-id "$CONTRACT_ID" --run-id "${CONTRACT_ID}" > "$WORK/pcr.json" 2> "$WORK/wiz.err" || {
    echo "WARNING: Wiz scan failed; falling back to Checkov on the plan" >&2
    RUNTIME_SCAN_ENGINE="checkov-plan"
  }
else
  RUNTIME_SCAN_ENGINE="checkov-plan"
fi
if [ "$RUNTIME_SCAN_ENGINE" = "checkov-plan" ]; then
  echo "--- Wiz not configured → Checkov on the plan (drop-in replacement) ---"
  if [ "$QUIET" = "0" ]; then
    checkov -f "$TF_DIR/tfplan" --framework terraform_plan -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ 2>&1 | tee "$WORK/checkov-plan.json"
  else
    checkov -f "$TF_DIR/tfplan" --framework terraform_plan -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ > "$WORK/checkov-plan.json" 2> "$WORK/checkov-plan.err"
  fi
  [ -s "$WORK/checkov-plan.json" ] || { echo "FAIL: checkov (plan) produced no output" >&2; exit 1; }
  echo ""
  echo "checkov (plan) summary: $(python3 -c "import json; d=json.load(open('$WORK/checkov-plan.json')); print(len(d.get('results',{}).get('failed_checks',[])), 'failed,', len(d.get('results',{}).get('passed_checks',[])), 'passed')")"
  echo ""
  echo "=== Step 6: Checkov (plan) adapter → PolicyCheckResult (compliance details) ==="
  python3 adapters/terraform/policy/checkov_adapter.py "$WORK/checkov-plan.json" "$CONTRACT_ID" > "$WORK/pcr.json" || { echo "FAIL: checkov (plan) adapter failed" >&2; exit 1; }
fi
echo "runtime scan engine: $RUNTIME_SCAN_ENGINE"
python3 -c "
import json
pcrs = json.load(open('$WORK/pcr.json'))
print(f'PolicyCheckResult: {len(pcrs)} record(s)')
print()
for pcr in pcrs:
    sev = pcr.get('severity', 'info')
    res = pcr.get('result', 'unknown')
    rule = pcr.get('ruleId', 'unknown')
    msg = pcr.get('message', '')
    marker = 'PASS' if res == 'pass' else 'FAIL' if res == 'fail' else 'SKIP' if res == 'skipped' else res.upper()
    print(f'  [{marker}] {sev:8s} {rule:30s} {msg}')
"

echo ""
echo "=== Step 7: confidence signal compute ==="
python3 <<PY > "$WORK/signal.json" || { echo "FAIL: confidence signal failed" >&2; exit 1; }
import json
import core.confidence_signal as c
pcr = json.load(open("$WORK/pcr.json"))
inputs = {
    "policy": pcr,
    "validation": {"schema": True, "stack_resolved": True, "tf_validated": True, "tf_planned": True},
    "freshness": {"age_days": 0, "max_age_days": 7},
    "source": {"submitter": "consumer", "commit_sha": "consumer-sha", "signed": False},
    "history": {"prior_rollbacks": 0, "prior_policy_fails": 0},
    "nfrs": {"conformance": None},
}
sig = c.compute("$CONTRACT_ID", "dev", inputs)
print(json.dumps({"score": sig.score, "band": sig.band, "perInput": sig.perInput, "reasonCodes": sig.reasonCodes}, indent=2))
PY
BAND=$(python3 -c "import json; print(json.load(open('$WORK/signal.json'))['band'])")
SCORE=$(python3 -c "import json; print(round(json.load(open('$WORK/signal.json'))['score'],3))")
echo "confidence: score=$SCORE band=$BAND"
[ "$BAND" = "pass" ] || { echo "FAIL: confidence band is $BAND, expected pass for dev" >&2; exit 1; }

echo ""
echo "=== Step 7b: HITL attestation gate (qa/prod/dr only) ==="
RESOLVED_ENV=$(python3 -c "import yaml; print(yaml.safe_load(open('$CONTRACT')).get('environment','dev'))" 2>/dev/null || echo "dev")
if [ -n "$ENVIRONMENT_OVERRIDE" ]; then
  RESOLVED_ENV="$ENVIRONMENT_OVERRIDE"
fi
run_hitl_gate "$CONTRACT_ID" "$RESOLVED_ENV" "" || { echo "FAIL: HITL attestation gate blocked the promotion" >&2; exit 1; }

echo ""
echo "=== Step 8: write evidence event to DynamoDB outbox ==="
STACK_NAME=$(python3 -c "import json; print(json.load(open('$WORK/stack.json'))['stack']['name'])")
python3 <<PY > "$WORK/event.json" || { echo "FAIL: event build failed" >&2; exit 1; }
import json, datetime
sig = json.load(open("$WORK/signal.json"))
event = {
    "contractId": "$CONTRACT_ID",
    "eventType": "CONFIDENCE_COMPUTED",
    "ts": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
    "environment": "dev",
    "stack": "$STACK_NAME",
    "score": sig["score"],
    "band": sig["band"],
    "prev_event_hash": "GENESIS",
}
print(json.dumps(event, indent=2))
PY
python3 core/outbox_writer.py "$WORK/event.json" > "$WORK/outbox_item.json" || { echo "FAIL: outbox write failed" >&2; exit 1; }
echo "outbox: $(python3 -c "import json; d=json.load(open('$WORK/outbox_item.json')); print('contractId=', d['contractId'], 'hash=', d['hash'][:16]+'...')")"

echo ""
echo "=== Step 9: publish outputs to SSM + GitHub PR comment ==="
cd "$TF_DIR"
TF_OUTPUTS=$(terraform output -json 2>/dev/null || echo "{}")
cd "$ROOT"
python3 <<PY > "$WORK/outputs_step.json" 2>/dev/null || true
import json, sys
sys.path.insert(0, "$ROOT")
from core.output_publisher import publish_to_ssm, format_comment, post_github_comment
tf_raw = json.loads('''$TF_OUTPUTS''')
outputs = {k: v.get("value") if isinstance(v, dict) else v for k, v in tf_raw.items()}
ssm_results = publish_to_ssm(outputs, "dev", "$CONTRACT_ID")
comment = format_comment(outputs, "dev", "$CONTRACT_ID", ssm_results)
posted = post_github_comment(comment)
print(json.dumps({"ssm": ssm_results, "posted": posted, "comment": comment}))
PY
if [ -f "$WORK/outputs_step.json" ]; then
  echo "outputs published to SSM: $(python3 -c "import json; d=json.load(open('$WORK/outputs_step.json')); print(len([v for v in d.get('ssm',{}).values() if v]), 'parameters')" 2>/dev/null || echo "done")"
  if [ "$QUIET" = "0" ]; then
    python3 -c "import json; d=json.load(open('$WORK/outputs_step.json')); print(d.get('comment',''))" 2>/dev/null || true
  fi
fi

echo ""
# Uptime monitoring: sourced from run_uptime.sh
source "$ROOT/scripts/run_uptime.sh"

echo ""
echo "=== POST-APPLY OK ==="
echo "Checkov(static, pre-plan) → Wiz-or-Checkov(plan) → confidence ($BAND) → outbox → outputs → uptime"
