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

# Manual end-to-end probe for POST /api/satellite/route strict validation
# (AZ-809). Each failure call should return HTTP 400 with an
# `application/problem+json` body. The happy path should return HTTP 200.
#
# Two enforcement layers:
#   1. UnmappedMemberHandling.Disallow + [JsonRequired] — deserializer rejects
#      missing-required and unknown fields with errors via GlobalExceptionHandler.
#   2. WithValidation<CreateRouteRequest> — runs CreateRouteRequestValidator +
#      RoutePointValidator + GeofencePolygonValidator (range, count, cross-field).
#
# Usage:
#   API_URL=https://localhost:8080 JWT="<bearer-token>" ./scripts/probe_route_validation.sh

API_URL="${API_URL:-https://localhost:8080}"
JWT="${JWT:-}"
ENDPOINT="${API_URL%/}/api/satellite/route"

if [[ -z "${JWT}" ]]; then
    echo "ERROR: set JWT env var to a bearer token. Mint one via:"
    echo "  dotnet run --project SatelliteProvider.IntegrationTests -- --mint-only"
    exit 2
fi

curl_args=(-sS -k -H "Authorization: Bearer ${JWT}" -H "Content-Type: application/json")

probe() {
    local label="$1"
    local body="$2"
    local expected_status="$3"

    echo "----- ${label} (expecting HTTP ${expected_status}) -----"
    local response
    response=$(curl "${curl_args[@]}" -X POST -d "${body}" "${ENDPOINT}" -w "\nHTTP_STATUS=%{http_code}\n")
    echo "${response}"
    local actual_status
    actual_status=$(echo "${response}" | tail -n 1 | sed 's/HTTP_STATUS=//')
    if [[ "${actual_status}" != "${expected_status}" ]]; then
        echo "FAIL: expected HTTP ${expected_status}, got ${actual_status}"
        return 1
    fi
    echo "OK: HTTP ${expected_status}"
    echo
}

route_id=$(uuidgen | tr '[:upper:]' '[:lower:]')

probe "happy-path-no-maps" '{
  "id": "'"${route_id}"'",
  "name": "probe-route-1",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": 50.10, "lon": 36.10 },
    { "lat": 50.11, "lon": 36.11 }
  ],
  "requestMaps": false,
  "createTilesZip": false
}' 200

# Rule 2: missing id (probe-confirmed gap)
probe "missing-id" '{
  "name": "probe-missing-id",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": 50.10, "lon": 36.10 },
    { "lat": 50.11, "lon": 36.11 }
  ],
  "requestMaps": false,
  "createTilesZip": false
}' 400

# Rule 2: zero-Guid id
probe "zero-guid-id" '{
  "id": "00000000-0000-0000-0000-000000000000",
  "name": "probe-zero-id",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": 50.10, "lon": 36.10 },
    { "lat": 50.11, "lon": 36.11 }
  ],
  "requestMaps": false,
  "createTilesZip": false
}' 400

# Rule 3: empty name
probe "empty-name" '{
  "id": "'$(uuidgen | tr '[:upper:]' '[:lower:]')'",
  "name": "",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": 50.10, "lon": 36.10 },
    { "lat": 50.11, "lon": 36.11 }
  ],
  "requestMaps": false,
  "createTilesZip": false
}' 400

# Rule 7: points too few (1)
probe "points-too-few" '{
  "id": "'$(uuidgen | tr '[:upper:]' '[:lower:]')'",
  "name": "probe-1-point",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": 50.10, "lon": 36.10 }
  ],
  "requestMaps": false,
  "createTilesZip": false
}' 400

# Rule 8: nested point lat out of range
probe "point-lat-out-of-range" '{
  "id": "'$(uuidgen | tr '[:upper:]' '[:lower:]')'",
  "name": "probe-point-lat",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": 50.10, "lon": 36.10 },
    { "lat": 91.0,  "lon": 36.11 }
  ],
  "requestMaps": false,
  "createTilesZip": false
}' 400

# Rule 9: geofence NW not north-of SE (cross-field invariant)
probe "geofence-nw-not-north" '{
  "id": "'$(uuidgen | tr '[:upper:]' '[:lower:]')'",
  "name": "probe-geofence-inverted",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": 50.10, "lon": 36.10 },
    { "lat": 50.11, "lon": 36.11 }
  ],
  "geofences": {
    "polygons": [
      { "northWest": { "lat": 50.05, "lon": 36.05 },
        "southEast": { "lat": 50.05, "lon": 36.15 } }
    ]
  },
  "requestMaps": false,
  "createTilesZip": false
}' 400

# Rule 12: cross-field createTilesZip without requestMaps
probe "createTilesZip-without-requestMaps" '{
  "id": "'$(uuidgen | tr '[:upper:]' '[:lower:]')'",
  "name": "probe-cross-field",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": 50.10, "lon": 36.10 },
    { "lat": 50.11, "lon": 36.11 }
  ],
  "requestMaps": false,
  "createTilesZip": true
}' 400

# Rule 13: unknown root field
probe "unknown-root-field" '{
  "id": "'$(uuidgen | tr '[:upper:]' '[:lower:]')'",
  "name": "probe-unknown",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": 50.10, "lon": 36.10 },
    { "lat": 50.11, "lon": 36.11 }
  ],
  "requestMaps": false,
  "createTilesZip": false,
  "debug": "fingerprint-probe"
}' 400

# Rule 14: nested type mismatch
probe "point-lat-type-mismatch" '{
  "id": "'$(uuidgen | tr '[:upper:]' '[:lower:]')'",
  "name": "probe-type-mismatch",
  "regionSizeMeters": 1000,
  "zoomLevel": 18,
  "points": [
    { "lat": "fifty", "lon": 36.10 },
    { "lat": 50.11,   "lon": 36.11 }
  ],
  "requestMaps": false,
  "createTilesZip": false
}' 400

echo "All probes passed."
