#!/usr/bin/env bash
# deckctl install — materialize artifact modules from local subtree split.
set -euo pipefail

install_resolve_current_target() { # $1=opt_base
  local current_link="$1/current"
  local target rev_dir
  [[ -L "$current_link" ]] || return 1
  target=$(readlink "$current_link")
  if [[ "$target" == /* ]]; then
    printf '%s\n' "$target"
    return 0
  fi
  rev_dir="$1/$target"
  [[ -d "$rev_dir" ]] && printf '%s\n' "$rev_dir"
}

install_subtree_split() { # $1=module $2=commit-ish
  local module="$1" commitish="$2"
  local prefix="modules/$module"
  local cache_dir="${HOME}/.local/state/overdeck/install-cache/$module"
  local resolved split_sha cache_file

  resolved=$(git -C "$DECKCTL_ROOT" rev-parse "$commitish")
  cache_file="$cache_dir/$resolved"
  split_sha=""
  if [[ -f "$cache_file" ]]; then
    split_sha=$(<"$cache_file")
    if ! git -C "$DECKCTL_ROOT" cat-file -e "${split_sha}^{commit}" 2>/dev/null; then
      split_sha=""
    fi
  fi
  if [[ -z "$split_sha" ]]; then
    split_sha=$(git -C "$DECKCTL_ROOT" subtree split -P "$prefix" "$commitish")
    mkdir -p "$cache_dir"
    printf '%s\n' "$split_sha" >"$cache_file"
  fi
  printf '%s\n' "$split_sha"
}

install_clone_split() { # $1=artifact_dir $2=split_sha
  local artifact_dir="$1" split_sha="$2"
  local git_url="file://${DECKCTL_ROOT}/.git"

  if [[ -d "$artifact_dir/.git" ]]; then
    return 0
  fi

  mkdir -p "$(dirname "$artifact_dir")"
  git init -q "$artifact_dir"
  git -C "$artifact_dir" remote add origin "$git_url"
  git -C "$artifact_dir" fetch -q origin "$split_sha:refs/heads/deckctl-install"
  git -C "$artifact_dir" checkout -q deckctl-install
}

install_link_state_dirs() { # $1=artifact_dir $2=manifest $3=state_base
  local artifact_dir="$1" manifest="$2" state_base="$3"
  local dir

  if ! jq -e 'has("state_dirs")' "$manifest" >/dev/null 2>&1; then
    return 0
  fi

  mkdir -p "$state_base"
  while IFS= read -r dir; do
    [[ -n "$dir" ]] || continue
    mkdir -p "$state_base/$dir"
    ln -sfn "$state_base/$dir" "$artifact_dir/$dir"
  done < <(jq -r '.state_dirs[]?' "$manifest")
}

install_run_health() { # $1=artifact_dir $2=manifest $3=opt_base
  local artifact_dir="$1" manifest="$2" opt_base="$3"
  local current_link="$opt_base/current"
  local health_rel saved_current="" restored=0

  health_rel=$(jq -r '.health' "$manifest")
  [[ -n "$health_rel" && "$health_rel" != "null" ]] || die "module manifest missing health script"

  export DECKCTL_ARTIFACT_ROOT="$artifact_dir"

  if [[ -L "$current_link" ]]; then
    saved_current=$(readlink "$current_link")
  fi

  ln -sfn "$(basename "$artifact_dir")" "$current_link"
  if ! (cd "$artifact_dir" && "./${health_rel#./}"); then
    if [[ -n "$saved_current" ]]; then
      ln -sfn "$saved_current" "$current_link"
    else
      rm -f "$current_link"
    fi
    restored=1
  fi
  unset DECKCTL_ARTIFACT_ROOT

  [[ "$restored" -eq 0 ]]
}

install_flip_current() { # $1=opt_base $2=rev
  local opt_base="$1" rev="$2"
  local current_link="$opt_base/current"
  local tmp_link="${current_link}.new"

  ln -sfn "$rev" "$tmp_link"
  mv -T "$tmp_link" "$current_link"
}

install_prune() { # $1=opt_base
  local opt_base="$1"
  local current_link="$opt_base/current"
  local keep_current="" keep_predecessor="" entry target base
  local -a dirs=()

  if target=$(install_resolve_current_target "$opt_base" 2>/dev/null); then
    keep_current="$target"
  fi

  for entry in "$opt_base"/*; do
    [[ -e "$entry" ]] || continue
    [[ "$entry" == "$current_link" ]] && continue
    [[ -L "$entry" ]] && continue
    [[ -d "$entry" ]] || continue
    dirs+=("$entry")
  done

  if ((${#dirs[@]} > 0)); then
    IFS=$'\n' dirs=($(printf '%s\n' "${dirs[@]}" | while IFS= read -r d; do
      printf '%s %s\n' "$(stat -c '%Y' "$d")" "$d"
    done | sort -rn | cut -d' ' -f2-))
    unset IFS
    for entry in "${dirs[@]}"; do
      [[ -n "$keep_current" && "$entry" == "$keep_current" ]] && continue
      keep_predecessor="$entry"
      break
    done
  fi

  for entry in "$opt_base"/*; do
    [[ -e "$entry" ]] || continue
    [[ "$entry" == "$current_link" ]] && continue
    [[ -L "$entry" ]] && continue
    [[ -d "$entry" ]] || continue
    [[ -n "$keep_current" && "$entry" == "$keep_current" ]] && continue
    [[ -n "$keep_predecessor" && "$entry" == "$keep_predecessor" ]] && continue
    rm -rf "$entry"
  done
}

cmd_install() {
  local module="" commitish="HEAD" prune=0
  local manifest moddir prefix split_sha rev artifact_dir opt_base state_base
  local current_target created=0

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --prune) prune=1; shift ;;
      -*) die "unknown install flag: $1" ;;
      *)
        if [[ -z "$module" ]]; then
          module="$1"
        elif [[ "$commitish" == "HEAD" ]]; then
          commitish="$1"
        else
          die "unexpected argument: $1"
        fi
        shift
        ;;
    esac
  done

  [[ -n "$module" ]] || die "usage: deckctl install <module> [<commit-ish>] [--prune]"

  moddir="$DECKCTL_ROOT/modules/$module"
  manifest="$moddir/deck.module.json"
  [[ -f "$manifest" ]] || die "module '$module' not found ($manifest missing)"

  if [[ "$(jq -r '.install.mode // ""' "$manifest")" != "artifact" ]]; then
    die "module '$module' is not install.mode artifact"
  fi

  prefix="modules/$module"
  [[ -d "$DECKCTL_ROOT/$prefix" ]] || die "module path missing: $prefix"

  split_sha=$(install_subtree_split "$module" "$commitish")
  rev="${split_sha:0:12}"
  opt_base="${HOME}/.local/opt/overdeck/$module"
  artifact_dir="$opt_base/$rev"
  state_base="${HOME}/.local/state/overdeck/$module"

  if [[ -L "$opt_base/current" ]]; then
    if current_target=$(install_resolve_current_target "$opt_base" 2>/dev/null); then
      if [[ "$current_target" == "$artifact_dir" && -d "$artifact_dir" ]]; then
        echo "install: $module@$rev already current (no-op)"
        if [[ "$prune" -eq 1 ]]; then
          install_prune "$opt_base"
        fi
        return 0
      fi
    fi
  fi

  if [[ ! -d "$artifact_dir/.git" ]]; then
    created=1
    install_clone_split "$artifact_dir" "$split_sha" || {
      rm -rf "$artifact_dir"
      die "failed to clone split for $module@$rev"
    }
  fi

  install_link_state_dirs "$artifact_dir" "$manifest" "$state_base" || {
    [[ "$created" -eq 1 ]] && rm -rf "$artifact_dir"
    die "failed to link state_dirs for $module@$rev"
  }

  if ! install_run_health "$artifact_dir" "$manifest" "$opt_base"; then
    [[ "$created" -eq 1 ]] && rm -rf "$artifact_dir"
    die "health check failed for $module@$rev"
  fi

  install_flip_current "$opt_base" "$rev"
  echo "install: $module@$rev installed -> $opt_base/current"

  if [[ "$prune" -eq 1 ]]; then
    install_prune "$opt_base"
  fi
}
