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

game_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Steam/native runtime discovery depends on the working directory, including
# steam_appid.txt. Desktop and current-terminal launches must behave alike.
cd "$game_dir" || exit $?
client_dir="$game_dir/.stardew-modsync"
update_control="$game_dir/thors-fjord-update.txt"
error_log="$client_dir/last-error.log"
smapi="$game_dir/StardewModdingAPI"
mode="launch"
python_mode=()
profile="legacy"
profile_seen=false
mods_path="${SMAPI_MODS_PATH:-}"
scan=("$@")
for ((i=0; i<${#scan[@]}; i++)); do
  case "${scan[i]}" in
    --fjord-profile)
      if [[ "$profile_seen" == true || $((i+1)) -ge ${#scan[@]} ]]; then
        echo "Use one --fjord-profile legacy|v2 argument." >&2; exit 2
      fi
      profile_seen=true; i=$((i+1)); profile="${scan[i]}"
      ;;
    --mods-path)
      if [[ $((i+1)) -ge ${#scan[@]} ]]; then echo "Missing --mods-path value." >&2; exit 2; fi
      i=$((i+1)); mods_path="${scan[i]}"
      ;;
    --fjord-profile=*|--mods-path=*) echo "Profile/path options require separate values." >&2; exit 2 ;;
  esac
done
case "$profile" in
  legacy) profile_mods="Mods" ;;
  v2) profile_mods="Mods-v2"; error_log="$client_dir/last-error-v2.log" ;;
  *) echo "Unknown Fjord profile: $profile" >&2; exit 2 ;;
esac
for selected in "$mods_path" "${SMAPI_MODS_PATH:-}"; do
  if [[ -n "$selected" && "$selected" != "$profile_mods" && "$selected" != "$game_dir/$profile_mods" ]]; then
    echo "Mods path conflicts with $profile profile; refusing update and launch." >&2; exit 2
  fi
done
if [[ -L "$game_dir/$profile_mods" ]]; then echo "Symlinked profile directory is unsafe." >&2; exit 2; fi
profile_mode=(--fjord-profile "$profile")
if [[ "$profile" == v2 ]]; then
  # An old SMAPI ignores unknown arguments. Never invoke it with a v2 flag.
  marker="$game_dir/smapi-internal/fjord-zip-profile.sha256"
  if [[ ! -f "$marker" || ! -f "$game_dir/StardewModdingAPI.dll" ]]; then
    echo "Install the profile-aware Thor's Fjord SMAPI before launching v2." >&2; exit 2
  fi
  if command -v shasum >/dev/null 2>&1; then
    actual_hash="$(shasum -a 256 "$game_dir/StardewModdingAPI.dll")"
  elif command -v sha256sum >/dev/null 2>&1; then
    actual_hash="$(sha256sum "$game_dir/StardewModdingAPI.dll")"
  else
    echo "Cannot verify profile-aware SMAPI; shasum or sha256sum is required." >&2; exit 2
  fi
  if [[ "${actual_hash%% *}" != "$(tr -d '\r\n' < "$marker")" ]]; then
    echo "SMAPI profile capability hash mismatch; repair the v2 installation." >&2; exit 2
  fi
fi

case "${1:-}" in
  --modsync-update-only)
    mode="explicit"
    shift
    ;;
  --modsync-dry-run)
    mode="explicit"
    python_mode=(--dry-run)
    shift
    ;;
  --modsync-validate-only)
    mode="explicit"
    python_mode=(--validate-only)
    shift
    ;;
esac

write_last_error() {
  local category="$1"
  local detail_file="${2:-}"
  local pending="$client_dir/.last-error.$$.${RANDOM}.tmp"

  if {
    printf 'Thor\047s Fjord launch synchronization failed at %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
    printf 'Category: %s\n' "$category"
    if [[ -n "$detail_file" && -f "$detail_file" ]]; then
      # Strip URL userinfo and common secret-bearing query parameters before
      # persisting diagnostics. Console output remains the synchronizer's own.
      sed -E \
        -e 's#(https?://)[^/@[:space:]]+@#\1[redacted]@#g' \
        -e 's#([?&](token|key|password|auth)=)[^&[:space:]]+#\1[redacted]#g' \
        "$detail_file"
    fi
  } >"$pending" 2>/dev/null; then
    chmod 600 "$pending" 2>/dev/null || true
    mv -f "$pending" "$error_log" 2>/dev/null || rm -f "$pending" 2>/dev/null || true
  else
    rm -f "$pending" 2>/dev/null || true
  fi
}

launch_smapi_in_macos_terminal() {
  local terminal_dir=""
  local command_file=""
  local osascript_path=""
  local argument

  command -v open >/dev/null 2>&1 || return 1
  terminal_dir="$(mktemp -d "${TMPDIR:-/tmp}/thors-fjord-smapi-terminal.XXXXXX" 2>/dev/null)" || return 1
  command_file="$terminal_dir/launch.command"

  if ! {
    printf '%s\n' '#!/usr/bin/env bash'
    printf 'rm -f -- %q\n' "$command_file"
    printf 'rmdir -- %q 2>/dev/null || true\n' "$terminal_dir"
    printf 'cd %q || exit $?\n' "$game_dir"
    printf 'exec %q' "$smapi"
    for argument in "$@"; do
      printf ' %q' "$argument"
    done
    printf '\n'
  } >"$command_file"; then
    rm -f "$command_file" 2>/dev/null || true
    rmdir "$terminal_dir" 2>/dev/null || true
    return 1
  fi
  chmod 700 "$command_file" || {
    rm -f "$command_file" 2>/dev/null || true
    rmdir "$terminal_dir" 2>/dev/null || true
    return 1
  }

  # iTerm's `open` document handler creates a new window even when another
  # window is available. Its AppleScript API can deliberately add a tab to the
  # current window, while still creating the first window when none exists.
  # Query iTerm itself instead of the process list: pgrep can be denied by
  # macOS privacy controls and would incorrectly force a second window.
  if osascript_path="$(command -v osascript 2>/dev/null)"; then
    if "$osascript_path" - "$command_file" <<'APPLESCRIPT'
on run argv
    set commandFile to item 1 of argv
    set launchCommand to quoted form of commandFile
    using terms from application "/Applications/iTerm.app"
        tell application "iTerm2"
            if (count of windows) > 0 then
                tell current window to create tab with default profile command launchCommand
            else
                create window with default profile command launchCommand
            end if
            activate
        end tell
    end using terms from
end run
APPLESCRIPT
    then
      # The command file removes itself as soon as iTerm starts it. Deleting it
      # here would race the new session after osascript returns.
      return 0
    fi
  fi

  # Preserve a visible-console path if iTerm isn't installed, its scripting
  # interface changes, or macOS denies Automation access.
  open -W "$command_file"
  local open_status=$?
  rm -f "$command_file" 2>/dev/null || true
  rmdir "$terminal_dir" 2>/dev/null || true
  return "$open_status"
}

launch_smapi_in_linux_terminal() {
  local terminal_name="${SMAPI_PREFER_TERMINAL_NAME:-}"
  local terminal_path=""
  local candidate
  local shell_command='cd "$1" || exit $?; shift; export TERM=xterm; exec "$@"'

  if [[ -n "$terminal_name" ]]; then
    terminal_path="$(command -v -- "$terminal_name" 2>/dev/null)" || return 1
  else
    for candidate in xterm gnome-terminal kitty terminator xfce4-terminal konsole alacritty mate-terminal wezterm x-terminal-emulator; do
      if terminal_path="$(command -v -- "$candidate" 2>/dev/null)"; then
        terminal_name="$candidate"
        break
      fi
    done
  fi
  [[ -n "$terminal_path" && -x "$terminal_path" ]] || return 1

  case "$terminal_name" in
    gnome-terminal)
      exec "$terminal_path" -- "$BASH" -c "$shell_command" thors-fjord-smapi "$game_dir" "$smapi" "$@"
      ;;
    terminator|xfce4-terminal|mate-terminal)
      exec "$terminal_path" -x "$BASH" -c "$shell_command" thors-fjord-smapi "$game_dir" "$smapi" "$@"
      ;;
    wezterm)
      exec "$terminal_path" start --cwd "$game_dir" -- "$BASH" -c "$shell_command" thors-fjord-smapi "$game_dir" "$smapi" "$@"
      ;;
    kitty)
      exec "$terminal_path" "$BASH" -c "$shell_command" thors-fjord-smapi "$game_dir" "$smapi" "$@"
      ;;
    *)
      exec "$terminal_path" -e "$BASH" -c "$shell_command" thors-fjord-smapi "$game_dir" "$smapi" "$@"
      ;;
  esac
}

launch_smapi() {
  if [[ "${SMAPI_NO_TERMINAL:-}" == "true" ]]; then
    exec "$smapi" --no-terminal "$@"
  fi
  if [[ "${SMAPI_USE_CURRENT_SHELL:-}" == "true" || -t 1 ]]; then
    exec "$smapi" "$@"
  fi

  case "$(uname -s)" in
    Darwin)
      if launch_smapi_in_macos_terminal "$@"; then
        exit 0
      fi
      ;;
    Linux)
      if launch_smapi_in_linux_terminal "$@"; then
        exit 0
      fi
      ;;
  esac

  echo "A terminal could not be opened; launching SMAPI without a visible console." >&2
  exec "$smapi" "$@"
}

update_mode=""
if [[ -f "$update_control" ]]; then
  update_mode="$(tr '\r\n' '  ' < "$update_control" | tr '[:upper:]' '[:lower:]' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/[[:space:]][[:space:]]*/ /g')" || update_mode=""
fi

if [[ "$mode" == "launch" && ! -x "$smapi" ]]; then
  echo "Installed SMAPI launcher is missing or not executable: $smapi" >&2
  exit 1
fi

if [[ "$update_mode" == "disable remote update" ]]; then
  echo "Remote mod update disabled by $update_control; using the installed mod set."
  if [[ "$mode" == "explicit" ]]; then
    exit 0
  fi
  launch_smapi "$@"
fi

if ! command -v python3 >/dev/null 2>&1; then
  echo "Python 3 is unavailable; launching the last installed mod set." >&2
  write_last_error "python3 is unavailable"
  if [[ "$mode" == "explicit" ]]; then
    exit 2
  fi
  launch_smapi "$@"
fi

if [[ "$mode" == "explicit" ]]; then
  exec python3 "$client_dir/modsync.py" --game-dir "$game_dir" "${profile_mode[@]}" "${python_mode[@]}"
fi

sync_error=""
if sync_error="$(mktemp "${TMPDIR:-/tmp}/stardew-modsync-error.XXXXXX" 2>/dev/null)"; then
  if python3 "$client_dir/modsync.py" --game-dir "$game_dir" "${profile_mode[@]}" 2>"$sync_error"; then
    rm -f "$sync_error" 2>/dev/null || true
  else
    sync_status=$?
    cat "$sync_error" >&2 || true
    echo "Thor's Fjord synchronization failed (exit $sync_status); launching the last installed mod set." >&2
    write_last_error "synchronizer exited with status $sync_status" "$sync_error"
    rm -f "$sync_error" 2>/dev/null || true
  fi
else
  # Lack of writable temporary storage must not turn a feed problem into a
  # launch blocker. Run without capture and retain a categorical diagnostic.
  if python3 "$client_dir/modsync.py" --game-dir "$game_dir" "${profile_mode[@]}"; then
    :
  else
    sync_status=$?
    echo "Thor's Fjord synchronization failed (exit $sync_status); launching the last installed mod set." >&2
    write_last_error "synchronizer exited with status $sync_status; detailed capture unavailable"
  fi
fi

launch_smapi "$@"
