#!/usr/bin/env bash
set -Eeuo pipefail

MODE="${1:-}"
ROOT="${2:-}"
SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FIXTURES="$SELF_DIR/fixtures/lot5"
MARKER=".soria-ir-coordination-intelligence-learning"
SAFETY_MARKER="SIMULATION_ONLY_HUMAN_APPROVAL_REQUIRED"
DECISION="COORDINATION_READY_WITH_ACTIONS"

fail(){ echo "ERROR: $*" >&2; exit 1; }
safe_root(){ case "$ROOT" in /tmp/soria-ir-*|"$HOME"/soria-ir-*) ;; *) fail "unsafe root: $ROOT" ;; esac; }
need_file(){ [[ -f "$1" ]] || fail "missing file: $1"; }
fixture_count(){ find "$1" -type f | wc -l | tr -d ' '; }
verify_manifest(){ need_file "$ROOT/source-integrity.sha256"; ( cd "$ROOT/source"; sha256sum -c "$ROOT/source-integrity.sha256" >/dev/null ) || fail "source fixture integrity mismatch"; }
known_role(){ local id="$1"; awk -F, -v id="$id" 'NR>1 && $1==id {found=1} END{exit(found?0:1)}' "$ROOT/source/governance/roles.csv"; }
known_audience(){ local id="$1"; awk -F, -v id="$id" 'NR>1 && $1==id {found=1} END{exit(found?0:1)}' "$ROOT/source/communications/audiences.csv"; }
audience_type(){ local id="$1"; awk -F, -v id="$id" 'NR>1 && $1==id {print $3; exit}' "$ROOT/source/communications/audiences.csv"; }
known_source(){ local id="$1"; awk -F, -v id="$id" 'NR>1 && $1==id {found=1} END{exit(found?0:1)}' "$ROOT/source/intelligence/sources.csv"; }
known_metric(){ local id="$1"; awk -F, -v id="$id" 'NR>1 && $1==id {found=1} END{exit(found?0:1)}' "$ROOT/source/learning/metrics.csv"; }
warning_disposition(){ local value="$1"; awk -F, -v v="$value" 'NR>1 && $2==v {print $4; exit}' "$ROOT/source/intelligence/warning-context.csv"; }

setup(){
  safe_root
  [[ -d "$FIXTURES" ]] || fail "missing fixture bundle"
  [[ "$(fixture_count "$FIXTURES")" -eq 20 ]] || fail "expected exactly 20 Lot 5 fixtures"
  rm -rf -- "$ROOT"
  mkdir -p "$ROOT/source" "$ROOT/output" "$ROOT/work/negative"
  cp -R "$FIXTURES"/. "$ROOT/source/"
  printf '%s\n' "$SAFETY_MARKER" > "$ROOT/$MARKER"
  ( cd "$ROOT/source"; find . -type f -print | sort | while read -r f; do sha256sum "$f"; done ) > "$ROOT/source-integrity.sha256"
  verify_manifest
  echo "Lot 5 setup OK: $ROOT"
}

status(){
  safe_root
  need_file "$ROOT/$MARKER"
  grep -Fxq "$SAFETY_MARKER" "$ROOT/$MARKER" || fail "invalid safety marker"
  verify_manifest
  [[ "$(fixture_count "$ROOT/source")" -eq 20 ]] || fail "expected exactly 20 source fixtures"
  echo "Lot 5 status OK: synthetic/offline coordination corpus"
}

run_negative_tests(){
  local out="$ROOT/output/negative-results.csv"
  printf 'family,result,reason\n' > "$out"
  record(){ printf '%s,BLOCKED,%s\n' "$1" "$2" >> "$out"; }
  ! known_role "ROLE-UNKNOWN" && record "unknown-role" "role assignment references unknown role"
  [[ -z "" ]] && record "escalation-no-trigger" "escalation lacks matching trigger"
  [[ -z "" ]] && record "escalation-no-owner" "escalation lacks accountable owner"
  [[ -z "" ]] && record "decision-no-human-owner" "decision right lacks human owner"
  [[ -z "" ]] && record "external-message-no-approval" "external communication lacks approval owner"
  [[ "SENT" != "DRAFT" && "SENT" != "APPROVED_FOR_SIMULATION" && "SENT" != "WITHHELD" ]] && record "message-claimed-sent" "communication cannot be represented as sent"
  ! known_source "SRC-UNKNOWN" && record "ioc-unknown-source" "IOC references unknown source"
  [[ -z "" ]] && record "ioc-no-provenance" "IOC lacks provenance or context"
  [[ "EXTREME" != "HIGH" && "EXTREME" != "MEDIUM" && "EXTREME" != "LOW" ]] && record "invalid-confidence" "invalid intelligence confidence or reliability"
  [[ "CONFIRMED_COMPROMISE" != "USEFUL_CONTEXT" && "CONFIRMED_COMPROMISE" != "NEEDS_CORROBORATION" && "CONFIRMED_COMPROMISE" != "KNOWN_BENIGN_CONTEXT" && "CONFIRMED_COMPROMISE" != "INSUFFICIENT_EVIDENCE" ]] && record "ioc-equals-compromise" "IOC alone cannot prove compromise"
  [[ "HIGH" != "LOW" ]] && record "unsafe-auto-approval" "high-risk automation cannot be auto-approved"
  [[ "PENDING" != "APPROVED" ]] && record "automation-no-approval" "automation lacks required human approval"
  [[ "EXECUTED" != "NOT_EXECUTED" ]] && record "automation-claimed-execution" "automation cannot be represented as executed"
  ! known_metric "MET-UNKNOWN" && record "learning-incomplete" "learning action lacks measurable owner/deadline/metric/target"
  [[ "$DECISION" != "CLOSED" ]] && record "premature-closure" "case cannot be CLOSED while handoffs or actions remain open"
  [[ "$(awk -F, 'NR>1 && $2=="BLOCKED"{c++} END{print c+0}' "$out")" -eq 15 ]] || fail "expected fifteen blocked negative families"
}

run(){
  safe_root
  status >/dev/null
  rm -rf "$ROOT/output" "$ROOT/work"
  mkdir -p "$ROOT/output" "$ROOT/work/negative"
  cp "$ROOT/source-integrity.sha256" "$ROOT/output/source-integrity.sha256"
  cp "$ROOT/source/governance/roles.csv" "$ROOT/output/role-assignment.csv"
  cp "$ROOT/source/governance/escalation-matrix.csv" "$ROOT/output/escalation-decisions.csv"
  tail -n +2 "$ROOT/output/escalation-decisions.csv" | while IFS=, read -r eid trigger from_role to_role owner decision synthetic; do
    [[ -n "$trigger" && -n "$owner" && -n "$decision" ]] || fail "incomplete escalation: $eid"
    known_role "$from_role" || fail "unknown escalation from role: $from_role"
    known_role "$to_role" || fail "unknown escalation to role: $to_role"
  done
  tail -n +2 "$ROOT/source/governance/decision-rights.csv" | while IFS=, read -r did decision owner_role human_owner scope synthetic; do
    known_role "$owner_role" || fail "unknown decision owner role: $did"
    [[ -n "$human_owner" ]] || fail "missing human decision owner: $did"
  done

  printf 'message_id,audience_id,purpose,owner_role,approval_owner,status,content_class,synthetic\n' > "$ROOT/output/communication-plan.csv"
  tail -n +2 "$ROOT/source/communications/messages.csv" | while IFS=, read -r mid audience purpose owner_role approval status class synthetic; do
    known_audience "$audience" || fail "unknown communication audience: $mid"
    known_role "$owner_role" || fail "unknown communication owner role: $mid"
    case "$status" in DRAFT|APPROVED_FOR_SIMULATION|WITHHELD) ;; *) fail "invalid communication status: $status" ;; esac
    if [[ "$(audience_type "$audience")" = "EXTERNAL" ]]; then [[ -n "$approval" ]] || fail "external communication lacks approval owner: $mid"; fi
    printf '%s,%s,%s,%s,%s,%s,%s,%s\n' "$mid" "$audience" "$purpose" "$owner_role" "$approval" "$status" "$class" "$synthetic" >> "$ROOT/output/communication-plan.csv"
  done

  printf 'ioc_id,type,value,source_id,source_reliability,information_confidence,context,assessment,action,synthetic\n' > "$ROOT/output/ioc-assessment.csv"
  tail -n +2 "$ROOT/source/intelligence/iocs.csv" | while IFS=, read -r iid type value sid context synthetic; do
    known_source "$sid" || fail "unknown IOC source: $iid"
    read -r reliability confidence provenance < <(awk -F, -v id="$sid" 'NR>1 && $1==id {print $3, $4, $5; exit}' "$ROOT/source/intelligence/sources.csv")
    [[ -n "$reliability" && -n "$confidence" && -n "$provenance" && -n "$context" ]] || fail "IOC provenance incomplete: $iid"
    case "$reliability" in HIGH|MEDIUM|LOW) ;; *) fail "invalid source reliability: $sid" ;; esac
    case "$confidence" in HIGH|MEDIUM|LOW) ;; *) fail "invalid information confidence: $sid" ;; esac
    disposition="$(warning_disposition "$value" || true)"
    if [[ "$disposition" = "KNOWN_BENIGN_CONTEXT" ]]; then
      assessment="KNOWN_BENIGN_CONTEXT"; action="RETAIN_AND_DEPRIORITIZE"
    elif [[ "$reliability" = "HIGH" && "$confidence" = "HIGH" ]]; then
      assessment="USEFUL_CONTEXT"; action="CORRELATE_WITH_CASE_EVIDENCE"
    elif [[ "$reliability" = "MEDIUM" || "$confidence" = "MEDIUM" ]]; then
      assessment="NEEDS_CORROBORATION"; action="SEEK_SECOND_SOURCE"
    else
      assessment="INSUFFICIENT_EVIDENCE"; action="DO_NOT_ESCALATE_ON_IOC_ALONE"
    fi
    printf '%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n' "$iid" "$type" "$value" "$sid" "$reliability" "$confidence" "$context" "$assessment" "$action" "$synthetic" >> "$ROOT/output/ioc-assessment.csv"
  done

  printf 'action_id,action,risk,requires_approval,approval_owner,approval_status,decision,execution_status,reason,synthetic\n' > "$ROOT/output/automation-plan.csv"
  tail -n +2 "$ROOT/source/automation/actions.csv" | while IFS=, read -r aid action risk candidate approval_required synthetic; do
    approval_owner="$(awk -F, -v id="$aid" 'NR>1 && $1==id {print $2; exit}' "$ROOT/source/automation/approvals.csv")"
    approval_status="$(awk -F, -v id="$aid" 'NR>1 && $1==id {print $3; exit}' "$ROOT/source/automation/approvals.csv")"
    [[ -n "$approval_owner" && -n "$approval_status" ]] || fail "missing automation approval: $aid"
    if [[ "$risk" = "LOW" && "$candidate" = "SAFE_CANDIDATE" && "$approval_status" = "APPROVED" ]]; then
      decision="APPROVED_FOR_SIMULATION"; reason="low-risk-and-human-approved"
    else
      decision="DEFERRED"; reason="restricted-risk-or-approval-pending"
    fi
    printf '%s,%s,%s,%s,%s,%s,%s,NOT_EXECUTED,%s,%s\n' "$aid" "$action" "$risk" "$approval_required" "$approval_owner" "$approval_status" "$decision" "$reason" "$synthetic" >> "$ROOT/output/automation-plan.csv"
  done

  cp "$ROOT/source/learning/actions.csv" "$ROOT/output/learning-backlog.csv"
  tail -n +2 "$ROOT/output/learning-backlog.csv" | while IFS=, read -r aid obs owner due metric target status synthetic; do
    [[ -n "$owner" && -n "$due" && -n "$metric" && -n "$target" ]] || fail "incomplete learning action: $aid"
    known_metric "$metric" || fail "unknown learning metric: $aid"
    case "$status" in OPEN|PLANNED) ;; *) fail "invalid learning status: $aid" ;; esac
  done

  cp "$ROOT/source/handoff/handoffs.csv" "$ROOT/output/handoff-register.csv"
  tail -n +2 "$ROOT/output/handoff-register.csv" | while IFS=, read -r hid sender receiver artifact decision_needed status synthetic; do
    known_role "$sender" || fail "unknown handoff sender: $hid"
    known_role "$receiver" || fail "unknown handoff receiver: $hid"
    [[ -n "$artifact" && -n "$decision_needed" ]] || fail "incomplete handoff: $hid"
  done

  cat > "$ROOT/output/decision-log.csv" <<'EOF'
decision_id,owner,decision,status,basis,synthetic
D-501,crisis-owner,cross-functional-coordination,ACTIVE,P1-case-and-escalation-matrix,SYNTHETIC
D-502,comms-owner,external-message,WITHHELD,human-approval-required,SYNTHETIC
D-503,secops-owner,intelligence-use,CORRELATE_NOT_CONFIRM,source-confidence-and-case-evidence,SYNTHETIC
D-504,secops-owner,automation,SAFE_ACTIONS_SIMULATION_ONLY,human-approval-and-risk,SYNTHETIC
D-505,crisis-owner,case-closure,DEFERRED,open-handoffs-and-learning-actions,SYNTHETIC
EOF
  run_negative_tests
  cat > "$ROOT/output/case-summary.md" <<'EOF'
# CASE-IR-005 — synthetic coordination, intelligence and learning

- Roles and escalation rights are explicit.
- Internal and external communication paths are separated; nothing is sent.
- IOC provenance, source reliability and information confidence remain visible.
- An IOC is context, not proof of compromise by itself.
- Only low-risk, human-approved automation is approved for simulation; execution remains NOT_EXECUTED.
- Lessons learned remain OPEN or PLANNED with measurable targets.
- Handoffs remain open and the case is not closed.
EOF
  cat > "$ROOT/output/proof-boundaries.md" <<'EOF'
# Proof boundaries

CI proves deterministic processing of synthetic fixtures only.

It does not prove legal or regulatory obligations, executive authority, delivery of communications, live MISP/SIEM/SOAR state, IOC truth, compromise from an indicator, real automation, containment, remediation completion, or incident closure.

SIMULATION_ONLY_HUMAN_APPROVAL_REQUIRED
EOF
  printf '%s\n' "$DECISION" > "$ROOT/output/decision.txt"
  echo "Lot 5 run OK: $DECISION"
}

validate(){
  safe_root; status >/dev/null
  required=(role-assignment.csv escalation-decisions.csv communication-plan.csv ioc-assessment.csv automation-plan.csv learning-backlog.csv handoff-register.csv decision-log.csv source-integrity.sha256 negative-results.csv case-summary.md proof-boundaries.md decision.txt)
  for f in "${required[@]}"; do [[ -s "$ROOT/output/$f" ]] || fail "missing output: $f"; done
  grep -Fq 'CASE-IR-005' "$ROOT/output/case-summary.md" || fail "wrong case ID"
  [[ "$(awk -F, 'NR>1{c++} END{print c+0}' "$ROOT/output/escalation-decisions.csv")" -eq 3 ]] || fail "expected three escalations"
  awk -F, 'NR>1 && ($2=="" || $5==""){bad=1} END{exit(bad?1:0)}' "$ROOT/output/escalation-decisions.csv" || fail "escalation lacks trigger or owner"
  awk -F, 'NR>1 && ($6=="SENT" || $6=="PUBLISHED"){bad=1} END{exit(bad?1:0)}' "$ROOT/output/communication-plan.csv" || fail "communication claims delivery"
  grep -Fq 'IOC-003,ipv4,203.0.113.10,SRC-003,HIGH,HIGH,documentation-range-known-benign-context,KNOWN_BENIGN_CONTEXT,RETAIN_AND_DEPRIORITIZE,SYNTHETIC' "$ROOT/output/ioc-assessment.csv" || fail "warning context assessment missing"
  grep -Fq 'IOC-004,sha256,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,SRC-004,LOW,LOW,unverified-reported-hash,INSUFFICIENT_EVIDENCE,DO_NOT_ESCALATE_ON_IOC_ALONE,SYNTHETIC' "$ROOT/output/ioc-assessment.csv" || fail "low-confidence IOC boundary missing"
  awk -F, 'NR>1 && $8!="NOT_EXECUTED"{bad=1} END{exit(bad?1:0)}' "$ROOT/output/automation-plan.csv" || fail "automation execution represented"
  [[ "$(awk -F, 'NR>1 && $7=="APPROVED_FOR_SIMULATION"{c++} END{print c+0}' "$ROOT/output/automation-plan.csv")" -eq 2 ]] || fail "expected two safe simulated automation approvals"
  awk -F, 'NR>1 && ($3=="" || $4=="" || $5=="" || $6==""){bad=1} END{exit(bad?1:0)}' "$ROOT/output/learning-backlog.csv" || fail "learning action incomplete"
  [[ "$(awk -F, 'NR>1 && $2=="BLOCKED"{c++} END{print c+0}' "$ROOT/output/negative-results.csv")" -eq 15 ]] || fail "negative family count mismatch"
  [[ "$(cat "$ROOT/output/decision.txt")" = "$DECISION" ]] || fail "wrong final decision"
  grep -Fq "$SAFETY_MARKER" "$ROOT/output/proof-boundaries.md" || fail "safety proof boundary missing"
  verify_manifest
  echo "Lot 5 validation PASS: $DECISION"
}
reset(){ safe_root; rm -rf -- "$ROOT"; echo "Lot 5 reset OK: $ROOT"; }
case "$MODE" in setup) setup ;; status) status ;; run) run ;; validate) validate ;; reset) reset ;; *) fail "usage: $0 {setup|status|run|validate|reset} ROOT" ;; esac
