#!/bin/bash
set -e

# Retrieve a subkey under the main group from the JSON. Allow default
function sub_getval()
{
   local parent="$1"   # e.g. broker1
   local key="$2"
   local default="$3"
   local file="$4"

   if val=$(_sub_getval "$parent" "$key" "$file"); then
      printf '%s\n' "$val"
   else
      printf '%s\n' "$default"
   fi
}
# Retrieve a subkey under the main group from the JSON.
function _sub_getval()
{
   local parent="$1"   # e.g. broker1
   local key="$2"      # e.g. client_xml_file
   local file="$3"
   local val

   if [[ -z "$parent" || -z "$key" || -z "$file" ]]; then
      echo "get_json_subkey: missing arguments" >&2
      return 1
   fi

   if ! val=$(jq -er --arg p "$parent" --arg k "$key" \
      '.[$p][$k]' "$file" 2>/dev/null); then
      return 2   # missing key or invalid
   fi

   printf '%s\n' "$val"
}

# Retrieve a key from the JSON. Allow default
function getval()
{
   local key="$1"
   local default="$2"
   local file="$3"

   if val=$(_getval "$key" "$file"); then
      printf '%s\n' "$val"
   else
      printf '%s\n' "$default"
   fi
}
# Retrieve a key from the JSON.
function _getval()
{
   local key="$1"
   local file="$2"
   local val

   if ! val=$(jq -er ".$key" "$file" 2>/dev/null); then
      return 2   # missing or jq error
   fi

   if [[ "$val" == "null" ]]; then
      return 3   # explicit null
   fi

   printf '%s\n' "$val"
   return 0
}

# Function to load list in JSON into array. Backward compatible with old string keys
# Returns: 0 - all good, even empty array if not required
#          1 - Fail, since array is empty and required
function load_array_from_json()
{
   local key="$1"                # e.g. "dbnames" or "parent.child.list"
   local file="$2"
   local -n out_array="$3"       # nameref target
   local required="${4:-true}"   # true = must exist and non-empty

   # Verify key exists
   if ! jq -e "getpath([\"${key//./\",\"}\"])" "$file" >/dev/null 2>&1; then
      if [[ "$required" == "true" ]]; then
         echo "ERROR: Required key missing: $key" >&2
         return 1
      else
         out_array=()
         return 0
      fi
   fi

   local type
   type=$(jq -r "getpath([\"${key//./\",\"}\"]) | type" "$file")

   case "$type" in
      array )
         mapfile -t out_array < <(
             jq -r "getpath([\"${key//./\",\"}\"])[]" "$file"
         )
         ;;
      string )
         local raw
         raw=$(jq -r "getpath([\"${key//./\",\"}\"])" "$file")
         IFS=',' read -r -a out_array <<< "$raw"
         ;;
      null )
         if [[ "$required" == "true" ]]; then
            echo "ERROR: Required key is null: $key" >&2
            return 1
         else
            out_array=()
            return 0
         fi
         ;;
      * )
         echo "ERROR: $key must be string or array (got $type)" >&2
         return 1
         ;;
   esac

   # Enforce non-empty if required
   if [[ "$required" == "true" ]] && ((${#out_array[@]} == 0)); then
      echo "ERROR: Required key is empty: $key" >&2
      return 1
   fi

   return 0
}

add_object_if_present()
{
   local key="$1"
   local file="$2"
   local json="$3"
   local obj

   if obj=$(jq -ce ".$key" "$file" 2>/dev/null); then
      json=$(jq --arg k "$key" --argjson v "$obj" '. + {($k): $v}' <<<"$json")
   fi

   echo "$json"
}

add_if_present()
{
   local key="$1"
   local file="$2"
   local json="$3"
   local val

   if ! val=$(_getval $key $file); then
      echo "$json"
      return
   fi

   # Determine if value is valid JSON (number/bool/object/array)
   if [[ "$val" =~ ^-?[0-9]+$ ]]; then
      json=$(jq --arg k "$key" --argjson v "$val" '. + {($k): $v}' <<<"$json")
   else
      json=$(jq --arg k "$key" --arg v "$val" '. + {($k): $v}' <<<"$json")
   fi

   echo "$json"
}

infile="$1"
outfile="$2"

if [[ -z "$infile" ]]; then
   echo "Usage: $0 original.json [original_migrated.json]"
   exit 1
fi

if [[ ! -f "$infile" ]]; then
   echo "ERROR: input JSON not found: $infile"
   exit 1
fi

# If outfile not provided, derive it from infile
if [[ -z "$outfile" ]]; then
   base="${infile%.json}"          # strip .json if present
   outfile="${base}_migrated.json"
fi

############################################
# Determine database list
############################################
load_array_from_json "dbnames" "$infile" dbnames false

if [[ ${#dbnames[@]} -eq 0 ]]; then
   dbnames=("hotel")
fi

############################################
# Build database array
############################################
db_json="[]"

for db in "${dbnames[@]}"; do
   dbtype=$(sub_getval "$db" "dbtype" "h2" "$infile")
   dbhost=$(sub_getval "$db" "dbhost" "localhost" "$infile")
   db_obj=$(jq -n \
      --arg name "$db" \
      --arg dbtype "$dbtype" \
      --arg dbhost "$dbhost" \
      '{name:$name,dbtype:$dbtype,dbhost:$dbhost}')

   if [[ "$dbtype" == "postgres" ]]; then
      dbport=$(sub_getval "$db" "dbport" "5432" "$infile")
      db_obj=$(jq --argjson dbport "$dbport" '. + {dbport:$dbport}' <<<"$db_obj")

      # Optional dbpath
      if dbpath=$(_sub_getval "$db" "dbpath" "$infile"); then
         db_obj=$(jq --arg dbpath "$dbpath" '. + {dbpath:$dbpath}' <<<"$db_obj")
      fi
   else
      dbpath=$(sub_getval "$db" "dbpath" "../db" "$infile")
      db_obj=$(jq --arg dbpath "$dbpath" '. + {dbpath:$dbpath}' <<<"$db_obj")
   fi

   # Optional collation
   if collation=$(_sub_getval "$db" "collation" "$infile"); then
      db_obj=$(jq --arg collation "$collation" '. + {collation:$collation}' <<<"$db_obj")
   fi

   db_json=$(jq --argjson obj "$db_obj" '. += [$obj]' <<<"$db_json")

done

############################################
# Build brokers list
############################################
broker_json="[]"

mapfile -t brokers < <(
jq -r '
   if .brokers == null then empty
   elif (.brokers|type) == "array" then .brokers[]
   elif (.brokers|type) == "string" then .brokers | split(",")[]
   else empty
   end
' "$infile"
)

i=0
for br in "${brokers[@]}"; do
   ((i++))

   br_obj=$(jq -c --arg b "$br" --arg i "$i" '
      .[$b] // {} |
      {
        name: ("localhost-broker"+$i),
        client_xml_file: (.client_xml_file // ("../etc/broker"+$i+"_client.xml")),
        broker_os_user: (.broker_os_user // "fwd"),
        broker_process_acl: (.broker_process_acl // "00000"),
        spawner_path: (.spawner_path // "/opt/spawner/spawn"),
        broker_client: (.broker_client // "localhost"),
        broker_server: (.broker_server // "localhost"),
        storepath: (.storepath // "./deploy/security"),
        logpath: (.logpath // "./deploy/logs"),
        fwd_lib: (.fwd_lib // "/opt/fwd/build/lib")
      }
   ' "$infile")

   broker_json=$(jq --argjson obj "$br_obj" '. += [$obj]' <<<"$broker_json")

done

############################################
# Build server object
############################################
server_json='{"name":"localhost"}'
for key in \
   spawner_path \
   server_xml_file \
   directory_xml_file \
   client_start_dir \
   dateFormat \
   numberGroupSep \
   numberDecimalSep \
   p2j_entry \
   pkgroot \
   propath \
   search_path \
   path_separator \
   file_separator \
   case_sensitive \
   os_user \
   kbd_layout \
   dbuser \
   dbuserpass \
   dbadmin \
   dbadminpass \
   server_log \
   client_log \
   embedded_host \
   admin_port \
   log_rotation_limit \
   log_rotation_count
do
   server_json=$(add_if_present "$key" "$infile" "$server_json")
done

# Add non-flat objects
server_json=$(add_object_if_present "proxy" "$infile" "$server_json")
server_json=$(add_object_if_present "webcert" "$infile" "$server_json")
server_json=$(add_object_if_present "soap" "$infile" "$server_json")
server_json=$(add_object_if_present "session_management" "$infile" "$server_json")

############################################
# Convert dbnames array → JSON array
############################################
db_array_json=$(printf '%s\n' "${dbnames[@]}" | jq -R . | jq -s .)

############################################
# Add databases to server
############################################

server_json=$(jq \
   --argjson dbs "$db_array_json" \
   '. + {databases: $dbs}' \
   <<<"$server_json")

# Add brokers, if any
if [[ "$broker_json" != "[]" ]]; then
   server_json=$(jq \
      --argjson brokers "$broker_json" \
      '. + {brokers: $brokers}' \
      <<<"$server_json")
fi

############################################
# Assemble final JSON
############################################
jq -n \
   --argjson server "$server_json" \
   --argjson dbs "$db_json" \
'
{
   servers: [$server],
   databases: $dbs
}
' > "$outfile"

echo "Migration complete:"
echo "Old: $infile"
echo "New: $outfile"
