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

ACTION="${1:-setup}"
LAB_ROOT="${2:-/tmp/soria-git-branches-${USER:-student}}"
MARKER_NAME=".soria-git-branches-integration"

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

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

require_marker() {
  [ -f "$LAB_ROOT/$MARKER_NAME" ] || fail "missing marker: $LAB_ROOT/$MARKER_NAME"
}

configure_repo() {
  local repo="$1"
  git -C "$repo" config user.name "SORIA Student"
  git -C "$repo" config user.email "student@soria.invalid"
  git -C "$repo" config init.defaultBranch main
  git -C "$repo" config merge.conflictStyle zdiff3
}

init_repo() {
  local repo="$1"
  mkdir -p "$repo"
  git -C "$repo" init -q -b main
  configure_repo "$repo"
}

commit_file() {
  local repo="$1"
  local path="$2"
  local message="$3"
  git -C "$repo" add "$path"
  git -C "$repo" commit -q -m "$message"
}

create_branches_repo() {
  local repo="$LAB_ROOT/branches"
  init_repo "$repo"
  cat > "$repo/README.md" <<'TXT'
# SORIA Branches Lab

Main contains the accepted baseline.
TXT
  commit_file "$repo" README.md "chore: initialize branch laboratory"
}

create_fast_forward_repo() {
  local repo="$LAB_ROOT/fast-forward"
  init_repo "$repo"
  printf 'healthcheck=disabled\n' > "$repo/service.conf"
  commit_file "$repo" service.conf "chore: initialize healthcheck configuration"
  git -C "$repo" switch -q -c feature/healthcheck
  printf 'healthcheck=enabled\n' > "$repo/service.conf"
  commit_file "$repo" service.conf "feat: enable service healthcheck"
  git -C "$repo" switch -q main
}

create_merge_commit_repo() {
  local repo="$LAB_ROOT/merge-commit"
  init_repo "$repo"
  mkdir -p "$repo/docs"
  printf 'baseline=ready\n' > "$repo/platform.conf"
  commit_file "$repo" platform.conf "chore: initialize platform baseline"

  git -C "$repo" switch -q -c feature/backup
  printf '# Backup runbook\n' > "$repo/docs/backup.md"
  commit_file "$repo" docs/backup.md "docs: add backup runbook"

  git -C "$repo" switch -q main
  printf 'maintenance-window=sunday\n' > "$repo/operations.conf"
  commit_file "$repo" operations.conf "chore: define maintenance window"
}

create_conflict_repo() {
  local repo="$LAB_ROOT/conflict"
  init_repo "$repo"
  cat > "$repo/application.conf" <<'TXT'
mode=standard
workers=2
TXT
  commit_file "$repo" application.conf "chore: initialize application configuration"

  git -C "$repo" switch -q -c feature/performance
  cat > "$repo/application.conf" <<'TXT'
mode=performance
workers=4
TXT
  commit_file "$repo" application.conf "feat: tune application for performance"

  git -C "$repo" switch -q main
  cat > "$repo/application.conf" <<'TXT'
mode=secure
workers=2
TXT
  commit_file "$repo" application.conf "fix: enforce secure application mode"
}

create_rebase_repo() {
  local repo="$LAB_ROOT/rebase"
  init_repo "$repo"
  mkdir -p "$repo/docs"
  printf '# Reporting\n' > "$repo/docs/reporting.md"
  commit_file "$repo" docs/reporting.md "chore: initialize reporting documentation"

  git -C "$repo" switch -q -c feature/reports
  printf '\n## Daily report\n' >> "$repo/docs/reporting.md"
  commit_file "$repo" docs/reporting.md "docs: add daily report section"
  printf '\n## Weekly report\n' >> "$repo/docs/reporting.md"
  commit_file "$repo" docs/reporting.md "docs: add weekly report section"
  git -C "$repo" rev-list --reverse main..feature/reports > "$LAB_ROOT/rebase-old-shas.txt"

  git -C "$repo" switch -q main
  printf 'retention-days=30\n' > "$repo/policy.conf"
  commit_file "$repo" policy.conf "fix: define report retention policy"
}

create_strategy_repo() {
  local repo="$LAB_ROOT/strategy"
  init_repo "$repo"
  mkdir -p "$repo/docs"
  printf '# SORIA Platform\n' > "$repo/README.md"
  commit_file "$repo" README.md "chore: initialize platform repository"

  git -C "$repo" switch -q -c dev
  printf 'integration=enabled\n' > "$repo/dev.conf"
  commit_file "$repo" dev.conf "chore: initialize integration branch"

  git -C "$repo" switch -q -c feature/network-policy
  printf 'default-deny=true\n' > "$repo/network-policy.conf"
  commit_file "$repo" network-policy.conf "feat: add default-deny network policy"

  git -C "$repo" switch -q dev
  printf '# Integration checklist\n' > "$repo/docs/integration.md"
  commit_file "$repo" docs/integration.md "docs: add integration checklist"
}

setup_lab() {
  if [ -e "$LAB_ROOT" ]; then
    require_marker
    rm -rf "$LAB_ROOT/branches" "$LAB_ROOT/fast-forward" "$LAB_ROOT/merge-commit" \
      "$LAB_ROOT/conflict" "$LAB_ROOT/rebase" "$LAB_ROOT/strategy" "$LAB_ROOT/evidence"
    rm -f "$LAB_ROOT/rebase-old-shas.txt" "$LAB_ROOT/rebase-new-shas.txt"
  else
    mkdir -p "$LAB_ROOT"
    : > "$LAB_ROOT/$MARKER_NAME"
  fi

  create_branches_repo
  create_fast_forward_repo
  create_merge_commit_repo
  create_conflict_repo
  create_rebase_repo
  create_strategy_repo
  mkdir -p "$LAB_ROOT/evidence"

  cat > "$LAB_ROOT/README.txt" <<'TXT'
Commands:
  setup     create isolated branch, merge, conflict, rebase and strategy repositories
  status    inspect every repository without changing it
  run       execute the reference integration scenarios
  validate  verify graph shape, content and clean states
  reset     delete this marked lab directory
TXT

  printf 'Git branches and integration laboratory ready: %s\n' "$LAB_ROOT"
}

show_status() {
  require_marker
  local name repo
  for name in branches fast-forward merge-commit conflict rebase strategy; do
    repo="$LAB_ROOT/$name"
    printf '\n[%s]\n' "$name"
    git -C "$repo" status --short --branch
    git -C "$repo" log --oneline --decorate --graph --all -8
  done
}

run_reference() {
  require_marker
  local repo

  repo="$LAB_ROOT/branches"
  git -C "$repo" switch -q -c feature/monitoring
  mkdir -p "$repo/docs"
  printf '# Monitoring notes\n' > "$repo/docs/monitoring.md"
  commit_file "$repo" docs/monitoring.md "docs: add monitoring notes"
  git -C "$repo" switch -q main
  git -C "$repo" switch -q -c docs/runbook
  mkdir -p "$repo/docs"
  printf '# Operations runbook\n' > "$repo/docs/runbook.md"
  commit_file "$repo" docs/runbook.md "docs: add operations runbook"
  git -C "$repo" switch -q main
  git -C "$repo" branch --format='%(refname:short)' > "$LAB_ROOT/evidence/branches.txt"

  repo="$LAB_ROOT/fast-forward"
  git -C "$repo" merge --ff-only feature/healthcheck >/dev/null
  git -C "$repo" log --oneline --decorate --graph --all > "$LAB_ROOT/evidence/fast-forward-graph.txt"

  repo="$LAB_ROOT/merge-commit"
  GIT_MERGE_AUTOEDIT=no git -C "$repo" merge --no-ff feature/backup -m "merge: integrate backup runbook" >/dev/null
  git -C "$repo" log --oneline --decorate --graph --all > "$LAB_ROOT/evidence/merge-commit-graph.txt"

  repo="$LAB_ROOT/conflict"
  if GIT_MERGE_AUTOEDIT=no git -C "$repo" merge feature/performance >/dev/null 2>&1; then
    fail "conflict scenario merged unexpectedly"
  fi
  git -C "$repo" status --short > "$LAB_ROOT/evidence/conflict-status.txt"
  git -C "$repo" ls-files -u > "$LAB_ROOT/evidence/conflict-index-stages.txt"
  cp "$repo/application.conf" "$LAB_ROOT/evidence/conflict-markers.txt"
  cat > "$repo/application.conf" <<'TXT'
mode=secure-performance
workers=4
TXT
  git -C "$repo" add application.conf
  git -C "$repo" commit -q -m "merge: resolve secure performance configuration"

  repo="$LAB_ROOT/rebase"
  git -C "$repo" switch -q feature/reports
  git -C "$repo" rebase main >/dev/null
  git -C "$repo" rev-list --reverse main..feature/reports > "$LAB_ROOT/rebase-new-shas.txt"
  git -C "$repo" log --oneline --decorate --graph --all > "$LAB_ROOT/evidence/rebase-graph.txt"

  repo="$LAB_ROOT/strategy"
  GIT_MERGE_AUTOEDIT=no git -C "$repo" merge --no-ff feature/network-policy -m "merge: integrate network policy" >/dev/null
  git -C "$repo" switch -q -c release/1.0
  printf '# Release 1.0\n' > "$repo/docs/release-1.0.md"
  commit_file "$repo" docs/release-1.0.md "docs: prepare release 1.0"
  git -C "$repo" switch -q main
  GIT_MERGE_AUTOEDIT=no git -C "$repo" merge --no-ff release/1.0 -m "merge: release version 1.0" >/dev/null
  git -C "$repo" log --oneline --decorate --graph --all > "$LAB_ROOT/evidence/strategy-graph.txt"

  printf 'Reference branch and integration scenarios completed.\n'
}

validate_reference() {
  require_marker
  local repo old_shas new_shas main_sha feature_sha parent_count

  repo="$LAB_ROOT/branches"
  [ "$(git -C "$repo" branch --show-current)" = 'main' ] || fail "branches repository should end on main"
  git -C "$repo" show-ref --verify --quiet refs/heads/feature/monitoring || fail "feature/monitoring missing"
  git -C "$repo" show-ref --verify --quiet refs/heads/docs/runbook || fail "docs/runbook missing"
  [ "$(git -C "$repo" rev-list --count main..feature/monitoring)" -eq 1 ] || fail "monitoring branch should be one commit ahead"
  [ "$(git -C "$repo" rev-list --count main..docs/runbook)" -eq 1 ] || fail "runbook branch should be one commit ahead"
  [ -z "$(git -C "$repo" status --porcelain)" ] || fail "branches repository is not clean"
  [ -z "$(git -C "$repo" remote)" ] || fail "branches repository has an unexpected remote"

  repo="$LAB_ROOT/fast-forward"
  main_sha="$(git -C "$repo" rev-parse main)"
  feature_sha="$(git -C "$repo" rev-parse feature/healthcheck)"
  [ "$main_sha" = "$feature_sha" ] || fail "fast-forward main and feature should match"
  parent_count="$(git -C "$repo" show -s --format='%P' HEAD | awk '{print NF}')"
  [ "$parent_count" -eq 1 ] || fail "fast-forward should not create a merge commit"
  grep -Fxq 'healthcheck=enabled' "$repo/service.conf" || fail "fast-forward content is wrong"
  [ -z "$(git -C "$repo" status --porcelain)" ] || fail "fast-forward repository is not clean"

  repo="$LAB_ROOT/merge-commit"
  parent_count="$(git -C "$repo" show -s --format='%P' HEAD | awk '{print NF}')"
  [ "$parent_count" -eq 2 ] || fail "expected a two-parent merge commit"
  [ -f "$repo/docs/backup.md" ] || fail "backup runbook missing after merge"
  [ -f "$repo/operations.conf" ] || fail "main-side change missing after merge"
  [ -z "$(git -C "$repo" status --porcelain)" ] || fail "merge-commit repository is not clean"

  repo="$LAB_ROOT/conflict"
  parent_count="$(git -C "$repo" show -s --format='%P' HEAD | awk '{print NF}')"
  [ "$parent_count" -eq 2 ] || fail "resolved conflict should end in a merge commit"
  grep -Fxq 'mode=secure-performance' "$repo/application.conf" || fail "resolved mode is wrong"
  grep -Fxq 'workers=4' "$repo/application.conf" || fail "resolved worker count is wrong"
  ! grep -Eq '^(<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|)' "$repo/application.conf" || fail "conflict markers remain"
  [ -s "$LAB_ROOT/evidence/conflict-index-stages.txt" ] || fail "conflict index evidence is empty"
  [ -z "$(git -C "$repo" status --porcelain)" ] || fail "conflict repository is not clean"

  repo="$LAB_ROOT/rebase"
  [ "$(git -C "$repo" merge-base main feature/reports)" = "$(git -C "$repo" rev-parse main)" ] || fail "rebased feature is not based on main"
  [ "$(git -C "$repo" rev-list --count main..feature/reports)" -eq 2 ] || fail "rebased feature should contain two commits"
  old_shas="$(cat "$LAB_ROOT/rebase-old-shas.txt")"
  new_shas="$(cat "$LAB_ROOT/rebase-new-shas.txt")"
  [ "$old_shas" != "$new_shas" ] || fail "rebase did not rewrite commit IDs"
  [ -z "$(git -C "$repo" rev-list --merges main..feature/reports)" ] || fail "rebased feature should be linear"
  [ "$(git -C "$repo" branch --show-current)" = 'feature/reports' ] || fail "rebase repository should end on feature/reports"
  [ -z "$(git -C "$repo" status --porcelain)" ] || fail "rebase repository is not clean"

  repo="$LAB_ROOT/strategy"
  for branch in main dev feature/network-policy release/1.0; do
    git -C "$repo" show-ref --verify --quiet "refs/heads/$branch" || fail "strategy branch missing: $branch"
  done
  git -C "$repo" merge-base --is-ancestor dev main || fail "main does not contain dev integration"
  git -C "$repo" merge-base --is-ancestor feature/network-policy dev || fail "dev does not contain feature branch"
  git -C "$repo" merge-base --is-ancestor release/1.0 main || fail "main does not contain release branch"
  [ -f "$repo/network-policy.conf" ] || fail "network policy missing from released main"
  [ -f "$repo/docs/release-1.0.md" ] || fail "release note missing from main"
  [ "$(git -C "$repo" branch --show-current)" = 'main' ] || fail "strategy repository should end on main"
  [ -z "$(git -C "$repo" status --porcelain)" ] || fail "strategy repository is not clean"
  [ -z "$(git -C "$repo" remote)" ] || fail "strategy repository has an unexpected remote"

  printf 'Validation successful: branches, fast-forward, merge commit, conflict, rebase and strategy states are correct.\n'
}

resolve_safe_root

case "$ACTION" in
  setup)
    setup_lab
    ;;
  status)
    show_status
    ;;
  run)
    run_reference
    ;;
  validate)
    validate_reference
    ;;
  reset)
    if [ ! -e "$LAB_ROOT" ]; then
      printf 'Nothing to remove: %s\n' "$LAB_ROOT"
      exit 0
    fi
    require_marker
    rm -rf "$LAB_ROOT"
    printf 'Laboratory removed: %s\n' "$LAB_ROOT"
    ;;
  *)
    fail "unknown action '$ACTION' (setup|status|run|validate|reset)"
    ;;
esac
