#!/bin/bash
#
# The storage script creates dev.json

declare -F vg_free_size > /dev/null || source /var/tmp/install/storage/stutil.sh

declare -F select_from > /dev/null || source /var/tmp/install/ui.sh

create_new_lukskey() {
  local lukskey
  lukskey=$(get_profile ".lukskey")
  if [[ -z $lukskey ]]; then
    lukskey=$(ask_new_key "lukskey for $(get_dev .ppv)") || return
  fi
  echo -n "$lukskey" > $installbase/lukskey
  chmod 600 $installbase/lukskey
}

create_vg() {
  local vgname ppv pv uuid
  vgname=$(get_unique_vgname "vg")
  ppv=$(get_dev .ppv)
  if [[ $(tail -c1 $installbase/lukskey | od -An -tx1) =~ [[:space:]]?0a ]]; then
    1>&2 echo "Error: Passphrase file must not end with a newline"
    return 1
  fi
  cryptsetup luksFormat --force-password --batch-mode "$ppv" $installbase/lukskey || return
  sleep 1
  uuid=$(get_uuid "$ppv") || return
  cryptsetup luksOpen --batch-mode --key-file=$installbase/lukskey "$ppv" "luks-$uuid" || return
  sleep 1
  pv=$(get_only_child "$ppv") || return
  pvcreate "$pv" || return
  sleep 1
  vgcreate "$vgname" "$pv" || return
  sleep 1
  jqd ".pv = \"$pv\""
  jqd ".vgname = \"$vgname\""
}

create_partitions_tasks() {
  local vgname root swap home opt acc="[]"
  vgname=$(get_dev .vgname)
  read -r root swap home opt < <(jq -r '"\(.root//0) \(.swap//0) \(.home//0) \(.opt//0)"' $installbase/size.json)
  if get_dev_bool .efi.wipe || get_dev_bool .efi.reformat_disk; then
    acc=$(jm "$acc" task=wipe dev=$(get_dev .efi.dev) t=efi)
  fi
  acc=$(jm "$acc" task=create size=$root t=ext4 name=root)
  acc=$(jm "$acc" task=create size=$swap t=swap name=swap)
  acc=$(jm "$acc" task=create size=$home t=ext4 name=home)
  acc=$(jm "$acc" task=create size=$opt t=ext4 name=opt)
  echo "$acc"
}

ask_existing_lukskey() {
  local ppv REPLY
  ppv=$(get_dev .ppv)
  REPLY=$(read_prompt_silent "Please enter the passphrase to unlock $ppv:")
  if [[ -z $REPLY ]]; then
    if read_yn "Restart storage configuration?"; then
      return 27
    else
      return 1
    fi
  fi
  echo -n "$REPLY" > $installbase/lukskey
  chmod 600 $installbase/lukskey
  cryptsetup luksOpen -q --disable-external-tokens --key-file $installbase/lukskey --test-passphrase "$ppv" || return
  jqd ".private_lukskey = true"
}

fresh_install() {
  local tasks
  configure_efi_partition || return
  configure_data_partition || return
  create_new_lukskey || return
  create_vg || return
  init_dev_json || return
  sensible_sizes || return
  tasks="$(create_partitions_tasks)" || return
  run_storage_tasks "$tasks"
}

open_luks() {
  local uuid ppv
  ppv=$(get_dev .ppv)
  uuid=$(get_uuid "$ppv") || return
  cryptsetup luksOpen -q --disable-external-tokens --key-file $installbase/lukskey "$ppv" "luks-$uuid" || return
  sleep 1
}

get_crypto_devices() {
  lsblk -n --filter "FSTYPE == 'crypto_LUKS'" -o KNAME,SIZE
}

configure_ppv() {
  local rows row kname path pos
  rows=$(get_crypto_devices)
  if [[ -z $rows ]]; then
    1>&2 echo "ERROR: configure_ppv: no crypto devices"
    return 1
  fi
  if [[ $(wc -l <<< $rows) = "1" ]]; then
    kname=$(sed 's/ .*//' <<< $rows)
    path=$(get_path "$kname")
    1>&2 echo "Using the only crypto device $path"
    jqd ".ppv = \"$path\""
    return
  fi
  1>&2 echo "Please choose the data partition"
  select_arrow "$rows" || return
  pos=$(get_ui_pos)
  row=$(get_nth "$pos" <<< $rows)
  kname=$(sed 's/ .*//' <<< $row)
  path=$(get_path "$kname")
  jqd ".ppv = \"$path\""
}

should_keep_data_partition() {
  local rows kname size
  rows=$(get_crypto_devices)
  if [[ -z $rows ]]; then
    return 1
  fi
  if [[ $(wc -l <<< $rows) = "1" ]]; then
    kname=$(sed 's/ .*//' <<< $rows)
    size=$(sed 's/.* //' <<< $rows)
    read_nodefault "Should $kname ($size) be the data partition?"
  else
    1>&2 echo "Encrypted partitions found on $(get_kname $(get_dev .data.disk))"
    read_nodefault "Use one of them as the data partition?"
  fi
}

get_existing_lukskey() {
  local rc
  while :; do
    ask_existing_lukskey ; rc=$?
    (( rc == 27 )) && return 27
    (( rc == 0 )) && return
  done
}

prepare_partitions() {
  local tasks rc efidisk datadisk again
  while :; do
    if [[ $again ]]; then
      read_yn "Retry the partitioning? \"n\" goes back to config" ; rc=$?
      (( rc == 27 )) && continue
      (( rc != 0 )) && return 1
    fi
    again=1
    auto_assemble_raid
    efidisk=$(configure_disk "the disk that will contain the EFI partition") || continue
    datadisk=$(configure_disk "the disk that will contain the data partition") || continue
    jqd ".efi.disk = \"$efidisk\""
    jqd ".data.disk = \"$datadisk\""
    configure_create_label || continue
    if get_dev_bool .data.reformat_disk; then
      rc=1
    elif get_dev_bool .efi.reformat_disk && [[ $datadisk = "$efidisk" ]]; then
      rc=1
    else
      should_keep_data_partition ; rc=$?
      (( rc == 27 )) && continue
    fi
    if ! get_dev_bool .efi.reformat_disk; then
      check_gpt_label "$efidisk" || continue
    fi
    if (( rc != 0 )); then
      fresh_install || continue
      return
    fi
    configure_efi_partition || continue
    configure_ppv || continue
    get_existing_lukskey || continue
    run open_luks || continue
    configure_pv || continue
    configure_vgname || continue
    configure_lv_roles || continue
    sensible_sizes || continue
    tasks=$(preserve_data_tasks) || continue
    run_storage_tasks "$tasks" || continue
    return
  done
}

storage_main() {
  prepare_partitions || return
  populate_dev_json || return
}

return 2> /dev/null || {
  storage_main
}
