#!/usr/bin/env bash

set -euo pipefail

REPO_URL="https://dock-it.dev/Idea-Studios/Maintainarr.git"
REPO_BRANCH="${MAINTAINARR_BRANCH:-main}"
APP_USER="${MAINTAINARR_USER:-maintainarr}"
INSTALL_ROOT="${MAINTAINARR_INSTALL_ROOT:-/opt/maintainarr}"
SRC_DIR="$INSTALL_ROOT/src"
BIN_DIR="$INSTALL_ROOT/bin"
BIN_PATH="$BIN_DIR/maintainarr"
DATA_DIR="${MAINTAINARR_DATA_DIR:-/var/lib/maintainarr}"
CONFIG_DIR="${MAINTAINARR_CONFIG_DIR:-/etc/maintainarr}"
ENV_FILE="$CONFIG_DIR/maintainarr.env"
SYSTEMD_UNIT="/etc/systemd/system/maintainarr.service"
UPDATE_SCRIPT="/usr/local/bin/maintainarr-update"
CRON_FILE="/etc/cron.d/maintainarr-update"
GO_VERSION="${MAINTAINARR_GO_VERSION:-1.26.4}"
INSTALL_COMMAND="curl -fsSL https://dock-it.dev/Idea-Studios/Maintainarr/raw/branch/main/install.sh | sudo bash"

log() {
  printf '[maintainarr] %s\n' "$*"
}

fail() {
  printf '[maintainarr] ERROR: %s\n' "$*" >&2
  exit 1
}

require_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    fail "Run this installer as root, for example: curl -fsSL ... | sudo bash"
  fi
}

command_exists() {
  command -v "$1" >/dev/null 2>&1
}

apt_package_installed() {
  dpkg -s "$1" >/dev/null 2>&1
}

version_ge() {
  [[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" == "$2" ]]
}

detect_arch() {
  case "$(uname -m)" in
    x86_64|amd64)
      echo "amd64"
      ;;
    aarch64|arm64)
      echo "arm64"
      ;;
    *)
      fail "Unsupported architecture: $(uname -m)"
      ;;
  esac
}

pkg_install() {
  if command_exists apt-get; then
    export DEBIAN_FRONTEND=noninteractive
    apt-get install -y "$@"
    return
  fi

  if command_exists dnf; then
    dnf install -y "$@"
    return
  fi

  if command_exists yum; then
    yum install -y "$@"
    return
  fi

  if command_exists pacman; then
    pacman -Sy --noconfirm "$@"
    return
  fi

  if command_exists zypper; then
    zypper --non-interactive install "$@"
    return
  fi

  if command_exists apk; then
    apk add --no-cache "$@"
    return
  fi

  fail "No supported package manager found"
}

install_base_packages() {
  if command_exists apt-get; then
    local packages=()
    local package

    for package in ca-certificates curl git tar cron; do
      if ! apt_package_installed "$package"; then
        packages+=("$package")
      fi
    done

    if [[ "${#packages[@]}" -eq 0 ]]; then
      log "System packages already present: ca-certificates curl git tar cron"
      return
    fi

    log "Installing missing system packages: ${packages[*]}"
    export DEBIAN_FRONTEND=noninteractive
    apt-get update
    apt-get install -y "${packages[@]}"
    return
  fi

  if command_exists dnf; then
    pkg_install ca-certificates curl git tar cronie
    return
  fi

  if command_exists yum; then
    pkg_install ca-certificates curl git tar cronie
    return
  fi

  if command_exists pacman; then
    pkg_install ca-certificates curl git tar cronie
    return
  fi

  if command_exists zypper; then
    pkg_install ca-certificates curl git tar cron
    return
  fi

  if command_exists apk; then
    pkg_install ca-certificates curl git tar dcron
    return
  fi

  fail "Unable to install prerequisites for this distribution"
}

install_go() {
  local current_version=""

  if command_exists go; then
    current_version="$(go version | awk '{print $3}' | sed 's/^go//')"
  elif [[ -x /usr/local/go/bin/go ]]; then
    current_version="$(/usr/local/go/bin/go version | awk '{print $3}' | sed 's/^go//')"
  fi

  if [[ -n "$current_version" ]] && version_ge "$current_version" "$GO_VERSION"; then
    log "Go $current_version already satisfies the requirement"
    return
  fi

  local arch
  arch="$(detect_arch)"
  local archive="/tmp/go${GO_VERSION}.linux-${arch}.tar.gz"

  log "Installing Go $GO_VERSION"
  curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${arch}.tar.gz" -o "$archive"
  rm -rf /usr/local/go
  tar -C /usr/local -xzf "$archive"
  ln -sf /usr/local/go/bin/go /usr/local/bin/go
  ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
}

tty_available() {
  [[ -r /dev/tty && -w /dev/tty ]]
}

prompt_yes_no() {
  local prompt="$1"
  local default_answer="$2"
  local reply=""
  local suffix="[y/N]"

  if [[ "$default_answer" == "y" ]]; then
    suffix="[Y/n]"
  fi

  if ! tty_available; then
    [[ "$default_answer" == "y" ]]
    return
  fi

  while true; do
    printf '%s %s ' "$prompt" "$suffix" > /dev/tty
    IFS= read -r reply < /dev/tty || true
    reply="${reply:-$default_answer}"

    case "$reply" in
      y|Y|yes|YES)
        return 0
        ;;
      n|N|no|NO)
        return 1
        ;;
      *)
        printf 'Please answer yes or no.\n' > /dev/tty
        ;;
    esac
  done
}

ensure_user() {
  if id -u "$APP_USER" >/dev/null 2>&1; then
    return
  fi

  local nologin_shell
  nologin_shell="$(command -v nologin || true)"
  nologin_shell="${nologin_shell:-/usr/sbin/nologin}"
  useradd --system --home "$INSTALL_ROOT" --create-home --shell "$nologin_shell" "$APP_USER"
}

clone_or_update_repo() {
  mkdir -p "$INSTALL_ROOT" "$BIN_DIR"

  if [[ -d "$SRC_DIR/.git" ]]; then
    log "Updating source in $SRC_DIR"
    git -C "$SRC_DIR" fetch --tags origin
    git -C "$SRC_DIR" checkout "$REPO_BRANCH"
    git -C "$SRC_DIR" pull --ff-only origin "$REPO_BRANCH"
    return
  fi

  if [[ -f "$SRC_DIR/go.mod" && -d "$SRC_DIR/cmd/maintainarr" ]]; then
    log "Existing Maintainarr source found in $SRC_DIR; skipping download"
    return
  fi

  log "Cloning source into $SRC_DIR"
  git clone --branch "$REPO_BRANCH" --single-branch "$REPO_URL" "$SRC_DIR"
}

build_binary() {
  log "Building Maintainarr"
  mkdir -p "$BIN_DIR"
  (
    cd "$SRC_DIR"
    export PATH="/usr/local/go/bin:/usr/local/bin:$PATH"
    export CGO_ENABLED=0
    go build -o "$BIN_PATH" ./cmd/maintainarr
  )
  chmod 755 "$BIN_PATH"
}

random_hex() {
  local bytes="$1"
  od -An -N"$bytes" -tx1 /dev/urandom | tr -d ' \n'
}

env_value() {
  local key="$1"
  awk -F= -v target="$key" '$1 == target { sub(/^[^=]+=*/, "", $0); print $0; exit }' "$ENV_FILE"
}

extract_port() {
  local addr="$1"

  if [[ "$addr" =~ :([0-9]+)$ ]]; then
    echo "${BASH_REMATCH[1]}"
    return
  fi

  echo "8080"
}

detect_local_ip() {
  local ip=""

  if command_exists hostname; then
    ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
  fi

  if [[ -z "$ip" ]] && command_exists ip; then
    ip="$(ip route get 1.1.1.1 2>/dev/null | awk '/src/ {for (i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}')"
  fi

  if [[ -z "$ip" ]]; then
    ip="127.0.0.1"
  fi

  echo "$ip"
}

print_summary() {
  local local_ip="$1"
  local port="$2"
  local auto_updates="$3"
  local start_on_boot="$4"
  local addr base_url

  addr="$(env_value "MAINTAINARR_ADDR")"
  base_url="$(env_value "MAINTAINARR_BASE_URL")"

  printf '\n'
  printf 'Maintainarr install complete\n'
  printf 'Local URL: http://%s:%s\n' "$local_ip" "$port"
  printf 'Base URL:  %s\n' "$base_url"
  printf '\n'
  printf 'Settings\n'
  printf 'MAINTAINARR_ADDR=%s\n' "$addr"
  printf 'MAINTAINARR_DB_PATH=%s\n' "$(env_value "MAINTAINARR_DB_PATH")"
  printf 'MAINTAINARR_LOG_ARCHIVE_DIR=%s\n' "$(env_value "MAINTAINARR_LOG_ARCHIVE_DIR")"
  printf 'MAINTAINARR_ORG_NAME=%s\n' "$(env_value "MAINTAINARR_ORG_NAME")"
  printf 'MAINTAINARR_BASE_URL=%s\n' "$base_url"
  printf 'MAINTAINARR_THEME=%s\n' "$(env_value "MAINTAINARR_THEME")"
  printf 'MAINTAINARR_THEME_MODE=%s\n' "$(env_value "MAINTAINARR_THEME_MODE")"
  printf 'MAINTAINARR_REFRESH_CRON=%s\n' "$(env_value "MAINTAINARR_REFRESH_CRON")"
  printf 'AUTO_UPDATES=%s\n' "$auto_updates"
  printf 'START_ON_BOOT=%s\n' "$start_on_boot"
  printf '\n'
  printf 'Paths\n'
  printf 'Config: %s\n' "$ENV_FILE"
  printf 'Data:   %s\n' "$DATA_DIR"
  printf 'Source: %s\n' "$SRC_DIR"
  printf 'Binary: %s\n' "$BIN_PATH"
  printf '\n'
  printf 'Re-run installer\n'
  printf '%s\n' "$INSTALL_COMMAND"
}

write_env_file() {
  mkdir -p "$CONFIG_DIR" "$DATA_DIR/log-archives"

  if [[ -f "$ENV_FILE" ]]; then
    log "Keeping existing environment file at $ENV_FILE"
    return
  fi

  local session_key
  local encryption_key
  session_key="$(random_hex 32)"
  encryption_key="$(random_hex 16)"

  cat > "$ENV_FILE" <<EOF
MAINTAINARR_ADDR=:8080
MAINTAINARR_DB_PATH=$DATA_DIR/maintainarr.db
MAINTAINARR_LOG_ARCHIVE_DIR=$DATA_DIR/log-archives
MAINTAINARR_SESSION_KEY=$session_key
MAINTAINARR_ENCRYPTION_KEY=$encryption_key
MAINTAINARR_ORG_NAME=Maintainarr
MAINTAINARR_BASE_URL=http://localhost:8080
MAINTAINARR_THEME=blue
MAINTAINARR_THEME_MODE=dark
MAINTAINARR_REFRESH_CRON=@every 5s
EOF

  chmod 640 "$ENV_FILE"
}

write_update_script() {
  cat > "$UPDATE_SCRIPT" <<EOF
#!/usr/bin/env bash
set -euo pipefail

export PATH="/usr/local/go/bin:/usr/local/bin:/usr/bin:/bin"

APP_USER="$APP_USER"
SRC_DIR="$SRC_DIR"
BIN_PATH="$BIN_PATH"
REPO_BRANCH="$REPO_BRANCH"

if [[ ! -d "\$SRC_DIR/.git" ]]; then
  echo "Maintainarr source directory not found: \$SRC_DIR" >&2
  exit 1
fi

git -C "\$SRC_DIR" fetch --tags origin
git -C "\$SRC_DIR" checkout "\$REPO_BRANCH"
git -C "\$SRC_DIR" pull --ff-only origin "\$REPO_BRANCH"

(
  cd "\$SRC_DIR"
  export CGO_ENABLED=0
  go build -o "\$BIN_PATH" ./cmd/maintainarr
)

chown "\$APP_USER:\$APP_USER" "\$BIN_PATH"

if command -v systemctl >/dev/null 2>&1 && systemctl is-enabled --quiet maintainarr 2>/dev/null; then
  systemctl restart maintainarr
fi
EOF

  chmod 755 "$UPDATE_SCRIPT"
}

write_systemd_service() {
  cat > "$SYSTEMD_UNIT" <<EOF
[Unit]
Description=Maintainarr service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=$APP_USER
Group=$APP_USER
WorkingDirectory=$SRC_DIR
EnvironmentFile=$ENV_FILE
ExecStart=$BIN_PATH
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
}

enable_start_on_boot() {
  if ! command_exists systemctl; then
    log "systemd is not available; skipping boot-start setup"
    return
  fi

  write_systemd_service
  systemctl daemon-reload
  systemctl enable --now maintainarr
  log "Enabled and started maintainarr.service"
}

configure_auto_updates() {
  write_update_script

  cat > "$CRON_FILE" <<EOF
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
17 3 * * * root $UPDATE_SCRIPT >> /var/log/maintainarr-update.log 2>&1
EOF

  chmod 644 "$CRON_FILE"

  if command_exists systemctl; then
    if systemctl list-unit-files | grep -q '^cron\.service'; then
      systemctl enable --now cron >/dev/null 2>&1 || true
    elif systemctl list-unit-files | grep -q '^crond\.service'; then
      systemctl enable --now crond >/dev/null 2>&1 || true
    fi
  fi

  log "Installed daily update job in $CRON_FILE"
}

main() {
  local auto_updates="disabled"
  local start_on_boot="disabled"
  local addr port local_ip

  require_root
  install_base_packages
  install_go
  ensure_user
  clone_or_update_repo
  write_env_file
  build_binary
  write_update_script

  mkdir -p "$DATA_DIR"
  chown -R "$APP_USER:$APP_USER" "$INSTALL_ROOT" "$DATA_DIR"
  chown root:"$APP_USER" "$ENV_FILE"

  if prompt_yes_no "Enable automatic daily updates with cron?" "n"; then
    configure_auto_updates
    auto_updates="enabled"
  else
    rm -f "$CRON_FILE"
    log "Skipped cron auto-update setup"
  fi

  if prompt_yes_no "Start Maintainarr at boot with systemd?" "y"; then
    enable_start_on_boot
    start_on_boot="enabled"
  else
    log "Skipped boot-start setup"
    log "Run it manually with: $BIN_PATH"
  fi

  addr="$(env_value "MAINTAINARR_ADDR")"
  port="$(extract_port "$addr")"
  local_ip="$(detect_local_ip)"

  print_summary "$local_ip" "$port" "$auto_updates" "$start_on_boot"
}

main "$@"
