#!/usr/bin/env bash
#
# Regenerates the gRPC C# files from the Communication project's .proto files.
#
# Background: protoc (linux/arm64) segfaults inside our Docker build container
# (Grpc.Tools). As a workaround the generated C# is checked into
# src/ZB.MOM.WW.ScadaBridge.Communication/ — SiteStreamGrpc/ for sitestream.proto,
# CentralControlGrpc/ for central_control.proto, and SiteCommandGrpc/ for
# site_command.proto — and the Protobuf ItemGroup in the .csproj is commented out,
# so Docker just compiles the checked-in files.
#
# Run this script ON YOUR DEV MACHINE whenever a .proto changes:
#
#   docker/regen-proto.sh [sitestream|centralcontrol|sitecommand|all]   (default: all)
#
#   1. Injects a Protobuf ItemGroup for the selected proto(s) so Grpc.Tools runs.
#   2. Deletes the stale checked-in C# so a failed regen is obvious.
#   3. dotnet build (regen writes fresh files to obj/).
#   4. Copies the regenerated files back into the source tree.
#   5. Restores the original csproj so no active Protobuf item is left behind.
#
# Only the SELECTED protos get a Protobuf item. Enabling one whose generated C#
# is still checked in would define every generated type twice, which is why the
# per-proto selection exists. central_control.proto imports sitestream.proto,
# but protoc resolves that from the project-relative path — the import needs no
# Protobuf item of its own.
#
# Once we move to a Dockerfile base image that ships a working linux/arm64
# protoc, this script can be retired and Docker can regen the protos on every
# build like every other normal .NET project.

set -euo pipefail

TARGET="${1:-all}"
case "$TARGET" in
  sitestream|centralcontrol|sitecommand|all) ;;
  *) echo "usage: $0 [sitestream|centralcontrol|sitecommand|all]" >&2; exit 2 ;;
esac

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
COMM_DIR="$REPO_ROOT/src/ZB.MOM.WW.ScadaBridge.Communication"
CSPROJ="$COMM_DIR/ZB.MOM.WW.ScadaBridge.Communication.csproj"
GEN="$COMM_DIR/obj/Debug/net10.0/Protos"

echo "=== Regenerating gRPC files ($TARGET) ==="

if [[ ! -f "$CSPROJ" ]]; then
  echo "ERROR: csproj not found at $CSPROJ" >&2
  exit 1
fi

# Backup so we can always restore the comment state on failure. Leaving the
# csproj with an active Protobuf item is the one outcome that breaks Docker, so
# every exit path restores this copy.
BACKUP="$(mktemp)"
cp "$CSPROJ" "$BACKUP"
trap 'cp "$BACKUP" "$CSPROJ"; rm -f "$BACKUP"; echo "Restored csproj from backup."' ERR

# 1. Inject an ItemGroup holding just the selected protos, immediately before
#    the closing </Project>. The documented commented-out block is left alone.
python3 - "$CSPROJ" "$TARGET" <<'PY'
import pathlib, sys

csproj, target = pathlib.Path(sys.argv[1]), sys.argv[2]

protos = []
if target in ("sitestream", "all"):
    protos.append("sitestream.proto")
if target in ("centralcontrol", "all"):
    protos.append("central_control.proto")
if target in ("sitecommand", "all"):
    protos.append("site_command.proto")

items = "\n".join(
    f'    <Protobuf Include="Protos\\{p}" GrpcServices="Both" />' for p in protos)
block = f"  <ItemGroup>\n{items}\n  </ItemGroup>\n\n</Project>"

src = csproj.read_text()
if "</Project>" not in src:
    raise SystemExit("Couldn't find </Project> to inject the Protobuf ItemGroup before.")
csproj.write_text(src.replace("</Project>", block, 1))
PY

# 2. Delete the stale files so any failure to regen is obvious.
if [[ "$TARGET" == "sitestream" || "$TARGET" == "all" ]]; then
  rm -f "$COMM_DIR/SiteStreamGrpc/Sitestream.cs" "$COMM_DIR/SiteStreamGrpc/SitestreamGrpc.cs"
fi
if [[ "$TARGET" == "centralcontrol" || "$TARGET" == "all" ]]; then
  rm -f "$COMM_DIR/CentralControlGrpc/CentralControl.cs" \
        "$COMM_DIR/CentralControlGrpc/CentralControlGrpc.cs"
fi
if [[ "$TARGET" == "sitecommand" || "$TARGET" == "all" ]]; then
  rm -f "$COMM_DIR/SiteCommandGrpc/SiteCommand.cs" \
        "$COMM_DIR/SiteCommandGrpc/SiteCommandGrpc.cs"
fi

# 3. Regenerate by building.
echo "Building Communication project (regen)..."
dotnet build "$CSPROJ" --nologo -v minimal | tail -5

# 4. Copy generated files back into the source tree.
if [[ "$TARGET" == "sitestream" || "$TARGET" == "all" ]]; then
  mkdir -p "$COMM_DIR/SiteStreamGrpc"
  cp "$GEN/Sitestream.cs" "$GEN/SitestreamGrpc.cs" "$COMM_DIR/SiteStreamGrpc/"
  echo "Copied regenerated files to SiteStreamGrpc/"
fi
if [[ "$TARGET" == "centralcontrol" || "$TARGET" == "all" ]]; then
  mkdir -p "$COMM_DIR/CentralControlGrpc"
  cp "$GEN/CentralControl.cs" "$GEN/CentralControlGrpc.cs" "$COMM_DIR/CentralControlGrpc/"
  echo "Copied regenerated files to CentralControlGrpc/"
fi
if [[ "$TARGET" == "sitecommand" || "$TARGET" == "all" ]]; then
  mkdir -p "$COMM_DIR/SiteCommandGrpc"
  cp "$GEN/SiteCommand.cs" "$GEN/SiteCommandGrpc.cs" "$COMM_DIR/SiteCommandGrpc/"
  echo "Copied regenerated files to SiteCommandGrpc/"
fi

# 5. Restore the backed-up csproj — i.e. drop the injected ItemGroup — so Docker
#    builds keep working.
cp "$BACKUP" "$CSPROJ"
rm -f "$BACKUP"
trap - ERR

echo ""
echo "Done. Review and commit:"
echo "  git diff src/ZB.MOM.WW.ScadaBridge.Communication/Protos/"
echo "  git diff src/ZB.MOM.WW.ScadaBridge.Communication/SiteStreamGrpc/"
echo "  git diff src/ZB.MOM.WW.ScadaBridge.Communication/CentralControlGrpc/"
echo "  git diff src/ZB.MOM.WW.ScadaBridge.Communication/SiteCommandGrpc/"
echo "  git diff -- src/ZB.MOM.WW.ScadaBridge.Communication/*.csproj   # must be EMPTY"
