#!/bin/bash

set -euo pipefail

MODE="baseline"
TARGET=""
CONTAINER_NAME=""
CONTAINER_PORT=""
SCHEME="http"
API_FORMAT="openapi"

REPORT_DIR="${REPORT_DIR:-$(pwd)/zap-reports}"
ZAP_IMAGE="${ZAP_IMAGE:-ghcr.io/zaproxy/zaproxy:stable}"

DOCKER_NETWORK=""
USE_HOST_NETWORK="auto"
IGNORE_WARNINGS="true"
TIMEOUT_SECONDS="0"

usage() {
  echo ""
  echo "ZAP Standalone DAST Wrapper"
  echo ""
  echo "Usage:"
  echo "  $0 --mode baseline --target <url>"
  echo "  $0 --mode full     --target <url>"
  echo "  $0 --mode api      --target <swagger/openapi/url> --api-format openapi"
  echo ""
  echo "Scan running Docker container exposed with -p:"
  echo "  $0 --mode baseline --container <container-name>"
  echo ""
  echo "Scan container inside Docker network:"
  echo "  $0 --mode baseline --container <container-name> --container-port <port> --network <docker-network>"
  echo ""
  echo "Options:"
  echo "  --mode              baseline | full | api"
  echo "  --target            Running application URL"
  echo "  --container         Running Docker container name"
  echo "  --container-port    Internal container port, example: 3000"
  echo "  --network           Docker network name, example: zap-net"
  echo "  --scheme            http or https. Default: http"
  echo "  --api-format        openapi | soap | graphql. Default: openapi"
  echo "  --report-dir        Report output directory"
  echo "  --zap-image         ZAP Docker image"
  echo "  --host-network      true | false | auto. Default: auto"
  echo "  --ignore-warnings   true | false. Default: true"
  echo "  --timeout           Timeout in seconds. Default: 0 means no timeout"
  echo ""
  echo "Examples:"
  echo "  $0 --mode baseline --target https://example.com"
  echo "  $0 --mode baseline --target http://127.0.0.1:3001"
  echo "  $0 --mode baseline --container juiceshop"
  echo "  $0 --mode baseline --container juiceshop --container-port 3000 --network zap-net"
  echo "  $0 --mode full --target http://127.0.0.1:3001"
  echo "  $0 --mode api --target http://127.0.0.1:8080/swagger/v1/swagger.json --api-format openapi"
  echo ""
  exit 1
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --mode)
      MODE="$2"
      shift 2
      ;;
    --target)
      TARGET="$2"
      shift 2
      ;;
    --container)
      CONTAINER_NAME="$2"
      shift 2
      ;;
    --container-port)
      CONTAINER_PORT="$2"
      shift 2
      ;;
    --network)
      DOCKER_NETWORK="$2"
      shift 2
      ;;
    --scheme)
      SCHEME="$2"
      shift 2
      ;;
    --api-format)
      API_FORMAT="$2"
      shift 2
      ;;
    --report-dir)
      REPORT_DIR="$2"
      shift 2
      ;;
    --zap-image)
      ZAP_IMAGE="$2"
      shift 2
      ;;
    --host-network)
      USE_HOST_NETWORK="$2"
      shift 2
      ;;
    --ignore-warnings)
      IGNORE_WARNINGS="$2"
      shift 2
      ;;
    --timeout)
      TIMEOUT_SECONDS="$2"
      shift 2
      ;;
    -h|--help)
      usage
      ;;
    *)
      echo "Unknown option: $1"
      usage
      ;;
  esac
done

if [[ "$MODE" != "baseline" && "$MODE" != "full" && "$MODE" != "api" ]]; then
  echo "Invalid mode: $MODE"
  usage
fi

if [[ -z "$TARGET" && -z "$CONTAINER_NAME" ]]; then
  echo "ERROR: Provide either --target or --container"
  usage
fi

mkdir -p "$REPORT_DIR"
chmod 777 "$REPORT_DIR" 2>/dev/null || true

DOCKER_ARGS=()

resolve_target_from_container() {
  local container="$1"

  if ! docker ps --format '{{.Names}}' | grep -wq "$container"; then
    echo "ERROR: Container '$container' is not running"
    echo "Check using: docker ps"
    exit 1
  fi

  if [[ -n "$DOCKER_NETWORK" ]]; then
    if [[ -z "$CONTAINER_PORT" ]]; then
      echo "ERROR: --container-port is required when using --network"
      echo "Example: --container juiceshop --container-port 3000 --network zap-net"
      exit 1
    fi

    TARGET="${SCHEME}://${container}:${CONTAINER_PORT}"
    DOCKER_ARGS+=(--network "$DOCKER_NETWORK")
    return
  fi

  local port_output=""
  local host_port=""

  if [[ -n "$CONTAINER_PORT" ]]; then
    port_output=$(docker port "$container" "${CONTAINER_PORT}/tcp" 2>/dev/null || true)
  else
    port_output=$(docker port "$container" 2>/dev/null | head -n 1 || true)
  fi

  if [[ -z "$port_output" ]]; then
    echo "ERROR: Could not find published port for container '$container'"
    echo ""
    echo "Reason:"
    echo "  The container may be running without -p hostPort:containerPort"
    echo ""
    echo "Solution 1: Run container with port mapping:"
    echo "  docker run -d --name app -p 3001:3000 image-name"
    echo ""
    echo "Solution 2: Use Docker network scan:"
    echo "  $0 --mode baseline --container $container --container-port 3000 --network <network-name>"
    exit 1
  fi

  host_port=$(echo "$port_output" | head -n 1 | awk -F ':' '{print $NF}')

  if [[ -z "$host_port" ]]; then
    echo "ERROR: Could not detect host port from:"
    echo "$port_output"
    exit 1
  fi

  TARGET="${SCHEME}://127.0.0.1:${host_port}"
  DOCKER_ARGS+=(--network host)
}

if [[ -n "$CONTAINER_NAME" && -z "$TARGET" ]]; then
  resolve_target_from_container "$CONTAINER_NAME"
else
  if [[ -n "$DOCKER_NETWORK" ]]; then
    DOCKER_ARGS+=(--network "$DOCKER_NETWORK")
  else
    if [[ "$USE_HOST_NETWORK" == "true" ]]; then
      DOCKER_ARGS+=(--network host)
    elif [[ "$USE_HOST_NETWORK" == "auto" ]]; then
      if [[ "$TARGET" == http://localhost* || "$TARGET" == https://localhost* || "$TARGET" == http://127.0.0.1* || "$TARGET" == https://127.0.0.1* ]]; then
        DOCKER_ARGS+=(--network host)
      fi
    fi
  fi
fi

TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
REPORT_PREFIX="zap-${MODE}-${TIMESTAMP}"

HTML_REPORT="${REPORT_PREFIX}.html"
JSON_REPORT="${REPORT_PREFIX}.json"
XML_REPORT="${REPORT_PREFIX}.xml"

ZAP_COMMON_ARGS=()

if [[ "$IGNORE_WARNINGS" == "true" ]]; then
  ZAP_COMMON_ARGS+=("-I")
fi

echo ""
echo "=========================================="
echo "ZAP Standalone DAST Scan"
echo "=========================================="
echo "Mode            : $MODE"
echo "Target          : $TARGET"
echo "Container       : ${CONTAINER_NAME:-not used}"
echo "Docker Network  : ${DOCKER_NETWORK:-not used}"
echo "Report Directory: $REPORT_DIR"
echo "ZAP Image       : $ZAP_IMAGE"
echo "Ignore Warnings : $IGNORE_WARNINGS"
echo "Timeout Seconds : $TIMEOUT_SECONDS"
echo "=========================================="
echo ""

run_docker_zap() {
  if [[ "$TIMEOUT_SECONDS" != "0" ]]; then
    timeout "$TIMEOUT_SECONDS" docker run --rm \
      "${DOCKER_ARGS[@]}" \
      -v "$REPORT_DIR:/zap/wrk/:rw" \
      -t "$ZAP_IMAGE" \
      "$@"
  else
    docker run --rm \
      "${DOCKER_ARGS[@]}" \
      -v "$REPORT_DIR:/zap/wrk/:rw" \
      -t "$ZAP_IMAGE" \
      "$@"
  fi
}

case "$MODE" in
  baseline)
    echo "Running ZAP baseline scan..."
    run_docker_zap zap-baseline.py \
      -t "$TARGET" \
      -r "$HTML_REPORT" \
      -J "$JSON_REPORT" \
      -x "$XML_REPORT" \
      "${ZAP_COMMON_ARGS[@]}"
    ;;

  full)
    echo "WARNING: ZAP full scan performs active attacks."
    echo "Use full scan only on test/staging applications."
    echo ""
    run_docker_zap zap-full-scan.py \
      -t "$TARGET" \
      -r "$HTML_REPORT" \
      -J "$JSON_REPORT" \
      -x "$XML_REPORT" \
      "${ZAP_COMMON_ARGS[@]}"
    ;;

  api)
    echo "Running ZAP API scan..."
    run_docker_zap zap-api-scan.py \
      -t "$TARGET" \
      -f "$API_FORMAT" \
      -r "$HTML_REPORT" \
      -J "$JSON_REPORT" \
      -x "$XML_REPORT" \
      "${ZAP_COMMON_ARGS[@]}"
    ;;
esac

echo ""
echo "=========================================="
echo "ZAP Scan Completed"
echo "=========================================="
echo "HTML Report: $REPORT_DIR/$HTML_REPORT"
echo "JSON Report: $REPORT_DIR/$JSON_REPORT"
echo "XML Report : $REPORT_DIR/$XML_REPORT"
echo "=========================================="
echo ""

if command -v ls >/dev/null 2>&1; then
  ls -lh "$REPORT_DIR" | tail
fi
