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

SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
REPO_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd)

declare -A ROOT_SCRIPT_SCOPE_ALIASES=(
  ["DeployAddressMapper.s.sol"]="utils"
  ["DeployAddressMapperOtherChain.s.sol"]="utils"
  ["DeployCCIPRouter.s.sol"]="ccip"
  ["DeployCCIPSender.s.sol"]="ccip"
  ["DeployCCIPSenderMainnet.s.sol"]="ccip"
  ["DeployCCIPReceiver.s.sol"]="ccip"
  ["DeployCCIPReceiverMainnet.s.sol"]="ccip"
  ["DeployCCIPRelay.s.sol"]="relay"
  ["DeployCCIPWETH9Bridge.s.sol"]="ccip"
  ["DeployCCIPWETH10Bridge.s.sol"]="ccip"
  ["DeployComplianceRegistry.s.sol"]="compliance"
  ["DeployCompliantUSDC.s.sol"]="tokens"
  ["DeployCompliantUSDT.s.sol"]="tokens"
  ["DeployCWAssetReserveVerifier.s.sol"]="bridge/integration"
  ["DeployCWReserveVerifier.s.sol"]="bridge/integration"
  ["DeployFeeCollector.s.sol"]="utils"
  ["DeployGenericStateChannelManager.s.sol"]="channels"
  ["DeployMainnetTether.s.sol"]="tether"
  ["DeployMirrorManager.s.sol"]="mirror"
  ["DeployMulticall.s.sol"]="utils"
  ["DeployMultiSig.s.sol"]="governance"
  ["DeployOfficialUSDC138.s.sol"]="tokens"
  ["DeployOfficialUSDT138.s.sol"]="tokens"
  ["DeployOracle.s.sol"]="oracle"
  ["DeployPaymentChannelManager.s.sol"]="channels"
  ["DeployTokenRegistry.s.sol"]="utils"
  ["DeployTransactionMirror.s.sol"]="mirror"
  ["DeployWETH.s.sol"]="tokens"
  ["DeployWETH10.s.sol"]="tokens"
  ["DeployWETHWithCCIP.s.sol"]="full"
)

usage() {
  cat <<'EOF'
Usage:
  bash scripts/forge/scope.sh list
  bash scripts/forge/scope.sh build [scope] [forge build args...]
  bash scripts/forge/scope.sh test [scope] [forge test args...]
  bash scripts/forge/scope.sh script [scope] <script-target> [forge script args...]
  bash scripts/forge/scope.sh create [scope] <Contract.sol:Name> [forge create args...]
  bash scripts/forge/scope.sh orphans [--json]

Examples:
  bash scripts/forge/scope.sh build treasury
  bash scripts/forge/scope.sh test flash --match-path 'test/flash/*.t.sol'
  bash scripts/forge/scope.sh script bridge/trustless script/bridge/trustless/DeployTrustlessBridge.s.sol:DeployTrustlessBridge --rpc-url "$RPC_URL_138"
  bash scripts/forge/scope.sh create tokens contracts/tokens/CompliantFiatToken.sol:CompliantFiatToken --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY" --legacy
  FORGE_SCOPE=vault bash scripts/forge/scope.sh test --match-path 'test/vault/*.t.sol'

Notes:
  - Omit [scope] to use FORGE_SCOPE, otherwise default to 'full'.
  - 'full' preserves the historical repo-wide Forge behavior.
  - Any other scope is resolved relative to contracts/, for example:
      treasury           -> contracts/treasury
      bridge/trustless   -> contracts/bridge/trustless
  - If no explicit scope is given, the runner tries to infer one from
    script/test/build paths and common root-level deployment script names.
EOF
}

die() {
  echo "error: $*" >&2
  exit 1
}

info() {
  echo "$*" >&2
}

resolve_scope() {
  local raw="${1:-${FORGE_SCOPE:-full}}"
  raw="${raw#contracts/}"
  raw="${raw#/}"
  if [[ -z "$raw" ]]; then
    raw="full"
  fi
  printf '%s\n' "$raw"
}

scope_label() {
  printf '%s\n' "${1//\//-}"
}

scope_exists() {
  local scope="$1"
  [[ "$scope" == "full" || -d "$REPO_ROOT/contracts/$scope" ]]
}

infer_scope_from_script_alias() {
  local raw="${1%%:*}"
  local base
  base=$(basename "$raw")
  if [[ -n "${ROOT_SCRIPT_SCOPE_ALIASES[$base]:-}" ]]; then
    printf '%s\n' "${ROOT_SCRIPT_SCOPE_ALIASES[$base]}"
    return 0
  fi
  return 1
}

infer_scope_from_script_imports() {
  local raw="${1%%:*}"
  local script_path="$REPO_ROOT/${raw#./}"
  [[ -f "$script_path" ]] || return 1

  python3 - "$REPO_ROOT" "$script_path" <<'PY'
from pathlib import Path
import re
import sys

repo_root = Path(sys.argv[1]).resolve()
script_path = Path(sys.argv[2]).resolve()
text = script_path.read_text(errors="ignore")
imports = re.findall(r'^\s*import\s+(?:\{[^}]+\}\s+from\s+)?["\']([^"\']+)["\'];', text, re.M)

for imp in imports:
    if not imp.startswith(("./", "../")):
        continue
    candidate = (script_path.parent / imp).resolve()
    try:
        rel = candidate.relative_to(repo_root).as_posix()
    except ValueError:
        continue
    if not rel.startswith("contracts/"):
        continue
    scope = rel[len("contracts/"):]
    if candidate.is_file():
        scope = str(Path(scope).parent).replace("\\", "/")
    scope = scope.strip(".")
    while scope:
        if (repo_root / "contracts" / scope).is_dir():
            print(scope)
            raise SystemExit(0)
        scope = scope.rsplit("/", 1)[0] if "/" in scope else ""

raise SystemExit(1)
PY
}

extract_scope_from_path() {
  local raw="${1%%:*}"
  raw="${raw#./}"

  case "$raw" in
    contracts/*)
      raw="${raw#contracts/}"
      ;;
    test/*)
      raw="${raw#test/}"
      ;;
    script/*)
      raw="${raw#script/}"
      ;;
    *)
      return 1
      ;;
  esac

  local candidate
  for candidate in "$raw" "${raw#deploy/}"; do
    [[ -n "$candidate" ]] || continue

    if [[ -d "$REPO_ROOT/contracts/$candidate" ]]; then
      printf '%s\n' "$candidate"
      return 0
    fi

    local probe="${candidate%/*}"
    if [[ "$probe" != "$candidate" ]]; then
      while [[ -n "$probe" ]]; do
        if [[ -d "$REPO_ROOT/contracts/$probe" ]]; then
          printf '%s\n' "$probe"
          return 0
        fi
        if [[ "$probe" != *"/"* ]]; then
          break
        fi
        probe="${probe%/*}"
      done
    fi
  done

  if [[ "$1" == script/* ]]; then
    infer_scope_from_script_alias "$1" && return 0
    infer_scope_from_script_imports "$1" && return 0
  fi

  return 1
}

infer_scope_from_args() {
  local arg inferred
  for arg in "$@"; do
    inferred=$(extract_scope_from_path "$arg" || true)
    if [[ -n "$inferred" ]]; then
      printf '%s\n' "$inferred"
      return 0
    fi
  done
  return 1
}

list_scopes() {
  (
    cd "$REPO_ROOT"
    echo "full"
    find contracts -mindepth 1 -maxdepth 2 -type d | sed 's#^contracts/##' | sort
  )
}

prepare_scope_env() {
  local scope="$1"
  local command="$2"

  if [[ "$scope" == "full" ]]; then
    return 0
  fi

  local src_dir="contracts/$scope"
  [[ -d "$REPO_ROOT/$src_dir" ]] || die "unknown scope '$scope' (expected directory '$src_dir')"

  local label
  label=$(scope_label "$scope")

  export FOUNDRY_SRC="$src_dir"
  export FOUNDRY_OUT="out/scopes/$label"
  export FOUNDRY_CACHE_PATH="cache/scopes/$label"
  export FOUNDRY_SPARSE_MODE="${FOUNDRY_SPARSE_MODE:-true}"

  if [[ "$command" == "test" ]]; then
    local test_dir="test/$scope"
    if [[ -d "$REPO_ROOT/$test_dir" ]]; then
      export FOUNDRY_TEST="$test_dir"
    fi
  fi

  if [[ "$command" == "script" ]]; then
    local script_dir="script/$scope"
    if [[ -d "$REPO_ROOT/$script_dir" ]]; then
      export FOUNDRY_SCRIPT="$script_dir"
    fi
  fi
}

main() {
  cd "$REPO_ROOT"

  local command="${1:-}"
  [[ -n "$command" ]] || {
    usage
    exit 1
  }
  shift || true

  case "$command" in
    help|-h|--help)
      usage
      ;;
    list)
      list_scopes
      ;;
    orphans)
      exec python3 scripts/forge/report-contract-reachability.py "$@"
      ;;
    build|test|script|create)
      local scope=""
      if [[ $# -gt 0 && "$1" != --* ]]; then
        local maybe_scope
        maybe_scope=$(resolve_scope "$1")
        if scope_exists "$maybe_scope"; then
          scope="$maybe_scope"
          shift
        fi
      fi

      if [[ -z "$scope" ]]; then
        if [[ -n "${FORGE_SCOPE:-}" ]]; then
          scope=$(resolve_scope)
        else
          scope=$(infer_scope_from_args "$@" || printf 'full\n')
        fi
      fi

      prepare_scope_env "$scope" "$command"

      if [[ "$scope" == "full" ]]; then
        info "Forge scope: full repo"
      else
        info "Forge scope: $scope"
        info "  src=$FOUNDRY_SRC out=$FOUNDRY_OUT cache=$FOUNDRY_CACHE_PATH"
        [[ -n "${FOUNDRY_TEST:-}" ]] && info "  test=$FOUNDRY_TEST"
        [[ -n "${FOUNDRY_SCRIPT:-}" ]] && info "  script=$FOUNDRY_SCRIPT"
      fi

      case "$command" in
        build)
          if [[ "$scope" == "full" ]]; then
            exec forge build "$@"
          fi
          exec forge build --skip test --skip script "$@"
          ;;
        test)
          if [[ "$scope" != "full" && -z "${FOUNDRY_TEST:-}" ]]; then
            die "scope '$scope' has no matching test/<scope> directory"
          fi
          exec forge test "$@"
          ;;
        script)
          [[ $# -gt 0 ]] || die "script command requires a script target, e.g. script/treasury/DeployTreasuryExecutor138.s.sol:DeployTreasuryExecutor138"
          exec forge script "$@"
          ;;
        create)
          [[ $# -gt 0 ]] || die "create command requires a contract target, e.g. contracts/tokens/CompliantFiatToken.sol:CompliantFiatToken"
          exec forge create "$@"
          ;;
      esac
      ;;
    *)
      die "unknown command '$command'"
      ;;
  esac
}

main "$@"
