#!/usr/bin/env bash
# MyOffice Proxmox LXC installer (community-scripts style).
# Run on the Proxmox host (public Gitea repo raw URL):
#   export GITEA_URL="https://gitea.example.com"
#   export GITEA_OWNER="org"
#   export GITEA_REPO="myoffice-public"
#   export GITEA_BRANCH="master"
#   export REPO_RAW_BASE="${GITEA_URL}/${GITEA_OWNER}/${GITEA_REPO}/raw/branch/${GITEA_BRANCH}"
#   bash -c "$(curl -fsSL ${REPO_RAW_BASE}/proxmox/myoffice.sh)"
#
# Creates one LXC with PostgreSQL + ASP.NET runtime + app from Gitea Release.
# No Docker, no nginx, no git/SDK/Node in the CT (use your existing proxy LXC in front).

set -euo pipefail

GITEA_URL="${GITEA_URL:-}"
GITEA_OWNER="${GITEA_OWNER:-}"
GITEA_REPO="${GITEA_REPO:-myoffice-public}"
GITEA_BRANCH="${GITEA_BRANCH:-master}"
RELEASE_TAG="${RELEASE_TAG:-latest}"
RELEASE_ASSET_URL="${RELEASE_ASSET_URL:-}"
GITEA_TOKEN="${GITEA_TOKEN:-}"

if [[ -z "${REPO_RAW_BASE:-}" ]]; then
  if [[ -n "$GITEA_URL" && -n "$GITEA_OWNER" ]]; then
    REPO_RAW_BASE="${GITEA_URL%/}/${GITEA_OWNER}/${GITEA_REPO}/raw/branch/${GITEA_BRANCH}"
  else
    REPO_RAW_BASE="https://gitea.example.com/OWNER/myoffice-public/raw/branch/${GITEA_BRANCH}"
  fi
fi

# Derive GITEA_* from REPO_RAW_BASE when only raw base is set:
#   https://host/owner/repo/raw/branch/master
if [[ -z "$GITEA_URL" || -z "$GITEA_OWNER" ]]; then
  if [[ "$REPO_RAW_BASE" =~ ^(https?://[^/]+)/([^/]+)/([^/]+)/raw/branch/([^/]+) ]]; then
    [[ -z "$GITEA_URL" ]] && GITEA_URL="${BASH_REMATCH[1]}"
    [[ -z "$GITEA_OWNER" ]] && GITEA_OWNER="${BASH_REMATCH[2]}"
    [[ "$GITEA_REPO" == "myoffice-public" ]] && GITEA_REPO="${BASH_REMATCH[3]}"
    GITEA_BRANCH="${BASH_REMATCH[4]}"
  elif [[ "$REPO_RAW_BASE" =~ ^(https?://[^/]+)/([^/]+)/([^/]+)/raw/ ]]; then
    [[ -z "$GITEA_URL" ]] && GITEA_URL="${BASH_REMATCH[1]}"
    [[ -z "$GITEA_OWNER" ]] && GITEA_OWNER="${BASH_REMATCH[2]}"
    [[ "$GITEA_REPO" == "myoffice-public" ]] && GITEA_REPO="${BASH_REMATCH[3]}"
  fi
fi

APP="MyOffice"
APP_DIR="/opt/myoffice"

YW=$'\033[33m'
BL=$'\033[36m'
RD=$'\033[01;31m'
BGN=$'\033[4;92m'
GN=$'\033[1;92m'
DGN=$'\033[32m'
CL=$'\033[m'
BOLD=$'\033[1m'
BFR=$'\r\033[K'
TAB=$'  '

CM="${TAB}✔️${TAB}${CL}"
CROSS="${TAB}✖️${TAB}${CL}"
INFO="${TAB}💡${TAB}${CL}"
OS="${TAB}🖥️${TAB}${CL}"
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
DISKSIZE="${TAB}💾${TAB}${CL}"
CPUCORE="${TAB}🧠${TAB}${CL}"
RAMSIZE="${TAB}🛠️${TAB}${CL}"
CONTAINERID="${TAB}🆔${TAB}${CL}"
HOSTNAME="${TAB}🏠${TAB}${CL}"
BRIDGE="${TAB}🌉${TAB}${CL}"
GATEWAY="${TAB}🌐${TAB}${CL}"
DEFAULT="${TAB}⚙️${TAB}${CL}"
CREATING="${TAB}🚀${TAB}${CL}"
ADVANCED="${TAB}🧩${TAB}${CL}"

header_info() {
  clear
  cat <<"EOF"
    __  ___      ____  ____________
   /  |/  /_  __/ __ \/ __/ __/  _/______
  / /|_/ / / / / / / / /_/ /_ / // ___/ _ \
 / /  / / /_/ / /_/ / __/ __// // /__/  __/
/_/  /_/\__, /\____/_/ /_/ /___/\___/\___/
       /____/  Proxmox LXC (Gitea release)
EOF
}

msg_info() { echo -ne "${TAB}${YW}${1}${CL}"; }
msg_ok() { echo -e "${BFR}${CM}${GN}${1}${CL}"; }
msg_error() { echo -e "${BFR}${CROSS}${RD}${1}${CL}"; }

error_handler() {
  local exit_code=$?
  local line_number=$1
  local command=$2
  echo -e "\n${RD}[ERROR]${CL} line ${RD}${line_number}${CL}: exit ${RD}${exit_code}${CL}: ${YW}${command}${CL}\n"
  if [[ -n "${CTID:-}" ]] && pct status "$CTID" &>/dev/null; then
    echo -e "${INFO}CT ${CTID} exists; destroy with: pct stop ${CTID}; pct destroy ${CTID}"
  fi
  exit "$exit_code"
}
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR

exit_script() {
  clear
  echo -e "\n${CROSS}${RD}User exited script${CL}\n"
  exit 0
}

check_root() {
  if [[ "$(id -u)" -ne 0 ]]; then
    msg_error "Please run this script as root on the Proxmox host."
    exit 1
  fi
}

pve_check() {
  if ! command -v pveversion >/dev/null 2>&1; then
    msg_error "This script must run on a Proxmox VE host (pveversion not found)."
    exit 1
  fi
  if ! command -v whiptail >/dev/null 2>&1; then
    msg_error "whiptail is required (apt install whiptail)."
    exit 1
  fi
}

arch_check() {
  if [[ "$(dpkg --print-architecture)" != "amd64" ]]; then
    msg_error "Only amd64 is supported."
    exit 1
  fi
}

get_valid_nextid() {
  local try_id
  try_id=$(pvesh get /cluster/nextid)
  while true; do
    if [[ -f "/etc/pve/qemu-server/${try_id}.conf" ]] || [[ -f "/etc/pve/lxc/${try_id}.conf" ]]; then
      try_id=$((try_id + 1))
      continue
    fi
    break
  done
  echo "$try_id"
}

select_storage() {
  local STORAGE_MENU=()
  local MSG_MAX_LENGTH=0
  local line TAG TYPE FREE ITEM OFFSET VALID
  while read -r line; do
    TAG=$(echo "$line" | awk '{print $1}')
    TYPE=$(echo "$line" | awk '{printf "%-10s", $2}')
    FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f 2>/dev/null | awk '{printf("%9sB", $6)}' || echo "?")
    ITEM=" Type: $TYPE Free: $FREE "
    OFFSET=2
    if [[ $((${#ITEM} + OFFSET)) -gt ${MSG_MAX_LENGTH:-0} ]]; then
      MSG_MAX_LENGTH=$((${#ITEM} + OFFSET))
    fi
    STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
  done < <(pvesm status -content rootdir | awk 'NR>1')

  VALID=$(pvesm status -content rootdir | awk 'NR>1')
  if [[ -z "$VALID" ]]; then
    msg_error "Unable to detect a valid storage location (rootdir)."
    exit 1
  elif [[ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]]; then
    STORAGE=${STORAGE_MENU[0]}
  else
    while [[ -z "${STORAGE:+x}" ]]; do
      STORAGE=$(whiptail --backtitle "MyOffice Proxmox" --title "Storage Pools" --radiolist \
        "Which storage pool for ${HN}?\n(Spacebar to select)\n" \
        16 $((MSG_MAX_LENGTH + 23)) 6 \
        "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit_script
    done
  fi
  msg_ok "Using ${CL}${BL}${STORAGE}${CL}${GN} for storage."
}

default_settings() {
  CTID=$(get_valid_nextid)
  HN="myoffice"
  CORE_COUNT="2"
  RAM_SIZE="4096"
  DISK_SIZE="20"
  BRG="vmbr0"
  NET="dhcp"
  GATEWAY_IP=""
  API_PORT="9100"
  PUBLIC_URL="" # filled after CT IP known
  START_CT="yes"
  METHOD="default"
  echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CTID}${CL}"
  echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
  echo -e "${OS}${BOLD}${DGN}OS: ${BGN}Debian 12${CL}"
  echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
  echo -e "${RAMSIZE}${BOLD}${DGN}RAM: ${BGN}${RAM_SIZE} MiB${CL}"
  echo -e "${DISKSIZE}${BOLD}${DGN}Disk: ${BGN}${DISK_SIZE}G${CL}"
  echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
  echo -e "${GATEWAY}${BOLD}${DGN}IP: ${BGN}DHCP${CL}"
  echo -e "${DEFAULT}${BOLD}${DGN}API listen: ${BGN}:${API_PORT}${CL} (SPA from release wwwroot)"
  echo -e "${CONTAINERTYPE}${BOLD}${DGN}Stack: ${BGN}PostgreSQL + ASP.NET + Gitea release${CL}"
  echo -e "${CREATING}${BOLD}${DGN}Creating MyOffice LXC with default settings${CL}"
}

advanced_settings() {
  METHOD="advanced"
  [[ -z "${CTID:-}" ]] && CTID=$(get_valid_nextid)

  while true; do
    if CTID=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Set Container ID" 8 58 "$CTID" --title "CONTAINER ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
      [[ -z "$CTID" ]] && CTID=$(get_valid_nextid)
      if pct status "$CTID" &>/dev/null || qm status "$CTID" &>/dev/null; then
        echo -e "${CROSS}${RD}ID $CTID is already in use${CL}"
        sleep 2
        continue
      fi
      echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CTID}${CL}"
      break
    else
      exit_script
    fi
  done

  if HN_IN=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Set Hostname" 8 58 myoffice --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
    HN=$(echo "${HN_IN:-myoffice}" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//' || true)
    [[ -z "$HN" ]] && HN="myoffice"
    echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
  else
    exit_script
  fi

  while true; do
    if CORE_COUNT=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
      [[ -z "$CORE_COUNT" ]] && CORE_COUNT="2"
      if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
        echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
        break
      fi
      whiptail --backtitle "MyOffice Proxmox" --title "INVALID" --msgbox "CPU cores must be a positive integer." 8 58
    else
      exit_script
    fi
  done

  while true; do
    if RAM_SIZE=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Allocate RAM in MiB" 8 58 4096 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
      [[ -z "$RAM_SIZE" ]] && RAM_SIZE="4096"
      if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
        echo -e "${RAMSIZE}${BOLD}${DGN}RAM: ${BGN}${RAM_SIZE} MiB${CL}"
        break
      fi
      whiptail --backtitle "MyOffice Proxmox" --title "INVALID" --msgbox "RAM must be a positive integer (MiB)." 8 58
    else
      exit_script
    fi
  done

  while true; do
    if DISK_SIZE=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Disk size in GiB" 8 58 20 --title "DISK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
      DISK_SIZE=$(echo "${DISK_SIZE:-20}" | tr -d ' Gg')
      if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then
        echo -e "${DISKSIZE}${BOLD}${DGN}Disk: ${BGN}${DISK_SIZE}G${CL}"
        break
      fi
      whiptail --backtitle "MyOffice Proxmox" --title "INVALID" --msgbox "Disk must be a positive integer (GiB)." 8 58
    else
      exit_script
    fi
  done

  if BRG=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
    [[ -z "$BRG" ]] && BRG="vmbr0"
    echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
  else
    exit_script
  fi

  if whiptail --backtitle "MyOffice Proxmox" --title "NETWORK" --yesno "Use DHCP?" --yes-button DHCP --no-button Static 10 58; then
    NET="dhcp"
    GATEWAY_IP=""
    echo -e "${GATEWAY}${BOLD}${DGN}IP: ${BGN}DHCP${CL}"
  else
    if NET=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Static IP CIDR (e.g. 192.168.1.120/24)" 8 58 --title "IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
      :
    else
      exit_script
    fi
    if GATEWAY_IP=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Gateway (e.g. 192.168.1.1)" 8 58 --title "GATEWAY" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
      :
    else
      exit_script
    fi
    echo -e "${GATEWAY}${BOLD}${DGN}IP: ${BGN}${NET} gw ${GATEWAY_IP}${CL}"
  fi

  if API_PORT=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Kestrel listen port" 8 58 9100 --title "API PORT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
    [[ -z "$API_PORT" ]] && API_PORT="9100"
  else
    exit_script
  fi
  echo -e "${DEFAULT}${BOLD}${DGN}API listen: ${BGN}:${API_PORT}${CL}"

  if PUBLIC_URL=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Browser public URL (empty = http://CT_IP:PORT). Use your nginx proxy URL if any." 10 70 "" --title "PUBLIC URL" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
    PUBLIC_URL=$(echo "$PUBLIC_URL" | sed 's:/*$::')
  else
    exit_script
  fi

  if RELEASE_TAG=$(whiptail --backtitle "MyOffice Proxmox" --inputbox "Gitea release tag (latest or v1.2.3)" 8 58 "${RELEASE_TAG}" --title "RELEASE TAG" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
    [[ -z "$RELEASE_TAG" ]] && RELEASE_TAG="latest"
  else
    exit_script
  fi
  echo -e "${DEFAULT}${BOLD}${DGN}Release: ${BGN}${RELEASE_TAG}${CL}"

  if whiptail --backtitle "MyOffice Proxmox" --title "START CONTAINER" --yesno "Start CT when created?" 10 58; then
    START_CT="yes"
  else
    START_CT="no"
  fi

  if whiptail --backtitle "MyOffice Proxmox" --title "ADVANCED SETTINGS" --yesno "Ready to create MyOffice LXC?" --no-button Do-Over 10 58; then
    echo -e "${CREATING}${BOLD}${DGN}Creating MyOffice LXC with advanced settings${CL}"
  else
    header_info
    echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
    advanced_settings
  fi
}

start_script() {
  if whiptail --backtitle "MyOffice Proxmox" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58; then
    header_info
    echo -e "${BL}Using Default Settings${CL}"
    default_settings
  else
    header_info
    echo -e "${RD}Using Advanced Settings${CL}"
    advanced_settings
  fi
}

ensure_template() {
  local template="debian-12-standard"
  local store="local"
  msg_info "Ensuring Debian 12 LXC template..."
  if ! pveam list "$store" 2>/dev/null | grep -q "$template"; then
    pveam update >/dev/null
    local remote
    remote=$(pveam available -section system | awk '/debian-12-standard/ {print $2; exit}')
    if [[ -z "$remote" ]]; then
      msg_error "Could not find debian-12-standard template in pveam available."
      exit 1
    fi
    pveam download "$store" "$remote"
  fi
  TEMPLATE_REF=$(pveam list "$store" | awk '/debian-12-standard/ {print $1; exit}')
  if [[ -z "${TEMPLATE_REF:-}" ]]; then
    msg_error "Debian 12 template not found after download."
    exit 1
  fi
  msg_ok "Template ${CL}${BL}${TEMPLATE_REF}${CL}"
}

create_container() {
  local net_cfg
  if [[ "$NET" == "dhcp" ]]; then
    net_cfg="name=eth0,bridge=${BRG},ip=dhcp"
  else
    net_cfg="name=eth0,bridge=${BRG},ip=${NET},gw=${GATEWAY_IP}"
  fi

  msg_info "Creating LXC ${CTID} (${HN})..."
  pct create "$CTID" "$TEMPLATE_REF" \
    --hostname "$HN" \
    --cores "$CORE_COUNT" \
    --memory "$RAM_SIZE" \
    --swap 512 \
    --rootfs "${STORAGE}:${DISK_SIZE}" \
    --net0 "$net_cfg" \
    --unprivileged 1 \
    --ostype debian \
    --onboot 1 \
    --timezone host \
    --tags "myoffice" \
    --description "MyOffice: PostgreSQL + ASP.NET Core from Gitea release. Proxy via external nginx LXC."
  msg_ok "Created CT ${CL}${BL}${CTID}${CL}"

  if [[ "$START_CT" == "yes" ]]; then
    msg_info "Starting CT ${CTID}..."
    pct start "$CTID"
    msg_ok "Started CT ${CTID}"
  else
    msg_error "CT created but not started (Start=no). Start with: pct start ${CTID}"
    exit 0
  fi
}

wait_for_network() {
  msg_info "Waiting for CT network..."
  local i ip
  for i in $(seq 1 60); do
    ip=$(pct exec "$CTID" -- bash -c "ip -4 -o addr show eth0 2>/dev/null | awk '{print \$4}' | cut -d/ -f1 | head -1" 2>/dev/null || true)
    if [[ -n "$ip" ]]; then
      CT_IP="$ip"
      msg_ok "CT IP ${CL}${BL}${CT_IP}${CL}"
      return 0
    fi
    sleep 2
  done
  msg_error "Timed out waiting for CT IP."
  exit 1
}

resolve_script_dir() {
  if [[ -n "${BASH_SOURCE[0]:-}" && -f "${BASH_SOURCE[0]}" ]]; then
    SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  else
    SCRIPT_DIR=""
  fi
}

push_install_assets() {
  local dest_dir="/tmp/myoffice-proxmox"
  pct exec "$CTID" -- mkdir -p "$dest_dir"

  if [[ -n "${SCRIPT_DIR}" && -f "${SCRIPT_DIR}/install-in-ct.sh" ]]; then
    for f in install-in-ct.sh fetch-release.sh myoffice-api.service update.sh; do
      pct push "$CTID" "${SCRIPT_DIR}/${f}" "${dest_dir}/${f}"
    done
  else
    msg_info "Downloading install assets from REPO_RAW_BASE..."
    for f in install-in-ct.sh fetch-release.sh myoffice-api.service update.sh; do
      curl -fsSL "${REPO_RAW_BASE}/proxmox/${f}" -o "/tmp/${f}"
      pct push "$CTID" "/tmp/${f}" "${dest_dir}/${f}"
    done
    msg_ok "Downloaded install assets"
  fi
  pct exec "$CTID" -- chmod +x "${dest_dir}/install-in-ct.sh" "${dest_dir}/fetch-release.sh" "${dest_dir}/update.sh"
}

run_ct_install() {
  if [[ -z "${PUBLIC_URL}" ]]; then
    PUBLIC_URL="http://${CT_IP}:${API_PORT}"
  fi
  if [[ -z "${GITEA_URL}" || -z "${GITEA_OWNER}" ]] && [[ -z "${RELEASE_ASSET_URL}" ]]; then
    msg_error "Set GITEA_URL + GITEA_OWNER (and GITEA_REPO), or RELEASE_ASSET_URL, before install."
    exit 1
  fi
  msg_info "Installing PostgreSQL + ASP.NET runtime + Gitea release..."
  pct exec "$CTID" -- env \
    REPO_RAW_BASE="$REPO_RAW_BASE" \
    APP_DIR="$APP_DIR" \
    PUBLIC_URL="$PUBLIC_URL" \
    API_PORT="$API_PORT" \
    ASSETS_DIR="/tmp/myoffice-proxmox" \
    GITEA_URL="$GITEA_URL" \
    GITEA_OWNER="$GITEA_OWNER" \
    GITEA_REPO="$GITEA_REPO" \
    RELEASE_TAG="$RELEASE_TAG" \
    RELEASE_ASSET_URL="$RELEASE_ASSET_URL" \
    GITEA_TOKEN="$GITEA_TOKEN" \
    bash /tmp/myoffice-proxmox/install-in-ct.sh
  msg_ok "Stack installed"
}

# --- main ---
header_info
echo -e "\n Loading..."
check_root
arch_check
pve_check

if ! whiptail --backtitle "MyOffice Proxmox" --title "MyOffice LXC" --yesno \
  "This will create a new LXC with:\n\n  • PostgreSQL\n  • ASP.NET Core runtime\n  • App from Gitea Release\n\nNo Docker, no nginx (point your proxy LXC at :9100).\n\nProceed?" 16 58; then
  exit_script
fi

start_script
select_storage
ensure_template
resolve_script_dir
create_container
wait_for_network
push_install_assets
run_ct_install

echo -e "\n${GN}${BOLD}Completed successfully!${CL}\n"
echo -e "${INFO}${YW}Stack in CT ${CTID}:${CL} PostgreSQL + ASP.NET + release ${RELEASE_TAG}"
echo -e "${GATEWAY}${BGN}Public URL ${PUBLIC_URL}${CL}"
echo -e "${GATEWAY}${BGN}Upstream   http://${CT_IP}:${API_PORT}${CL}  (for your nginx proxy)"
echo -e "${INFO}Demo login: user_UAH@user_UAH.userUAH / user_UAH"
echo -e "${INFO}Update: pct enter ${CTID}  then  ${APP_DIR}/proxmox/update.sh"
echo
