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

ACTION="${1:-setup}"
ROOT="${2:-/tmp/soria-cyber-governance-response-${USER:-student}}"
MARKER=".soria-cyber-governance-response"

fail(){ printf 'ERROR: %s\n' "$*" >&2; exit 1; }

safe_root(){
  case "$ROOT" in
    /tmp/soria-cyber-*|"$HOME"/soria-cyber-*) ;;
    *) fail "refused lab root: $ROOT" ;;
  esac
  [ "$ROOT" != "/tmp" ] || fail "refused root: /tmp"
  [ "$ROOT" != "$HOME" ] || fail "refused root: $HOME"
}

require_marker(){ [ -f "$ROOT/$MARKER" ] || fail "missing marker: $ROOT/$MARKER"; }
rows(){ awk 'END { print (NR > 0 ? NR - 1 : 0) }' "$1"; }
unique_col(){
  local file="$1" col="$2" label="$3"
  awk -F',' -v c="$col" -v label="$label" '
    NR==1 { next }
    $c=="" { print "empty " label " at line " NR > "/dev/stderr"; exit 1 }
    seen[$c]++ { print "duplicate " label ": " $c > "/dev/stderr"; exit 1 }
  ' "$file" || fail "$label validation failed"
}

setup_lab(){
  if [ -e "$ROOT" ]; then
    require_marker
    rm -rf "$ROOT/input" "$ROOT/output" "$ROOT/evidence"
  else
    mkdir -p "$ROOT"
    : > "$ROOT/$MARKER"
  fi
  mkdir -p "$ROOT/input" "$ROOT/output" "$ROOT/evidence"

  cat > "$ROOT/input/governance.csv" <<'CSV'
governance_id,domain,accountable,responsible,approved,review_cycle_days,days_since_review,exception_process,evidence
GOV-101,Cybersecurity strategy,Direction,RSSI,yes,365,120,yes,approved_strategy
GOV-102,Incident roles,Direction,Incident_lead,yes,180,200,yes,exercise_report
GOV-103,Access control policy,Direction,Identity_owner,no,365,30,yes,draft_policy
GOV-104,Awareness programme,HR,Security_awareness,yes,365,90,yes,attendance_and_exercises
GOV-105,Supplier risk policy,Purchasing,Security_owner,yes,365,400,yes,supplier_register
GOV-106,Security exceptions,Risk_committee,Asset_owner,yes,365,100,no,exception_template_missing
GOV-107,Backup governance,Direction,Continuity_owner,yes,365,60,yes,restore_evidence
GOV-108,Data governance,Direction,Data_owner,yes,365,150,yes,data_register
CSV

  cat > "$ROOT/input/suppliers.csv" <<'CSV'
supplier_id,service,criticality,data_classification,due_diligence,incident_notification_hours,exit_plan,backup_or_recovery_scope,evidence
SUP-101,HR SaaS,high,restricted,yes,24,yes,documented,contract_and_review
SUP-102,Managed administration,critical,restricted,yes,4,yes,documented,access_and_incident_clause
SUP-103,Marketing analytics,moderate,public,no,72,yes,not_applicable,partial_questionnaire
SUP-104,Backup provider,critical,restricted,yes,24,no,unknown,contract_only
SUP-105,Software component supplier,high,internal,no,0,no,not_applicable,none
SUP-106,Internet provider,critical,internal,yes,4,yes,not_applicable,sla_and_contacts
CSV

  cat > "$ROOT/input/control-baseline.csv" <<'CSV'
control_id,csf_function,control,owner,implementation,evidence_present,evidence_type,test_frequency_days,days_since_test
CTRL-GV-01,GOVERN,Roles and responsibilities,Direction,implemented,yes,approved_raci,365,120
CTRL-GV-02,GOVERN,Supplier risk review,Purchasing,partial,yes,supplier_register,365,400
CTRL-ID-01,IDENTIFY,Asset inventory,IT,implemented,yes,inventory_export,90,30
CTRL-ID-02,IDENTIFY,Risk register,Risk_owner,implemented,yes,risk_register,180,90
CTRL-PR-01,PROTECT,MFA for sensitive accounts,Identity_owner,implemented,yes,coverage_report,90,20
CTRL-PR-02,PROTECT,Separated backups,Continuity_owner,implemented,yes,restore_report,180,45
CTRL-DE-01,DETECT,Central security logging,Security_operations,implemented,yes,log_sample,90,30
CTRL-DE-02,DETECT,Alert triage procedure,Security_operations,partial,no,missing,180,70
CTRL-RS-01,RESPOND,Incident contact tree,Incident_lead,implemented,yes,contact_test,180,60
CTRL-RS-02,RESPOND,Containment authorization,Incident_lead,partial,no,missing,180,70
CTRL-RC-01,RECOVER,Restoration exercise,Continuity_owner,implemented,yes,exercise_report,180,45
CTRL-RC-02,RECOVER,Lessons learned process,Risk_owner,implemented,yes,review_minutes,365,500
CSV

  cat > "$ROOT/input/events.csv" <<'CSV'
event_id,source,observation,authorized_test,corroboration,confirmed_impact,scope,current_status
EVT-101,Vulnerability scanner,Scheduled scan traffic from owned lab,yes,change_ticket,none,lab,closed
EVT-102,Identity and mail,Unsolicited MFA approval followed by unfamiliar login and mailbox rule,no,multiple_logs,account_compromise,user_account,active
EVT-103,EDR,Suspicious process blocked on one workstation,no,single_sensor,none_confirmed,one_host,investigating
EVT-104,Backup platform,Nightly backup job failed,no,job_log,availability_risk,one_service,open
EVT-105,Web monitoring,Public homepage content changed without authorization,no,web_and_deploy_logs,integrity_loss,public_site,active
EVT-106,EDR,Known internal harmless test matched a signature,yes,test_ticket,none,test_host,closed
EVT-107,Source repository,Active cloud API key committed to a public repository,no,repository_history,secret_disclosure,cloud_tenant,active
EVT-108,Vendor advisory,Applicable vulnerability announced for an internal product,no,inventory_match,none_confirmed,product_scope,open
CSV

  cat > "$ROOT/input/response-actions.csv" <<'CSV'
action_id,case_id,action,authorized,destructive,preserves_evidence,known_channel,decision_hint
ACT-101,EVT-102,Notify incident lead using tested contact tree,yes,no,yes,yes,accept_coordination
ACT-102,EVT-105,Export relevant logs read-only and record checksums,yes,no,yes,yes,accept_preservation
ACT-103,EVT-102,Reboot the affected server before collecting evidence,no,yes,no,yes,block_destructive
ACT-104,EVT-102,Disable the affected account through an approved playbook,yes,no,yes,yes,accept_containment
ACT-105,EVT-103,Wipe the workstation immediately to be safe,no,yes,no,yes,block_destructive
ACT-106,EVT-107,Publish screenshots and identifiers in a public chat,no,no,no,no,block_disclosure
ACT-107,EVT-105,Create a UTC timeline with source and operator,yes,no,yes,yes,accept_documentation
ACT-108,EVT-108,Contact the supplier through the registered security channel,yes,no,yes,yes,accept_coordination
CSV

  cat > "$ROOT/input/final-project.csv" <<'CSV'
deliverable_id,deliverable,owner,evidence,status
FINAL-101,Asset inventory,IT,inventory_export,complete
FINAL-102,Risk register,Risk_owner,risk_register,complete
FINAL-103,Identity protection review,Identity_owner,coverage_report,complete
FINAL-104,Vulnerability treatment plan,IT,patch_plan,complete
FINAL-105,Backup and restore evidence,Continuity_owner,restore_report,complete
FINAL-106,Data classification register,Data_owner,data_register,complete
FINAL-107,Network flow matrix,Infrastructure_owner,flow_matrix,complete
FINAL-108,Supplier due diligence,Purchasing,partial_questionnaire,action_required
FINAL-109,Incident contacts and roles,Incident_lead,contact_tree,complete
FINAL-110,Full incident exercise,Direction,tabletop_only,action_required
CSV

  cat > "$ROOT/input/proof-boundary.md" <<'MD'
# Frontière de preuve

Le laboratoire utilise uniquement des données synthétiques. Il ne contacte aucun fournisseur, ne collecte aucun journal réel, ne modifie aucun compte ou système, ne contient aucun incident réel, ne produit aucune preuve forensique réelle et ne remplace ni une obligation réglementaire, ni une décision juridique, ni un exercice de crise autorisé.
MD

  (cd "$ROOT" && sha256sum input/* > evidence/sources-before.sha256)
  printf 'Cybersecurity governance and response lab ready: %s\n' "$ROOT"
}

show_status(){
  require_marker
  printf 'Governance records: %s\n' "$(rows "$ROOT/input/governance.csv")"
  printf 'Suppliers: %s\n' "$(rows "$ROOT/input/suppliers.csv")"
  printf 'Baseline controls: %s\n' "$(rows "$ROOT/input/control-baseline.csv")"
  printf 'Events: %s\n' "$(rows "$ROOT/input/events.csv")"
  printf 'Response actions: %s\n' "$(rows "$ROOT/input/response-actions.csv")"
  printf 'Final deliverables: %s\n' "$(rows "$ROOT/input/final-project.csv")"
  [ -f "$ROOT/output/governance-review.csv" ] && printf 'Outputs: generated\n' || printf 'Outputs: not generated\n'
}

run_lab(){
  require_marker
  unique_col "$ROOT/input/governance.csv" 1 "governance id"
  unique_col "$ROOT/input/suppliers.csv" 1 "supplier id"
  unique_col "$ROOT/input/control-baseline.csv" 1 "control id"
  unique_col "$ROOT/input/events.csv" 1 "event id"
  unique_col "$ROOT/input/response-actions.csv" 1 "action id"
  unique_col "$ROOT/input/final-project.csv" 1 "deliverable id"

  awk -F',' -v OFS=',' '
    NR==1 { print "governance_id,domain,decision,reasons"; next }
    {
      decision="PASS_GOVERNANCE"; reasons="APPROVED+OWNER+EVIDENCE+EXCEPTION_PROCESS"
      if($5!="yes") { decision="BLOCK_UNAPPROVED"; reasons="POLICY_NOT_APPROVED" }
      else if($8!="yes") { decision="BLOCK_NO_EXCEPTION_PROCESS"; reasons="EXCEPTIONS_NOT_GOVERNED" }
      else if($7>$6) { decision="REVIEW_OVERDUE"; reasons="REVIEW_CYCLE_EXCEEDED" }
      print $1,$2,decision,reasons
    }
  ' "$ROOT/input/governance.csv" > "$ROOT/output/governance-review.csv"

  awk -F',' -v OFS=',' '
    NR==1 { print "supplier_id,service,decision,reasons"; next }
    {
      decision="PASS_SUPPLIER_REVIEW"; reasons="DUE_DILIGENCE+INCIDENT_CLAUSE+EXIT+EVIDENCE"
      if($3=="critical" && ($7!="yes" || $8=="unknown")) { decision="BLOCK_EXIT_AND_RECOVERY"; reasons="CRITICAL_SUPPLIER_WITHOUT_EXIT_OR_RECOVERY_SCOPE" }
      else if($3=="high" && ($5!="yes" || $9=="none")) { decision="BLOCK_SUPPLIER_ASSURANCE"; reasons="HIGH_RISK_SUPPLIER_WITHOUT_DUE_DILIGENCE" }
      else if($5!="yes") { decision="IMPROVE_DUE_DILIGENCE"; reasons="ASSESSMENT_INCOMPLETE" }
      print $1,$2,decision,reasons
    }
  ' "$ROOT/input/suppliers.csv" > "$ROOT/output/supplier-review.csv"

  awk -F',' -v OFS=',' '
    NR==1 { print "control_id,csf_function,control,decision,reasons"; next }
    {
      decision="PASS_EVIDENCE"; reasons="IMPLEMENTED+EVIDENCE+TEST_CURRENT"
      if($6!="yes") { decision="BLOCK_MISSING_EVIDENCE"; reasons="CONTROL_CLAIM_WITHOUT_EVIDENCE" }
      else if($9>$8) { decision="RETEST_OVERDUE"; reasons="TEST_FREQUENCY_EXCEEDED" }
      else if($5=="partial") { decision="IMPROVE_IMPLEMENTATION"; reasons="PARTIAL_IMPLEMENTATION" }
      print $1,$2,$3,decision,reasons
    }
  ' "$ROOT/input/control-baseline.csv" > "$ROOT/output/baseline-evidence.csv"

  awk -F',' -v OFS=',' '
    NR==1 { print "event_id,classification,priority,reasons"; next }
    {
      classification="EVENT"; priority="NORMAL"; reasons="OBSERVED_OCCURRENCE_WITHOUT_CONFIRMED_SECURITY_IMPACT"
      if($1=="EVT-102") { classification="INCIDENT"; priority="CRITICAL"; reasons="CONFIRMED_ACCOUNT_COMPROMISE+ACTIVE" }
      else if($1=="EVT-103") { classification="ALERT"; priority="HIGH"; reasons="SENSOR_SIGNAL_REQUIRES_CORROBORATION" }
      else if($1=="EVT-105") { classification="INCIDENT"; priority="HIGH"; reasons="CONFIRMED_UNAUTHORIZED_INTEGRITY_LOSS" }
      else if($1=="EVT-106") { classification="FALSE_POSITIVE"; priority="CLOSED"; reasons="AUTHORIZED_TEST+NO_IMPACT" }
      else if($1=="EVT-107") { classification="INCIDENT"; priority="CRITICAL"; reasons="CONFIRMED_ACTIVE_SECRET_DISCLOSURE" }
      else if($1=="EVT-108") { classification="VULNERABILITY_CASE"; priority="HIGH"; reasons="APPLICABLE_ADVISORY_WITHOUT_CONFIRMED_EXPLOITATION" }
      print $1,classification,priority,reasons
    }
  ' "$ROOT/input/events.csv" > "$ROOT/output/incident-qualification.csv"

  awk -F',' -v OFS=',' '
    NR==1 { print "action_id,case_id,decision,reasons"; next }
    {
      decision="ACCEPT"; reasons="AUTHORIZED+BOUNDED+PRESERVES_EVIDENCE+KNOWN_CHANNEL"
      if($8=="block_destructive") { decision="BLOCK_DESTRUCTIVE"; reasons="UNAUTHORIZED_DESTRUCTIVE_ACTION+EVIDENCE_LOSS" }
      else if($8=="block_disclosure") { decision="BLOCK_UNCONTROLLED_DISCLOSURE"; reasons="SENSITIVE_INFORMATION_SHARED_OUTSIDE_CONTROLLED_CHANNEL" }
      else if($8=="accept_coordination") { decision="ACCEPT_COORDINATION"; reasons="KNOWN_CHANNEL+DEFINED_ROLE" }
      else if($8=="accept_preservation") { decision="ACCEPT_PRESERVATION"; reasons="READ_ONLY_EXPORT+CHECKSUM" }
      else if($8=="accept_containment") { decision="ACCEPT_AUTHORIZED_CONTAINMENT"; reasons="APPROVED_PLAYBOOK+ACCOUNT_SCOPE" }
      else if($8=="accept_documentation") { decision="ACCEPT_DOCUMENTATION"; reasons="UTC_TIMELINE+SOURCE+OPERATOR" }
      print $1,$2,decision,reasons
    }
  ' "$ROOT/input/response-actions.csv" > "$ROOT/output/response-decisions.csv"

  awk -F',' -v OFS=',' '
    NR==1 { print "deliverable_id,deliverable,decision,reasons"; next }
    {
      decision="PASS_DELIVERABLE"; reasons="OWNER+EVIDENCE+COMPLETE"
      if($5=="action_required") { decision="ACTION_REQUIRED"; reasons="EVIDENCE_OR_REAL_EXERCISE_INCOMPLETE" }
      print $1,$2,decision,reasons
    }
  ' "$ROOT/input/final-project.csv" > "$ROOT/output/final-readiness.csv"

  governance_blockers=$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/governance-review.csv")
  supplier_blockers=$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/supplier-review.csv")
  evidence_blockers=$(awk -F',' 'NR>1&&$4=="BLOCK_MISSING_EVIDENCE"{c++}END{print c+0}' "$ROOT/output/baseline-evidence.csv")
  incidents=$(awk -F',' 'NR>1&&$2=="INCIDENT"{c++}END{print c+0}' "$ROOT/output/incident-qualification.csv")
  action_blockers=$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/response-decisions.csv")
  final_actions=$(awk -F',' 'NR>1&&$3=="ACTION_REQUIRED"{c++}END{print c+0}' "$ROOT/output/final-readiness.csv")
  overall="READY"
  [ "$governance_blockers" -eq 0 ] && [ "$supplier_blockers" -eq 0 ] && [ "$evidence_blockers" -eq 0 ] && [ "$final_actions" -eq 0 ] || overall="READY_WITH_ACTIONS"

  {
    printf '# Synthèse lot 5\n\n'
    printf -- '- blockers gouvernance : %s\n' "$governance_blockers"
    printf -- '- blockers fournisseurs : %s\n' "$supplier_blockers"
    printf -- '- preuves de baseline manquantes : %s\n' "$evidence_blockers"
    printf -- '- incidents qualifiés : %s\n' "$incidents"
    printf -- '- actions de réponse bloquées : %s\n' "$action_blockers"
    printf -- '- livrables finaux avec actions : %s\n' "$final_actions"
    printf -- '- décision finale : %s\n\n' "$overall"
    cat "$ROOT/input/proof-boundary.md"
  } > "$ROOT/evidence/summary.md"

  (cd "$ROOT" && sha256sum input/* > evidence/sources-after.sha256 && sha256sum output/*.csv evidence/summary.md > evidence/outputs.sha256)
  diff -u "$ROOT/evidence/sources-before.sha256" "$ROOT/evidence/sources-after.sha256" > "$ROOT/evidence/source.diff" || fail "source fixtures changed"
  printf 'Governance, incident response and final project review completed.\n'
}

validate_lab(){
  require_marker
  for f in \
    output/governance-review.csv \
    output/supplier-review.csv \
    output/baseline-evidence.csv \
    output/incident-qualification.csv \
    output/response-decisions.csv \
    output/final-readiness.csv \
    evidence/summary.md \
    evidence/outputs.sha256 \
    evidence/source.diff
  do
    [ -f "$ROOT/$f" ] || fail "missing output: $f"
  done

  [ "$(rows "$ROOT/input/governance.csv")" -eq 8 ] || fail "expected 8 governance records"
  [ "$(rows "$ROOT/input/suppliers.csv")" -eq 6 ] || fail "expected 6 suppliers"
  [ "$(rows "$ROOT/input/control-baseline.csv")" -eq 12 ] || fail "expected 12 baseline controls"
  [ "$(rows "$ROOT/input/events.csv")" -eq 8 ] || fail "expected 8 events"
  [ "$(rows "$ROOT/input/response-actions.csv")" -eq 8 ] || fail "expected 8 response actions"
  [ "$(rows "$ROOT/input/final-project.csv")" -eq 10 ] || fail "expected 10 final deliverables"

  grep -Fq 'GOV-103,Access control policy,BLOCK_UNAPPROVED' "$ROOT/output/governance-review.csv" || fail "unapproved policy blocker missing"
  grep -Fq 'GOV-106,Security exceptions,BLOCK_NO_EXCEPTION_PROCESS' "$ROOT/output/governance-review.csv" || fail "exception blocker missing"
  [ "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/governance-review.csv")" -eq 2 ] || fail "expected 2 governance blockers"

  grep -Fq 'SUP-104,Backup provider,BLOCK_EXIT_AND_RECOVERY' "$ROOT/output/supplier-review.csv" || fail "critical supplier blocker missing"
  grep -Fq 'SUP-105,Software component supplier,BLOCK_SUPPLIER_ASSURANCE' "$ROOT/output/supplier-review.csv" || fail "supplier assurance blocker missing"
  [ "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/supplier-review.csv")" -eq 2 ] || fail "expected 2 supplier blockers"

  grep -Fq 'CTRL-DE-02,DETECT,Alert triage procedure,BLOCK_MISSING_EVIDENCE' "$ROOT/output/baseline-evidence.csv" || fail "detect evidence blocker missing"
  grep -Fq 'CTRL-RS-02,RESPOND,Containment authorization,BLOCK_MISSING_EVIDENCE' "$ROOT/output/baseline-evidence.csv" || fail "respond evidence blocker missing"
  [ "$(awk -F',' 'NR>1&&$4=="BLOCK_MISSING_EVIDENCE"{c++}END{print c+0}' "$ROOT/output/baseline-evidence.csv")" -eq 2 ] || fail "expected 2 missing evidence blockers"

  grep -Fq 'EVT-102,INCIDENT,CRITICAL' "$ROOT/output/incident-qualification.csv" || fail "account incident missing"
  grep -Fq 'EVT-103,ALERT,HIGH' "$ROOT/output/incident-qualification.csv" || fail "alert classification missing"
  grep -Fq 'EVT-106,FALSE_POSITIVE,CLOSED' "$ROOT/output/incident-qualification.csv" || fail "false positive classification missing"
  grep -Fq 'EVT-108,VULNERABILITY_CASE,HIGH' "$ROOT/output/incident-qualification.csv" || fail "vulnerability case missing"
  [ "$(awk -F',' 'NR>1&&$2=="INCIDENT"{c++}END{print c+0}' "$ROOT/output/incident-qualification.csv")" -eq 3 ] || fail "expected 3 incidents"

  grep -Fq 'ACT-103,EVT-102,BLOCK_DESTRUCTIVE' "$ROOT/output/response-decisions.csv" || fail "reboot blocker missing"
  grep -Fq 'ACT-105,EVT-103,BLOCK_DESTRUCTIVE' "$ROOT/output/response-decisions.csv" || fail "wipe blocker missing"
  grep -Fq 'ACT-106,EVT-107,BLOCK_UNCONTROLLED_DISCLOSURE' "$ROOT/output/response-decisions.csv" || fail "disclosure blocker missing"
  [ "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/response-decisions.csv")" -eq 3 ] || fail "expected 3 response blockers"

  grep -Fq 'FINAL-108,Supplier due diligence,ACTION_REQUIRED' "$ROOT/output/final-readiness.csv" || fail "supplier final action missing"
  grep -Fq 'FINAL-110,Full incident exercise,ACTION_REQUIRED' "$ROOT/output/final-readiness.csv" || fail "exercise final action missing"
  [ "$(awk -F',' 'NR>1&&$3=="ACTION_REQUIRED"{c++}END{print c+0}' "$ROOT/output/final-readiness.csv")" -eq 2 ] || fail "expected 2 final actions"
  grep -Fq -- '- décision finale : READY_WITH_ACTIONS' "$ROOT/evidence/summary.md" || fail "final readiness decision missing"

  [ ! -s "$ROOT/evidence/source.diff" ] || fail "source diff not empty"
  (cd "$ROOT" && sha256sum -c evidence/sources-after.sha256 >/dev/null && sha256sum -c evidence/outputs.sha256 >/dev/null) || fail "checksum validation failed"
  printf 'Validation successful: governance, suppliers, evidence, incidents, actions and final readiness are correct.\n'
}

reset_lab(){
  require_marker
  rm -rf "$ROOT"
  printf 'Cybersecurity governance and response lab removed: %s\n' "$ROOT"
}

safe_root
case "$ACTION" in
  setup) setup_lab ;;
  status) show_status ;;
  run) run_lab ;;
  validate) validate_lab ;;
  reset) reset_lab ;;
  *) fail "unknown action: $ACTION" ;;
esac
