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

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

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/data-assets.csv" <<'CSV'
data_id,name,owner,classification,confidentiality,integrity,availability,location,retention_days
DATA-101,Site public,Communication,public,low,moderate,moderate,cdn,365
DATA-102,Annuaire interne,IT,internal,moderate,high,high,identity_service,365
DATA-103,Dossiers salariés,RH,restricted,high,high,moderate,saas_hr,1825
DATA-104,Base clients,Ventes,confidential,high,high,high,erp_database,2555
DATA-105,Clés de sauvegarde,Continuité,restricted,high,high,high,key_vault,3650
DATA-106,Logs techniques,IT,internal,moderate,high,moderate,log_platform,180
DATA-107,Secrets CI/CD,DevOps,restricted,high,high,high,secret_manager,90
DATA-108,Documentation réseau,Infrastructure,confidential,high,high,moderate,document_store,730
CSV

  cat > "$ROOT/input/crypto-services.csv" <<'CSV'
crypto_id,service,protection_scope,mechanism,key_or_cert_owner,key_store,rotation_days,hostname_validation,revocation_process
CRYPTO-101,ERP database,at_rest,AES-256-GCM,IT,key_vault,365,not_applicable,documented
CRYPTO-102,Public website,in_transit,TLS-1.3,Web,managed_ca,90,required,documented
CRYPTO-103,Legacy admin portal,in_transit,TLS-1.0,IT,local_file,1825,disabled,unknown
CRYPTO-104,Backup repository,at_rest,AES-256,Continuité,same_server,0,not_applicable,unknown
CRYPTO-105,Site-to-site tunnel,in_transit,IPsec-IKEv2,Infrastructure,hsm,180,peer_identity,documented
CRYPTO-106,Internal API,in_transit,TLS-1.3,DevOps,secret_manager,90,required,documented
CSV

  cat > "$ROOT/input/network-flows.csv" <<'CSV'
flow_id,source_zone,destination_zone,service,port,business_need,identity_condition,logging,declared_action
FLOW-101,internet,reverse_proxy,https,443,public_site,none,yes,allow
FLOW-102,user_vlan,erp_app,https,443,orders,employee_mfa,yes,allow
FLOW-103,user_vlan,erp_database,postgres,5432,direct_query,none,no,allow
FLOW-104,admin_vlan,identity_service,ldaps,636,identity_admin,admin_mfa,yes,allow
FLOW-105,guest_wifi,internal_dns,dns,53,name_resolution,none,yes,allow
FLOW-106,backup_zone,erp_database,postgres,5432,backup_service,service_identity,yes,allow
FLOW-107,internet,admin_portal,https,443,remote_admin,admin_mfa,yes,allow
FLOW-108,monitoring_zone,servers,node_exporter,9100,metrics,service_identity,yes,allow
CSV

  cat > "$ROOT/input/access-channels.csv" <<'CSV'
channel_id,channel,scenario,authentication,encryption,segmentation,device_posture,decision_hint
ACCESS-101,corporate_wifi,managed_laptop,802.1X_EAP_TLS,WPA3_Enterprise,corporate_vlan,managed,allow
ACCESS-102,guest_wifi,visitor_device,captive_portal,WPA2_Personal,guest_only,unknown,allow_limited
ACCESS-103,public_wifi,finance_operation,open,none,public_network,unknown,block_sensitive
ACCESS-104,remote_vpn,admin_access,certificate_plus_mfa,WireGuard_or_IPsec,admin_zone,managed,allow
ACCESS-105,remote_vpn,personal_device,password_only,legacy_ssl_vpn,full_network,unmanaged,block
ACCESS-106,corporate_wifi,unknown_ssid,password_prompt,unknown,unknown,unknown,block_rogue
CSV

  cat > "$ROOT/input/cloud-controls.csv" <<'CSV'
control_id,service_model,control,provider_responsibility,customer_responsibility,evidence,declared_owner
CLOUD-101,IaaS,physical_datacenter,yes,no,provider_attestation,provider
CLOUD-102,IaaS,guest_os_patching,no,yes,patch_report,customer
CLOUD-103,PaaS,managed_runtime_patch,yes,shared,provider_notice_plus_inventory,shared
CLOUD-104,SaaS,user_access_reviews,no,yes,access_review,customer
CLOUD-105,SaaS,application_code_patch,yes,no,provider_release_note,provider
CLOUD-106,IaaS,security_group_rules,no,yes,configuration_export,customer
CLOUD-107,PaaS,data_classification,no,yes,data_register,customer
CLOUD-108,SaaS,tenant_backup_scope,shared,shared,contract_plus_restore_test,shared
CSV

  cat > "$ROOT/input/secrets.csv" <<'CSV'
secret_id,use,storage,repository_tracked,rotation_days,scope,decision_hint
SECRET-101,CI_registry,secret_manager,no,90,project,pass
SECRET-102,Database_password,.env_untracked,no,60,service,improve
SECRET-103,Cloud_API_key,source_code,yes,0,tenant,block_exposed
SECRET-104,TLS_private_key,managed_key_store,no,90,service,pass
SECRET-105,Shared_admin_password,wiki,no,0,global,block_shared
SECRET-106,Backup_encryption_key,key_vault,no,365,backup_domain,pass
CSV

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

Le laboratoire utilise uniquement des données synthétiques. Il ne chiffre aucun fichier réel, ne génère aucune clé, ne contacte aucun serveur TLS, ne modifie aucun pare-feu, ne rejoint aucun réseau Wi-Fi, n'établit aucun VPN, ne contacte aucun fournisseur cloud et ne manipule aucun secret réel.
MD

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

show_status(){
  require_marker
  printf 'Data assets: %s\n' "$(rows "$ROOT/input/data-assets.csv")"
  printf 'Crypto services: %s\n' "$(rows "$ROOT/input/crypto-services.csv")"
  printf 'Network flows: %s\n' "$(rows "$ROOT/input/network-flows.csv")"
  printf 'Access channels: %s\n' "$(rows "$ROOT/input/access-channels.csv")"
  printf 'Cloud controls: %s\n' "$(rows "$ROOT/input/cloud-controls.csv")"
  printf 'Secrets: %s\n' "$(rows "$ROOT/input/secrets.csv")"
  [ -f "$ROOT/output/data-protection.csv" ] && printf 'Outputs: generated\n' || printf 'Outputs: not generated\n'
}

run_lab(){
  require_marker
  unique_col "$ROOT/input/data-assets.csv" 1 "data id"
  unique_col "$ROOT/input/crypto-services.csv" 1 "crypto id"
  unique_col "$ROOT/input/network-flows.csv" 1 "flow id"
  unique_col "$ROOT/input/access-channels.csv" 1 "channel id"
  unique_col "$ROOT/input/cloud-controls.csv" 1 "control id"
  unique_col "$ROOT/input/secrets.csv" 1 "secret id"

  awk -F',' -v OFS=',' '
    NR==1{print "data_id,name,classification,protection_profile,reasons";next}
    {
      profile="PUBLIC_BASELINE"; reasons="PUBLIC_CONTENT_WITH_INTEGRITY_AND_AVAILABILITY"
      if($4=="restricted") {profile="RESTRICTED_STRONG"; reasons="STRICT_ACCESS+ENCRYPT_AT_REST+ENCRYPT_IN_TRANSIT+AUDIT+MINIMIZE"}
      else if($4=="confidential") {profile="CONFIDENTIAL_CONTROLLED"; reasons="NEED_TO_KNOW+ENCRYPT+AUDIT+RETENTION"}
      else if($4=="internal") {profile="INTERNAL_CONTROLLED"; reasons="AUTHENTICATED_ACCESS+INTEGRITY+RETENTION"}
      print $1,$2,$4,profile,reasons
    }
  ' "$ROOT/input/data-assets.csv" > "$ROOT/output/data-protection.csv"

  awk -F',' -v OFS=',' '
    NR==1{print "crypto_id,service,decision,reasons";next}
    {
      decision="PASS_REVIEW"; reasons="CURRENT_MECHANISM_AND_MANAGED_LIFECYCLE"
      if($4=="TLS-1.0") {decision="BLOCK_LEGACY_TLS"; reasons="OBSOLETE_PROTOCOL+HOSTNAME_VALIDATION_DISABLED"}
      else if($6=="same_server") {decision="BLOCK_KEY_COLOCATION"; reasons="KEY_AND_DATA_SHARE_FAILURE_DOMAIN"}
      else if($7==0) {decision="BLOCK_NO_ROTATION"; reasons="NO_KEY_LIFECYCLE"}
      else if($3=="in_transit" && $8=="disabled") {decision="BLOCK_TRUST_VALIDATION"; reasons="PEER_NAME_NOT_VALIDATED"}
      else if($9!="documented") {decision="IMPROVE_REVOCATION"; reasons="REVOCATION_PROCESS_MISSING"}
      print $1,$2,decision,reasons
    }
  ' "$ROOT/input/crypto-services.csv" > "$ROOT/output/crypto-review.csv"

  awk -F',' -v OFS=',' '
    NR==1{print "flow_id,declared_action,decision,reasons";next}
    {
      decision="ALLOW_MINIMUM"; reasons="BUSINESS_NEED+IDENTITY_OR_PUBLIC_SERVICE+LOGGING"
      if($1=="FLOW-103") {decision="BLOCK_DIRECT_DATABASE"; reasons="USER_ZONE_MUST_USE_APPLICATION_TIER"}
      else if($1=="FLOW-105") {decision="BLOCK_GUEST_TO_INTERNAL"; reasons="GUEST_MUST_USE_ISOLATED_RESOLVER"}
      else if($1=="FLOW-107") {decision="BLOCK_PUBLIC_ADMIN"; reasons="ADMIN_PORTAL_NOT_DIRECTLY_INTERNET_EXPOSED"}
      else if($8!="yes") {decision="IMPROVE_LOGGING"; reasons="ALLOWED_FLOW_WITHOUT_EVIDENCE"}
      print $1,$9,decision,reasons
    }
  ' "$ROOT/input/network-flows.csv" > "$ROOT/output/flow-policy.csv"

  awk -F',' -v OFS=',' '
    NR==1{print "channel_id,channel,decision,reasons";next}
    {
      decision="ALLOW"; reasons="AUTHENTICATED_ENCRYPTED_SEGMENTED"
      if($8=="allow_limited") {decision="ALLOW_LIMITED"; reasons="GUEST_ISOLATION_ONLY"}
      else if($8=="block_sensitive") {decision="BLOCK_SENSITIVE_OPERATION"; reasons="OPEN_PUBLIC_WIFI"}
      else if($8=="block") {decision="BLOCK_UNMANAGED_REMOTE"; reasons="WEAK_AUTH+UNMANAGED_DEVICE+FULL_NETWORK"}
      else if($8=="block_rogue") {decision="BLOCK_ROGUE_SSID"; reasons="UNVERIFIED_NETWORK_AND_PASSWORD_PROMPT"}
      print $1,$2,decision,reasons
    }
  ' "$ROOT/input/access-channels.csv" > "$ROOT/output/access-channel-review.csv"

  awk -F',' -v OFS=',' '
    NR==1{print "control_id,service_model,control,decision,reasons";next}
    {
      expected="shared"
      if($4=="yes" && $5=="no") expected="provider"
      else if($4=="no" && $5=="yes") expected="customer"
      decision=($7==expected?"PASS_RESPONSIBILITY":"BLOCK_OWNER_MISMATCH")
      reasons=(decision=="PASS_RESPONSIBILITY"?"OWNER_MATCHES_CONTROL_BOUNDARY":"DECLARED_OWNER_DIFFERS_FROM_BOUNDARY")
      print $1,$2,$3,decision,reasons
    }
  ' "$ROOT/input/cloud-controls.csv" > "$ROOT/output/cloud-responsibility.csv"

  awk -F',' -v OFS=',' '
    NR==1{print "secret_id,use,decision,reasons";next}
    {
      decision="PASS_SECRET_CONTROL"; reasons="DEDICATED_STORAGE+SCOPED+ROTATED"
      if($7=="improve") {decision="IMPROVE_SECRET_STORAGE"; reasons="LOCAL_ENV_REQUIRES_GOVERNANCE_AND_BACKUP"}
      else if($7=="block_exposed") {decision="BLOCK_EXPOSED_SECRET"; reasons="TRACKED_SOURCE_CODE+NO_ROTATION"}
      else if($7=="block_shared") {decision="BLOCK_SHARED_SECRET"; reasons="WIKI_STORAGE+GLOBAL_SCOPE+NO_ROTATION"}
      print $1,$2,decision,reasons
    }
  ' "$ROOT/input/secrets.csv" > "$ROOT/output/secret-review.csv"

  {
    printf '# Synthèse lot 4\n\n'
    printf -- '- données restricted : %s\n' "$(awk -F',' 'NR>1&&$3=="restricted"{c++}END{print c+0}' "$ROOT/output/data-protection.csv")"
    printf -- '- contrôles crypto bloqués : %s\n' "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/crypto-review.csv")"
    printf -- '- flux réseau bloqués : %s\n' "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/flow-policy.csv")"
    printf -- '- canaux bloqués : %s\n' "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/access-channel-review.csv")"
    printf -- '- responsabilités cloud incorrectes : %s\n' "$(awk -F',' 'NR>1&&$4=="BLOCK_OWNER_MISMATCH"{c++}END{print c+0}' "$ROOT/output/cloud-responsibility.csv")"
    printf -- '- secrets bloqués : %s\n\n' "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/secret-review.csv")"
    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 'Data, network and cloud review completed.\n'
}

validate_lab(){
  require_marker
  for f in output/data-protection.csv output/crypto-review.csv output/flow-policy.csv output/access-channel-review.csv output/cloud-responsibility.csv output/secret-review.csv evidence/summary.md evidence/outputs.sha256 evidence/source.diff; do
    [ -f "$ROOT/$f" ] || fail "missing output: $f"
  done

  [ "$(rows "$ROOT/input/data-assets.csv")" -eq 8 ] || fail "expected 8 data assets"
  [ "$(rows "$ROOT/input/crypto-services.csv")" -eq 6 ] || fail "expected 6 crypto services"
  [ "$(rows "$ROOT/input/network-flows.csv")" -eq 8 ] || fail "expected 8 flows"
  [ "$(rows "$ROOT/input/access-channels.csv")" -eq 6 ] || fail "expected 6 access channels"
  [ "$(rows "$ROOT/input/cloud-controls.csv")" -eq 8 ] || fail "expected 8 cloud controls"
  [ "$(rows "$ROOT/input/secrets.csv")" -eq 6 ] || fail "expected 6 secrets"

  [ "$(awk -F',' 'NR>1&&$3=="restricted"{c++}END{print c+0}' "$ROOT/output/data-protection.csv")" -eq 3 ] || fail "expected 3 restricted data assets"
  grep -Fq 'CRYPTO-103,Legacy admin portal,BLOCK_LEGACY_TLS' "$ROOT/output/crypto-review.csv" || fail "legacy TLS blocker missing"
  grep -Fq 'CRYPTO-104,Backup repository,BLOCK_KEY_COLOCATION' "$ROOT/output/crypto-review.csv" || fail "key colocation blocker missing"
  [ "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/crypto-review.csv")" -eq 2 ] || fail "expected 2 crypto blockers"

  grep -Fq 'FLOW-103,allow,BLOCK_DIRECT_DATABASE' "$ROOT/output/flow-policy.csv" || fail "direct DB blocker missing"
  grep -Fq 'FLOW-105,allow,BLOCK_GUEST_TO_INTERNAL' "$ROOT/output/flow-policy.csv" || fail "guest blocker missing"
  grep -Fq 'FLOW-107,allow,BLOCK_PUBLIC_ADMIN' "$ROOT/output/flow-policy.csv" || fail "public admin blocker missing"
  [ "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/flow-policy.csv")" -eq 3 ] || fail "expected 3 flow blockers"

  grep -Fq 'ACCESS-103,public_wifi,BLOCK_SENSITIVE_OPERATION' "$ROOT/output/access-channel-review.csv" || fail "public wifi blocker missing"
  grep -Fq 'ACCESS-105,remote_vpn,BLOCK_UNMANAGED_REMOTE' "$ROOT/output/access-channel-review.csv" || fail "unmanaged VPN blocker missing"
  grep -Fq 'ACCESS-106,corporate_wifi,BLOCK_ROGUE_SSID' "$ROOT/output/access-channel-review.csv" || fail "rogue SSID blocker missing"
  [ "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/access-channel-review.csv")" -eq 3 ] || fail "expected 3 access blockers"

  [ "$(awk -F',' 'NR>1&&$4=="BLOCK_OWNER_MISMATCH"{c++}END{print c+0}' "$ROOT/output/cloud-responsibility.csv")" -eq 0 ] || fail "unexpected cloud owner mismatch"
  grep -Fq 'CLOUD-102,IaaS,guest_os_patching,PASS_RESPONSIBILITY' "$ROOT/output/cloud-responsibility.csv" || fail "IaaS customer responsibility missing"
  grep -Fq 'CLOUD-108,SaaS,tenant_backup_scope,PASS_RESPONSIBILITY' "$ROOT/output/cloud-responsibility.csv" || fail "shared backup responsibility missing"

  grep -Fq 'SECRET-103,Cloud_API_key,BLOCK_EXPOSED_SECRET' "$ROOT/output/secret-review.csv" || fail "exposed secret blocker missing"
  grep -Fq 'SECRET-105,Shared_admin_password,BLOCK_SHARED_SECRET' "$ROOT/output/secret-review.csv" || fail "shared secret blocker missing"
  [ "$(awk -F',' 'NR>1&&$3~/^BLOCK_/{c++}END{print c+0}' "$ROOT/output/secret-review.csv")" -eq 2 ] || fail "expected 2 secret blockers"

  [ ! -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: data, crypto, flows, access, cloud and secrets are correct.\n'
}

reset_lab(){ require_marker; rm -rf "$ROOT"; printf 'Cybersecurity data, network and cloud 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
