#!/bin/sh
set -eu

# PoC OPX-to-POSIX generator
# Reads an .opx file, finds the first pipeline, and emits a POSIX shell script.
# This is a smoke test for the serialization format — not production quality.

OPX_FILE="${1:?usage: opx-gen.sh <file.opx>}"

# --- Parser ---

parse_id() { printf '%s' "$1" | cut -d' ' -f1; }

# Everything after the first space is the "descriptor"
_descriptor() { printf '%s' "$1" | cut -d' ' -f2-; }

parse_depth() { _descriptor "$1" | cut -d'.' -f1; }
parse_role() { _descriptor "$1" | cut -d'.' -f2- | cut -d':' -f1; }
is_leaf() { _descriptor "$1" | grep -q ':'; }
parse_type() { _descriptor "$1" | cut -d':' -f2 | cut -d'.' -f1; }
parse_value() { printf '%s' "$1" | sed 's/.*:.*\.\"//' | sed 's/"$//'; }

# Collect children of a node at a given line number and depth.
# Prints line numbers of direct children (depth+1).
collect_children() {
  _start_line="$1"
  _parent_depth="$2"
  _child_depth=$((_parent_depth + 1))
  _lnum=0
  while IFS= read -r _line; do
    _lnum=$((_lnum + 1))
    [ "$_lnum" -le "$_start_line" ] && continue
    [ -z "$_line" ] && continue
    _d="$(parse_depth "$_line")"
    [ "$_d" -le "$_parent_depth" ] && break
    [ "$_d" -eq "$_child_depth" ] && printf '%s\n' "$_lnum"
  done < "$OPX_FILE"
}

# Get a child by role (first match)
find_child() {
  _parent_line="$1"
  _parent_depth="$2"
  _target_role="$3"
  for _cline in $(collect_children "$_parent_line" "$_parent_depth"); do
    _cnode="$(sed -n "${_cline}p" "$OPX_FILE")"
    _crole="$(parse_role "$_cnode")"
    [ "$_crole" = "$_target_role" ] && printf '%s' "$_cline" && return 0
  done
  return 1
}

# Get leaf value of a child by role
child_value() {
  _parent_line="$1"
  _parent_depth="$2"
  _target_role="$3"
  _cline="$(find_child "$_parent_line" "$_parent_depth" "$_target_role")" || return 1
  _cnode="$(sed -n "${_cline}p" "$OPX_FILE")"
  parse_value "$_cnode"
}

# --- Emitter ---

emit_header() {
  printf '#!/bin/sh\n'
  printf 'set -eu\n\n'
  printf '# Generated from: %s\n' "$OPX_FILE"
  printf '# Pipeline: %s\n\n' "$1"
}

emit_call() {
  _call_line="$1"
  _call_depth="$2"
  _indent="${3:-  }"
  _target="$(child_value "$_call_line" "$_call_depth" "target")"

  _has_args=false
  _args_line=""
  if _args_line="$(find_child "$_call_line" "$_call_depth" "args")"; then
    _has_args=true
  fi

  _has_export=false
  _export_var=""
  if _export_var="$(child_value "$_call_line" "$_call_depth" "export")"; then
    _has_export=true
  fi

  if $_has_args && [ -n "$_args_line" ]; then
    _args_depth=$((_call_depth + 1))
    _arg_str=""
    for _aline in $(collect_children "$_args_line" "$_args_depth"); do
      _anode="$(sed -n "${_aline}p" "$OPX_FILE")"
      _arole="$(parse_role "$_anode")"
      _adepth="$(parse_depth "$_anode")"
      case "$_arole" in
        positional)
          _val="$(parse_value "$_anode")"
          _arg_str="${_arg_str:+$_arg_str }\"$_val\""
          ;;
        named)
          _aname="$(child_value "$_aline" "$_adepth" "name")"
          _avar="$(child_value "$_aline" "$_adepth" "var" 2>/dev/null)" || true
          if [ -n "$_avar" ]; then
            _arg_str="${_arg_str:+$_arg_str }--${_aname} \"\$${_avar}\""
          else
            _aval="$(child_value "$_aline" "$_adepth" "value" 2>/dev/null)" || _aval=""
            _arg_str="${_arg_str:+$_arg_str }--${_aname} \"$_aval\""
          fi
          ;;
      esac
    done
    printf '%s%s %s\n' "$_indent" "$_target" "$_arg_str"
  else
    printf '%s%s\n' "$_indent" "$_target"
  fi

  if $_has_export && [ -n "$_export_var" ]; then
    _fn_safe="$(printf '%s' "$_target" | tr '.' '_' | tr '-' '_')"
    printf '%s%s="$(%s_RESULT)"\n' "$_indent" "$_export_var" "$_fn_safe"
  fi
}

emit_branch() {
  _br_line="$1"
  _br_depth="$2"
  _first=true

  for _clause_line in $(collect_children "$_br_line" "$_br_depth"); do
    _clause_node="$(sed -n "${_clause_line}p" "$OPX_FILE")"
    _clause_role="$(parse_role "$_clause_node")"
    _clause_depth="$(parse_depth "$_clause_node")"

    case "$_clause_role" in
      case)
        _when_line="$(find_child "$_clause_line" "$_clause_depth" "when")" || continue
        _when_node="$(sed -n "${_when_line}p" "$OPX_FILE")"
        _when_depth="$(parse_depth "$_when_node")"

        if is_leaf "$_when_node"; then
          _when_val="$(parse_value "$_when_node")"
          if [ "$_when_val" = "always" ]; then
            printf '  # try body\n'
          fi
        else
          _expr_line="$(find_child "$_when_line" "$_when_depth" "expr" 2>/dev/null)" || true
          if [ -n "$_expr_line" ]; then
            _expr_depth="$(parse_depth "$(sed -n "${_expr_line}p" "$OPX_FILE")")"
            _op="$(child_value "$_expr_line" "$_expr_depth" "op")"
            _left_line="$(find_child "$_expr_line" "$_expr_depth" "left")"
            _left_depth="$(parse_depth "$(sed -n "${_left_line}p" "$OPX_FILE")")"
            _left_var="$(child_value "$_left_line" "$_left_depth" "var")"
            _right="$(child_value "$_expr_line" "$_expr_depth" "right")"

            if $_first; then
              printf '  if [ "$%s" = "%s" ]; then\n' "$_left_var" "$_right"
              _first=false
            else
              printf '  elif [ "$%s" = "%s" ]; then\n' "$_left_var" "$_right"
            fi
          fi
        fi

        for _body_line in $(collect_children "$_clause_line" "$_clause_depth"); do
          _body_node="$(sed -n "${_body_line}p" "$OPX_FILE")"
          _body_role="$(parse_role "$_body_node")"
          _body_depth="$(parse_depth "$_body_node")"
          case "$_body_role" in
            call) emit_call "$_body_line" "$_body_depth" "    " ;;
            when|export|error) ;;
          esac
        done
        ;;
      default)
        printf '  else\n'
        for _body_line in $(collect_children "$_clause_line" "$_clause_depth"); do
          _body_node="$(sed -n "${_body_line}p" "$OPX_FILE")"
          _body_role="$(parse_role "$_body_node")"
          _body_depth="$(parse_depth "$_body_node")"
          case "$_body_role" in
            call) emit_call "$_body_line" "$_body_depth" "    " ;;
          esac
        done
        ;;
    esac
  done
  printf '  fi\n\n'
}

# --- Main ---

# Find the first pipeline
PIPELINE_LINE=""
PIPELINE_DEPTH=""
LNUM=0
while IFS= read -r line; do
  LNUM=$((LNUM + 1))
  [ -z "$line" ] && continue
  role="$(parse_role "$line")"
  if [ "$role" = "pipeline" ]; then
    PIPELINE_LINE="$LNUM"
    PIPELINE_DEPTH="$(parse_depth "$line")"
    break
  fi
done < "$OPX_FILE"

[ -z "$PIPELINE_LINE" ] && { printf 'No pipeline found\n' >&2; exit 1; }

PIPELINE_NAME="$(child_value "$PIPELINE_LINE" "$PIPELINE_DEPTH" "name")"

emit_header "$PIPELINE_NAME"

printf '# --- Pipeline: %s ---\n\n' "$PIPELINE_NAME"
printf '%s() {\n' "$PIPELINE_NAME"

for child_line in $(collect_children "$PIPELINE_LINE" "$PIPELINE_DEPTH"); do
  child_node="$(sed -n "${child_line}p" "$OPX_FILE")"
  child_role="$(parse_role "$child_node")"
  child_depth="$(parse_depth "$child_node")"

  case "$child_role" in
    call) emit_call "$child_line" "$child_depth" ;;
    branch) emit_branch "$child_line" "$child_depth" ;;
    name|entrypoint|safety|need|want) ;; # skip metadata
    *) printf '  # unhandled role: %s\n\n' "$child_role" ;;
  esac
done

printf '}\n\n'
printf '%s "$@"\n' "$PIPELINE_NAME"
