#!/bin/bash
#
# cockpit-client-launcher
#
# Copyright (C) Lubos Kocman <lubos.kocman@suse.com>
# Copyright (C) 2026 openSUSE and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# SPDX-License-Identifier: LGPL-2.1-or-later
#

set -euo pipefail

SOCKET_UNIT="cockpit.socket"
FIREWALL_PORT_DEFAULT="9090"
ZENITY_BIN="${ZENITY_BIN:-zenity}"

# Allow override for debugging
port="${COCKPIT_CLIENT_PORT:-}"

ICON_LOCAL="$(dirname "$0")/cockpit-icon-y2-colors.svg"
ICON_SYSTEM="/usr/share/icons/hicolor/scalable/apps/cockpit-client-launcher.svg"

if [[ -f "$ICON_LOCAL" ]]; then
  ZENITY_ICON="$ICON_LOCAL"
elif [[ -f "$ICON_SYSTEM" ]]; then
  ZENITY_ICON="$ICON_SYSTEM"
else
  ZENITY_ICON="cockpit-client-launcher"  # fallback to theme icon name
fi

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

zenity_question() {
  # args: title, width, text
  local title="$1" width="$2" text="$3"
  if ! have_zenity; then
    return 1
  fi
  "$ZENITY_BIN" --question \
    --window-icon="$ZENITY_ICON" \
    --title="$title" \
    --width="$width" \
    --text="$text"
}

zenity_error() {
  # args: title, width, text
  local title="$1" width="$2" text="$3"
  if ! have_zenity; then
    echo "Error: $title" >&2
    echo -e "$text" >&2
    return 0
  fi
  "$ZENITY_BIN" --error \
    --window-icon="$ZENITY_ICON" \
    --title="$title" \
    --width="$width" \
    --text="$text"
}

socket_active() {
  systemctl is-active --quiet "$SOCKET_UNIT"
}

start_socket_prompt() {
  zenity_question \
    "Start Cockpit?" \
    440 \
    "Cockpit isn’t running.\n\nStart it now?\n\nYou may be asked for admin authentication multiple times.\nOnce for cockpit socket enablement and later for firewall configuration."
}

start_socket() {
  if command -v pkexec >/dev/null 2>&1; then
    pkexec systemctl enable --now "$SOCKET_UNIT"
    return 0
  fi

  # Desktop launcher: don't rely on sudo prompting in a terminal
  return 1
}

detect_port_from_socket() {
  # sets global $port if detectable
  local listen
  listen="$(systemctl show "$SOCKET_UNIT" -p Listen --value 2>/dev/null || true)"
  if [[ -z "$listen" ]]; then
    return 0
  fi

  # Strip trailing " (Stream)" etc.
  listen="${listen%% *}"

  if [[ "$listen" =~ ^[0-9]+$ ]]; then
    port="$listen"
  else
    port="${listen##*:}"
  fi
}

firewalld_available() {
  command -v firewall-cmd >/dev/null 2>&1
}

cockpit_exposed_in_firewalld() {
  local p="${port:-$FIREWALL_PORT_DEFAULT}"

  firewall-cmd --quiet --query-service=cockpit && return 0
  firewall-cmd --quiet --query-port="${p}/tcp" && return 0
  return 1
}

firewall_prompt_keep_open() {
  # OK     -> keep remote access
  # Cancel -> switch to local-only (recommended)
  "$ZENITY_BIN" \
    --question \
    --window-icon="$ZENITY_ICON" \
    --title="Cockpit Network Access" \
    --width=540 \
    --ok-label="Allow Remote Access" \
    --cancel-label="Local Only (Recommended)" \
    --text="Cockpit can currently be accessed from other computers on this network.\n\nHow would you like to use it?\n\nAllowing remote access lets other machines connect to this system.\n\nKeeping it local-only is recommended for most users."
}

close_cockpit_in_firewalld() {
  # Single pkexec to avoid multiple password prompts.
  # Best effort: try removing both service and port; ignore failures.
  local p="${port:-$FIREWALL_PORT_DEFAULT}"

  if command -v pkexec >/dev/null 2>&1; then
    pkexec bash -c "
      set -e
      firewall-cmd --remove-service=cockpit --permanent >/dev/null 2>&1 || true
      firewall-cmd --remove-port='${p}/tcp' --permanent >/dev/null 2>&1 || true
      firewall-cmd --reload >/dev/null 2>&1
    "
    return 0
  fi

  return 1
}

started_by_launcher=0

# --- Ensure cockpit socket is running (or ask to start it) ---
if ! socket_active; then
  if start_socket_prompt; then
    if ! start_socket; then
      zenity_error \
        "Failed to start Cockpit" \
        520 \
        "Could not start ${SOCKET_UNIT} (pkexec not available or authorization failed).\n\nTry in a terminal:\n  sudo systemctl enable --now ${SOCKET_UNIT}\n\nOr check:\n  systemctl status ${SOCKET_UNIT}"
      exit 1
    fi
    started_by_launcher=1
  else
    # User clicked No / cancelled.
    exit 0
  fi
fi

# --- Port detection (your original logic, wrapped) ---
if [[ -z "$port" ]]; then
  detect_port_from_socket
fi

port="${port:-$FIREWALL_PORT_DEFAULT}"

# --- Network access choice: only if we started Cockpit just now ---
if [[ "$started_by_launcher" -eq 1 ]]; then
  if firewalld_available && have_zenity; then
    if cockpit_exposed_in_firewalld; then
      # OK => keep remote access (do nothing)
      # Cancel => switch to local-only
      if ! firewall_prompt_keep_open; then
        if ! close_cockpit_in_firewalld; then
          zenity_error \
            "Network Access Not Changed" \
            520 \
            "I couldn’t adjust the system settings (pkexec not available or authorization failed).\n\nIf you want local-only access, run:\n  sudo firewall-cmd --remove-service=cockpit --permanent\n  sudo firewall-cmd --remove-port=${port}/tcp --permanent\n  sudo firewall-cmd --reload"
        fi
      fi
    fi
  fi
fi

url="http://127.0.0.1:${port}/"
exec /usr/libexec/cockpit-client --external-ws="$url" "$@"