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

ACTION="${1:-setup}"
LAB_ROOT="${2:-/tmp/soria-git-release-ci-${USER:-student}}"
MARKER=".soria-git-release-ci-project"
DEMO_SECRET="exercise-only-soria-credential"

fail(){ printf 'ERROR: %s\n' "$*" >&2; exit 1; }
safe_root(){
  case "$LAB_ROOT" in /tmp/soria-git-*|"${HOME}"/soria-git-*) ;; *) fail "refused lab root: $LAB_ROOT";; esac
  [ "$LAB_ROOT" != /tmp ] && [ "$LAB_ROOT" != "$HOME" ] || fail "refused root: $LAB_ROOT"
}
require_marker(){ [ -f "$LAB_ROOT/$MARKER" ] || fail "missing marker: $LAB_ROOT/$MARKER"; }
config_repo(){
  git -C "$1" config user.name "SORIA Student"
  git -C "$1" config user.email "student@soria.invalid"
  git -C "$1" config init.defaultBranch main
}
init_repo(){ mkdir -p "$1"; git -C "$1" init -q -b main; config_repo "$1"; }
commit(){ git -C "$1" add "$2"; git -C "$1" commit -q -m "$3"; }

create_release(){
  local r="$LAB_ROOT/release"; init_repo "$r"; mkdir -p "$r/src" "$r/docs"
  printf '# SORIA Agent\n' > "$r/README.md"
  printf '#!/usr/bin/env bash\nprintf "healthy\\n"\n' > "$r/src/healthcheck.sh"; chmod +x "$r/src/healthcheck.sh"
  commit "$r" . "chore: initialize release repository"
  printf 'timeout=5\n' > "$r/src/agent.conf"; commit "$r" src/agent.conf "feat: add timeout"
}
create_secret(){
  local r="$LAB_ROOT/secret-origin"; init_repo "$r"
  printf '# Incident lab\n' > "$r/README.md"; commit "$r" README.md "chore: initialize incident repository"
  printf '%s\n' "$DEMO_SECRET" > "$r/.env"; commit "$r" .env "chore: add demonstration credential"
  printf '.env\n' > "$r/.gitignore"; git -C "$r" rm -q .env; commit "$r" .gitignore "fix: stop tracking environment file"
  git clone -q "$r" "$LAB_ROOT/secret-rewrite"; config_repo "$LAB_ROOT/secret-rewrite"
}
create_artifacts(){
  local r="$LAB_ROOT/artifacts"; init_repo "$r"; mkdir -p "$r/src" "$r/dist" "$r/images"
  printf 'echo build\n' > "$r/src/build.sh"
  printf 'dist/\n*.iso\n*.qcow2\n*.log\n' > "$r/.gitignore"
  printf '*.png binary\n*.zip binary\n*.pdf binary\n' > "$r/.gitattributes"
  commit "$r" . "chore: define artifact policy"
  dd if=/dev/zero of="$r/dist/app.bin" bs=1024 count=64 status=none
  printf 'runtime\n' > "$r/build.log"; printf 'vm\n' > "$r/images/lab.qcow2"
}
write_ci_check(){
  cat > "$1" <<'SH'
#!/usr/bin/env bash
set -eu
file="${1:-platform.conf}"
test -f "$file"
grep -Fxq 'environment=staging' "$file"
grep -Fxq 'backup_check=enabled' "$file"
! grep -Eiq '(ghp_|github_pat_|password[[:space:]]*=|token[[:space:]]*=)' "$file"
printf 'CI validation passed for %s\n' "$file"
SH
  chmod +x "$1"
}
create_ci(){
  local r="$LAB_ROOT/ci"; init_repo "$r"; mkdir -p "$r/ci" "$r/.github/workflows" "$r/evidence"
  printf 'environment=staging\nbackup_check=enabled\n' > "$r/platform.conf"; write_ci_check "$r/ci/check.sh"
  cat > "$r/.github/workflows/ci.yml" <<'YAML'
name: repository-validation
on: [push, pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: bash ci/check.sh platform.conf
YAML
  commit "$r" . "ci: add reproducible validation"
}
create_project(){
  local r="$LAB_ROOT/project"; init_repo "$r"; mkdir -p "$r/ci" "$r/config" "$r/docs" "$r/evidence" "$r/.github/workflows"
  printf '# SORIA PME Infrastructure\n' > "$r/README.md"
  printf '.env\nartifacts/\n*.log\n' > "$r/.gitignore"
  printf 'environment=staging\nmonitoring=enabled\nbackup_check=enabled\n' > "$r/config/platform.conf"
  cat > "$r/ci/check.sh" <<'SH'
#!/usr/bin/env bash
set -eu
script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
cd "$script_dir/.."
git diff --check
grep -Fxq 'environment=staging' config/platform.conf
grep -Fxq 'monitoring=enabled' config/platform.conf
grep -Fxq 'backup_check=enabled' config/platform.conf
! git ls-files | grep -Eq '(^|/)\.env$|(^|/)artifacts/'
! git grep -I -E '(ghp_|github_pat_|password[[:space:]]*=|token[[:space:]]*=)' HEAD -- . ':(exclude)ci/check.sh'
printf 'Project validation passed.\n'
SH
  chmod +x "$r/ci/check.sh"
  printf 'name: infrastructure-validation\non: [push, pull_request]\njobs:\n  validate:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - run: bash ci/check.sh\n' > "$r/.github/workflows/ci.yml"
  printf '# Contribution\n\nBranch from dev, review, validate, document rollback and never commit credentials or generated artifacts.\n' > "$r/docs/CONTRIBUTING.md"
  commit "$r" . "chore: initialize final project"
}

setup(){
  if [ -e "$LAB_ROOT" ]; then require_marker; rm -rf "$LAB_ROOT"/{release,secret-origin,secret-rewrite,artifacts,ci,project,evidence}; else mkdir -p "$LAB_ROOT"; : > "$LAB_ROOT/$MARKER"; fi
  mkdir -p "$LAB_ROOT/evidence"; create_release; create_secret; create_artifacts; create_ci; create_project
  printf 'Git release, CI and final project laboratory ready: %s\n' "$LAB_ROOT"
}
status(){
  require_marker
  local n; for n in release secret-origin secret-rewrite artifacts ci project; do
    printf '\n[%s]\n' "$n"; git -C "$LAB_ROOT/$n" status --short --branch; git -C "$LAB_ROOT/$n" log --oneline --decorate --graph --all -8; git -C "$LAB_ROOT/$n" tag -n || true
  done
}
run_release(){
  local r="$LAB_ROOT/release"; mkdir -p "$r/dist"
  tar -czf "$r/dist/soria-agent-1.0.0.tar.gz" -C "$r" README.md src
  (cd "$r/dist"; sha256sum soria-agent-1.0.0.tar.gz > SHA256SUMS)
  printf '# Release 1.0.0\n\nValidation: syntax and checksum.\nRollback: return to the previous annotated tag.\n' > "$r/docs/RELEASE_NOTES_1.0.0.md"
  commit "$r" docs/RELEASE_NOTES_1.0.0.md "docs: prepare release 1.0.0"
  git -C "$r" tag -a v1.0.0 -m "SORIA Agent 1.0.0"
  git -C "$r" show v1.0.0 > "$LAB_ROOT/evidence/release-tag.txt"; cp "$r/dist/SHA256SUMS" "$LAB_ROOT/evidence/release-checksum.txt"
}
run_secret(){
  local o="$LAB_ROOT/secret-origin" r="$LAB_ROOT/secret-rewrite"
  git -C "$o" log -p --all -- .env > "$LAB_ROOT/evidence/secret-before.txt"
  grep -Fq "$DEMO_SECRET" "$LAB_ROOT/evidence/secret-before.txt" || fail "demo credential missing"
  (cd "$r"; FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch .env' --prune-empty -- --all >/dev/null; rm -rf .git/refs/original; git reflog expire --expire=now --all; git gc --prune=now >/dev/null)
  git -C "$r" log --all --oneline > "$LAB_ROOT/evidence/secret-rewritten-history.txt"
  printf '# Response\n\n1. Revoke or rotate.\n2. Use git-filter-repo in real work.\n3. Coordinate force updates, forks and clones.\n4. Add scanning and push protection.\n' > "$LAB_ROOT/evidence/secret-response.md"
}
run_artifacts(){ git -C "$LAB_ROOT/artifacts" status --ignored --short > "$LAB_ROOT/evidence/artifact-status.txt"; printf '# Policy\n\nCommit sources; store builds, VM images and logs outside Git. Evaluate Git LFS only for binaries that must be versioned.\n' > "$LAB_ROOT/evidence/artifact-policy.md"; }
run_ci(){
  local r="$LAB_ROOT/ci" code
  bash "$r/ci/check.sh" "$r/platform.conf" > "$r/evidence/pass.txt"
  git -C "$r" switch -q -c feature/broken-check; printf 'environment=staging\nbackup_check=disabled\n' > "$r/platform.conf"; commit "$r" platform.conf "test: introduce CI failure"
  set +e; bash "$r/ci/check.sh" "$r/platform.conf" > "$r/evidence/fail.stdout" 2> "$r/evidence/fail.stderr"; code=$?; set -e
  [ "$code" -ne 0 ] || fail "broken check passed"; printf '%s\n' "$code" > "$r/evidence/fail.exit-code"
  printf 'environment=staging\nbackup_check=enabled\n' > "$r/platform.conf"; commit "$r" platform.conf "fix: restore valid configuration"; bash "$r/ci/check.sh" "$r/platform.conf" > "$r/evidence/fixed.txt"; git -C "$r" switch -q main
  cp "$r/evidence/pass.txt" "$LAB_ROOT/evidence/ci-pass.txt"; cp "$r/evidence/fail.exit-code" "$LAB_ROOT/evidence/ci-fail-exit-code.txt"; cp "$r/evidence/fixed.txt" "$LAB_ROOT/evidence/ci-fixed.txt"
}
run_project(){
  local r="$LAB_ROOT/project"
  git -C "$r" switch -q -c dev; printf 'integration=true\n' > "$r/config/integration.conf"; commit "$r" config/integration.conf "chore: initialize dev"
  git -C "$r" switch -q -c feature/backup-runbook
  printf '# Backup runbook\n\nValidate backup_check=enabled and record evidence. Rollback by reverting the feature merge.\n' > "$r/docs/BACKUP_RUNBOOK.md"; commit "$r" docs/BACKUP_RUNBOOK.md "docs: add backup runbook"
  printf '# Issue\nAcceptance: validation and rollback documented.\n' > "$r/evidence/issue.md"
  printf '# Pull request\nBase: dev\nHead: feature/backup-runbook\nValidation: bash ci/check.sh\n' > "$r/evidence/pull-request.md"
  printf '# Review\nApproved after rollback clarification.\n' > "$r/evidence/review.md"; commit "$r" evidence "docs: record review evidence"
  bash "$r/ci/check.sh" > "$r/evidence/ci-before-merge.txt"
  git -C "$r" switch -q dev; git -C "$r" merge --no-ff feature/backup-runbook -m "merge: integrate backup runbook" >/dev/null; bash "$r/ci/check.sh" > "$r/evidence/ci-after-merge.txt"
  git -C "$r" switch -q main; git -C "$r" merge --no-ff dev -m "merge: release infrastructure baseline" >/dev/null
  printf '# Infrastructure 1.0.0\nIncludes CI, backup validation and contribution rules.\n' > "$r/docs/RELEASE_1.0.0.md"; commit "$r" docs/RELEASE_1.0.0.md "docs: prepare release 1.0.0"
  bash "$r/ci/check.sh" > "$r/evidence/ci-release.txt"
  printf '# Final checklist\n- [x] issue and review\n- [x] CI before and after merge\n- [x] no tracked secret or artifact\n- [x] annotated release tag\n- [x] rollback documentation\n' > "$r/evidence/FINAL_CHECKLIST.md"; commit "$r" evidence "docs: complete acceptance evidence"
  git -C "$r" tag -a v1.0.0 -m "Infrastructure baseline 1.0.0"
  git -C "$r" log --oneline --decorate --graph --all > "$LAB_ROOT/evidence/project-graph.txt"; git -C "$r" show v1.0.0 > "$LAB_ROOT/evidence/project-tag.txt"
}
run(){ require_marker; run_release; run_secret; run_artifacts; run_ci; run_project; printf 'Reference release, secret, artifact, CI and final project scenarios completed.\n'; }
validate(){
  require_marker; local r before after merge parents code
  r="$LAB_ROOT/release"; [ "$(git -C "$r" cat-file -t v1.0.0)" = tag ] || fail "release tag is not annotated"; [ "$(git -C "$r" rev-list -n1 v1.0.0)" = "$(git -C "$r" rev-parse main)" ] || fail "release tag target is wrong"; (cd "$r/dist"; sha256sum -c SHA256SUMS >/dev/null) || fail "checksum failed"
  before="$(git -C "$LAB_ROOT/secret-origin" log -p --all -- .env)"; case "$before" in *"$DEMO_SECRET"*) ;; *) fail "original history lost demo credential";; esac
  after="$(git -C "$LAB_ROOT/secret-rewrite" log -p --all -- .env)"; case "$after" in *"$DEMO_SECRET"*) fail "rewritten history retains demo credential";; esac; git -C "$LAB_ROOT/secret-rewrite" check-ignore -q .env || fail ".env not ignored"
  r="$LAB_ROOT/artifacts"; git -C "$r" check-ignore -q dist/app.bin; git -C "$r" check-ignore -q build.log; git -C "$r" check-ignore -q images/lab.qcow2; ! git -C "$r" ls-files | grep -Eq '(^|/)dist/|\.qcow2$|\.log$' || fail "artifact tracked"
  r="$LAB_ROOT/ci"; code="$(cat "$r/evidence/fail.exit-code")"; [ "$code" -ne 0 ] || fail "failure code is zero"; grep -Fq 'CI validation passed' "$r/evidence/pass.txt"; grep -Fq 'CI validation passed' "$r/evidence/fixed.txt"; [ "$(git -C "$r" branch --show-current)" = main ] || fail "CI repo not on main"
  r="$LAB_ROOT/project"; git -C "$r" merge-base --is-ancestor feature/backup-runbook dev || fail "dev misses feature"; git -C "$r" merge-base --is-ancestor dev main || fail "main misses dev"
  merge="$(git -C "$r" log --format='%H' --grep='^merge: integrate backup runbook$' -1)"; [ -n "$merge" ] || fail "feature merge missing"; parents="$(git -C "$r" show -s --format='%P' "$merge" | awk '{print NF}')"; [ "$parents" -eq 2 ] || fail "feature integration not a merge commit"
  [ "$(git -C "$r" cat-file -t v1.0.0)" = tag ] || fail "project tag not annotated"; [ "$(git -C "$r" rev-list -n1 v1.0.0)" = "$(git -C "$r" rev-parse main)" ] || fail "project tag target wrong"; bash "$r/ci/check.sh" >/dev/null
  ! git -C "$r" ls-files | grep -Eq '(^|/)\.env$|(^|/)artifacts/' || fail "project tracks forbidden paths"
  ! git -C "$r" grep -I -E '(ghp_|github_pat_|password[[:space:]]*=|token[[:space:]]*=)' HEAD -- . ':(exclude)ci/check.sh' >/dev/null 2>&1 || fail "credential-like data in project"
  [ -z "$(git -C "$r" status --porcelain)" ] || fail "project not clean"
  local f; for f in release-tag.txt release-checksum.txt secret-before.txt secret-rewritten-history.txt secret-response.md artifact-status.txt artifact-policy.md ci-pass.txt ci-fail-exit-code.txt ci-fixed.txt project-graph.txt project-tag.txt; do [ -s "$LAB_ROOT/evidence/$f" ] || fail "missing evidence: $f"; done
  printf 'Validation successful: annotated releases, rewrite limits, artifact policy, CI failure recovery and final project are correct.\n'
}
reset(){ require_marker; rm -rf "$LAB_ROOT"; printf 'Git release, CI and final project laboratory removed: %s\n' "$LAB_ROOT"; }

safe_root
case "$ACTION" in setup) setup;; status) status;; run) run;; validate) validate;; reset) reset;; *) fail "unknown action: $ACTION";; esac
