#!/bin/sh
#
# HappyIoT Home Assistant Installation Script
# Version: 1.1.2
#
# This script deploys HappyIoT branding, theming, and OIDC authentication
# integration to a running Home Assistant Docker container.
#
# IMPORTANT BEHAVIOUR:
#   - This installer is intended for a fresh HappyIoT Home Assistant bootstrap.
#   - It replaces configuration.yaml and automations.yaml by design.
#   - If you run it via curl | sh, the script is non-interactive and will not
#     prompt for missing values. In that mode, pass all required options.
#   - If you want interactive prompts, download the script first and run it
#     locally from the Home Assistant host.
#
# QUICK START (from Home Assistant host):
#
#   # 1) Interactive dry-run (recommended first test)
#   curl -fsSLO https://iot.kioskterminal.net/happyiot/happyiot-install.sh
#   chmod +x happyiot-install.sh
#   sudo ./happyiot-install.sh --dry-run --verbose
#
#   # 2) Interactive installation
#   sudo ./happyiot-install.sh --verbose
#
#   # 3) Fully non-interactive dry-run
#   curl -fsSL https://iot.kioskterminal.net/happyiot/happyiot-install.sh | sudo sh -s -- #     --dry-run #     --verbose #     --yes #     --container-name homeassistant #     --config-dir /opt/homeassistant/config #     --instance-url https://homeassistant.happyiot.com #     --oidc-client-id YOUR_CLIENT_ID #     --oidc-discovery-url https://login.happyiot.com/application/o/home-assistant/.well-known/openid-configuration #     --favicon-base-url https://iot.kioskterminal.net/happyiot/favicons #     --logo-url https://iot.kioskterminal.net/happyiot_logo_square.png #     --background-url https://iot.kioskterminal.net/happyiot11_background_no_logo.png
#
#   # 4) Fully non-interactive installation
#   curl -fsSL https://iot.kioskterminal.net/happyiot/happyiot-install.sh | sudo sh -s -- #     --verbose #     --yes #     --container-name homeassistant #     --config-dir /opt/homeassistant/config #     --instance-url https://homeassistant.happyiot.com #     --oidc-client-id YOUR_CLIENT_ID #     --oidc-discovery-url https://login.happyiot.com/application/o/home-assistant/.well-known/openid-configuration #     --favicon-base-url https://iot.kioskterminal.net/happyiot/favicons #     --logo-url https://iot.kioskterminal.net/happyiot_logo_square.png #     --background-url https://iot.kioskterminal.net/happyiot11_background_no_logo.png
#
# USAGE (if running locally):
#   ./happyiot-install.sh [OPTIONS]
#
# OPTIONS:
#   --help                    Show help message and exit
#   --version                 Show script version and exit
#   --dry-run                 Run without applying persistent HA/config changes
#   --verbose                 Enable verbose logging
#   --no-backup               Skip creating backups (NOT RECOMMENDED)
#   --yes                     Run non-interactively and skip confirmation prompt
#   --container-name NAME     Home Assistant container name
#   --config-dir PATH         Home Assistant config directory on host
#   --instance-url URL        Home Assistant instance URL for cors_allowed_origins
#   --oidc-client-id ID       Authentik OIDC client ID
#   --oidc-discovery-url URL  OIDC discovery URL
#   --favicon-base-url URL    Base URL for favicon assets
#   --logo-url URL            Logo URL for login branding
#   --background-url URL      Background image URL for login branding
#
# BEFORE RUNNING:
#   1. Run this on the Home Assistant host.
#   2. Home Assistant container must be running.
#   3. Config directory must exist and be writable by the user running the script.
#   4. Have the Authentik OIDC Client ID ready.
#   5. Internet access is required for branding asset downloads.
#   6. STRONGLY RECOMMENDED: Run with --dry-run first.
#
# REQUIREMENTS & PREREQUISITES:
#   IMPORTANT - This script assumes you have already:
#
#   1. Completed the HappyIoT setup guide up to Home Assistant deployment.
#   2. Installed HACS (Home Assistant Community Store).
#   3. Installed the auth_oidc integration via HACS.
#      Integration: hass-oidc-auth
#      Author: christiaangoossens
#      GitHub: https://github.com/christiaangoossens/hass-oidc-auth
#
# WHAT IT DOES:
#   - Creates configuration.yaml with auth_oidc integration.
#   - Creates automations.yaml and the LightBrand theme.
#   - Installs the HappyIoT HelpScout beacon JavaScript.
#   - Downloads HappyIoT favicon assets.
#   - Patches Home Assistant login branding.
#   - Patches Home Assistant local auth title branding.
#   - Patches auth_oidc templates when present.
#   - Creates timestamped backups of modified files before changes are applied.
#   - Performs YAML syntax validation and best-effort Home Assistant config validation.
#   - Restarts the Home Assistant container.
#
# BACKUPS:
#   Backups are created inside the Home Assistant config directory at:
#   /config/.happyiot-backup-YYYYMMDD-HHMMSS/
#
#   You can inspect or restore from backups using:
#   curl -fsSL https://iot.kioskterminal.net/happyiot/rollback-happyiot-install.sh | sh -s -- --list
#   curl -fsSL https://iot.kioskterminal.net/happyiot/rollback-happyiot-install.sh | sh -s -- --dry-run
#   curl -fsSL https://iot.kioskterminal.net/happyiot/rollback-happyiot-install.sh | sh

set -eu

SCRIPT_VERSION="1.1.0"

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

warn() {
  printf '%s\n' "[WARN] $*" >&2
}

debug() {
  if [ "${VERBOSE:-0}" -eq 1 ]; then
    printf '%s\n' "[DEBUG] $*" >&2
  fi
}

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

need_cmd() {
  command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"
}

is_tty() {
  [ -t 0 ] && [ -t 1 ]
}

prompt_value() {
  var_name="$1"
  prompt_text="$2"
  default_value="${3:-}"
  value=""

  if ! is_tty; then
    die "Missing required option for non-interactive execution: $var_name"
  fi

  while :; do
    if [ -n "$default_value" ]; then
      printf '%s' "$prompt_text [$default_value]: "
    else
      printf '%s' "$prompt_text: "
    fi
    IFS= read -r value || true
    if [ -z "$value" ] && [ -n "$default_value" ]; then
      value="$default_value"
    fi
    if [ -n "$value" ]; then
      eval "$var_name=\$value"
      export "$var_name"
      return 0
    fi
    warn "Value cannot be empty."
  done
}

ensure_value() {
  var_name="$1"
  prompt_text="$2"
  default_value="${3:-}"
  eval "current_value=\${$var_name:-}"
  if [ -n "$current_value" ]; then
    return 0
  fi
  prompt_value "$var_name" "$prompt_text" "$default_value"
}

confirm() {
  if [ "${ASSUME_YES:-0}" -eq 1 ]; then
    return 0
  fi
  if ! is_tty; then
    die "Refusing to prompt for confirmation in non-interactive mode. Re-run with --yes."
  fi
  printf '%s' "$1 [y/N]: "
  IFS= read -r reply || true
  case "$reply" in
    y|Y|yes|YES) return 0 ;;
    *) return 1 ;;
  esac
}

run_cmd() {
  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would execute: $*"
    return 0
  fi
  debug "Executing: $*"
  "$@"
}

run_sh() {
  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would execute shell command: $*"
    return 0
  fi
  debug "Executing shell command: $*"
  sh -c "$*"
}

backup_file() {
  src="$1"
  dest="$2"
  if [ "$SKIP_BACKUP" -eq 1 ]; then
    log "Skipping backup of $src (--no-backup)"
    return 0
  fi
  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would backup: $src -> $dest"
    return 0
  fi
  if [ -f "$src" ]; then
    mkdir -p "$(dirname "$dest")"
    cp -a "$src" "$dest"
    log "Backed up $src"
  fi
}

backup_dir() {
  src="$1"
  dest="$2"
  if [ "$SKIP_BACKUP" -eq 1 ]; then
    log "Skipping backup of $src (--no-backup)"
    return 0
  fi
  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would backup: $src -> $dest"
    return 0
  fi
  if [ -d "$src" ]; then
    mkdir -p "$(dirname "$dest")"
    cp -a "$src" "$dest"
    log "Backed up $src"
  fi
}

download_file() {
  src_url="$1"
  dest_file="$2"

  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would download: $src_url -> $dest_file"
    return 0
  fi

  if command -v wget >/dev/null 2>&1; then
    wget -q -O "$dest_file" "$src_url" || return 1
  else
    curl -fsSL "$src_url" -o "$dest_file" || return 1
  fi
}

cleanup() {
  if [ -n "${WORKDIR:-}" ] && [ -d "${WORKDIR:-}" ]; then
    rm -rf "$WORKDIR"
  fi
}
trap cleanup EXIT INT TERM

usage() {
  cat <<'USAGE_EOF'
HappyIoT Home Assistant Installation Script

Usage: happyiot-install.sh [OPTIONS]

Options:
  --help                    Show this help message and exit
  --version                 Show script version and exit
  --dry-run                 Run without making any changes (test mode)
  --verbose                 Enable verbose logging
  --no-backup               Skip creating backups (not recommended)
  --yes                     Run non-interactively and skip confirmation prompt
  --container-name NAME     Home Assistant container name
  --config-dir PATH         Home Assistant config directory on host
  --instance-url URL        Home Assistant instance URL for cors_allowed_origins
  --oidc-client-id ID       Authentik OIDC client ID
  --oidc-discovery-url URL  OIDC discovery URL
  --favicon-base-url URL    Base URL for favicon assets
  --logo-url URL            Logo URL for login branding
  --background-url URL      Background image URL for login branding

Examples:
  happyiot-install.sh
  happyiot-install.sh --dry-run
  happyiot-install.sh --verbose
  happyiot-install.sh --yes --container-name homeassistant --config-dir /opt/homeassistant/config --oidc-client-id YOUR_CLIENT_ID
USAGE_EOF
}

docker_exec_wrap() {
  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would execute: docker exec $*"
    return 0
  fi
  debug "Executing: docker exec $*"
  docker exec "$@"
}

docker_cp_wrap() {
  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would execute: docker cp $*"
    return 0
  fi
  debug "Executing: docker cp $*"
  docker cp "$@"
}

docker_restart_wrap() {
  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would execute: docker restart $*"
    return 0
  fi
  debug "Executing: docker restart $*"
  docker restart "$@"
}

check_url() {
  url="$1"
  if command -v curl >/dev/null 2>&1; then
    curl -fsSLI --connect-timeout 5 "$url" >/dev/null 2>&1 && return 0 || return 1
  fi
  if command -v wget >/dev/null 2>&1; then
    wget -q --spider --timeout=5 "$url" >/dev/null 2>&1 && return 0 || return 1
  fi
  return 0
}

get_disk_usage_percent() {
  dir="$1"
  df "$dir" | awk 'NR==2 {print $5}' | sed 's/%$//'
}

validate_yaml_syntax() {
  file="$1"
  if command -v python3 >/dev/null 2>&1; then
    if python3 - "$file" >/dev/null 2>&1 <<'PY'
import sys
try:
    import yaml
except Exception:
    raise SystemExit(2)

class Loader(yaml.SafeLoader):
    pass

def _unknown(loader, tag_suffix, node):
    if isinstance(node, yaml.ScalarNode):
        return loader.construct_scalar(node)
    if isinstance(node, yaml.SequenceNode):
        return loader.construct_sequence(node)
    if isinstance(node, yaml.MappingNode):
        return loader.construct_mapping(node)
    return None

Loader.add_multi_constructor('!', _unknown)

with open(sys.argv[1], 'r', encoding='utf-8') as fh:
    yaml.load(fh, Loader=Loader)
PY
    then
      return 0
    else
      rc=$?
      if [ "$rc" -eq 2 ]; then
        warn "Host python3 is available but PyYAML is not installed; skipping YAML syntax validation for $file"
        return 0
      fi
      return 1
    fi
  fi
  warn "python3 not available on host; skipping YAML syntax validation for $file"
  return 0
}

validate_no_newlines() {
  value_name="$1"
  value="$2"
  case "$value" in
    *'
'*) die "$value_name must not contain newline characters" ;;
  esac
}

validate_no_single_quotes() {
  value_name="$1"
  value="$2"
  case "$value" in
    *\'*) die "$value_name must not contain single quote characters" ;;
  esac
}

validate_required_url_like() {
  value_name="$1"
  value="$2"
  case "$value" in
    http://*|https://*) return 0 ;;
    *) die "$value_name must start with http:// or https://" ;;
  esac
}

validate_container_name() {
  value="$1"
  case "$value" in
    *[!A-Za-z0-9_.-]*|'') die "CONTAINER_NAME contains unsupported characters" ;;
  esac
}

validate_simple_token() {
  value_name="$1"
  value="$2"
  validate_no_newlines "$value_name" "$value"
}

validate_ha_config_best_effort() {
  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would validate Home Assistant configuration in container"
    return 0
  fi

  if docker_exec_wrap "$CONTAINER_NAME" sh -c 'python3 -m homeassistant --script check_config --config /config' >/tmp/happyiot-ha-check.$$ 2>&1; then
    rm -f /tmp/happyiot-ha-check.$$
    log "Home Assistant configuration check passed"
    return 0
  fi

  warn "Home Assistant configuration check reported issues or is unavailable. Review container output below:"
  sed 's/^/[WARN] /' /tmp/happyiot-ha-check.$$ >&2 || true
  rm -f /tmp/happyiot-ha-check.$$
  return 0
}

require_nonempty() {
  value_name="$1"
  value="$2"
  [ -n "$value" ] || die "$value_name cannot be empty"
}

write_text_file() {
  dest_file="$1"
  src_file="$2"
  if [ "$DRY_RUN" -eq 1 ]; then
    log "[DRY-RUN] Would write file: $dest_file"
    return 0
  fi
  debug "Writing file: $dest_file from $src_file"
  cp "$src_file" "$dest_file"
}

DRY_RUN=0
VERBOSE=0
SKIP_BACKUP=0
ASSUME_YES=0

CONTAINER_NAME=""
CONFIG_DIR=""
INSTANCE_URL=""
OIDC_CLIENT_ID=""
OIDC_DISCOVERY_URL=""
FAVICON_BASE_URL=""
LOGO_URL=""
BACKGROUND_URL=""

while [ $# -gt 0 ]; do
  case "$1" in
    --help)
      usage
      exit 0
      ;;
    --version)
      printf '%s\n' "$SCRIPT_VERSION"
      exit 0
      ;;
    --dry-run)
      DRY_RUN=1
      log "DRY-RUN MODE: No persistent changes will be applied to Home Assistant or its config"
      shift
      ;;
    --verbose)
      VERBOSE=1
      log "VERBOSE MODE: Enabled"
      shift
      ;;
    --no-backup)
      SKIP_BACKUP=1
      warn "Backups will be skipped (NOT RECOMMENDED)"
      shift
      ;;
    --yes)
      ASSUME_YES=1
      shift
      ;;
    --container-name)
      [ $# -ge 2 ] || die "Missing value for --container-name"
      CONTAINER_NAME="$2"
      shift 2
      ;;
    --container-name=*)
      CONTAINER_NAME=${1#*=}
      shift
      ;;
    --config-dir)
      [ $# -ge 2 ] || die "Missing value for --config-dir"
      CONFIG_DIR="$2"
      shift 2
      ;;
    --config-dir=*)
      CONFIG_DIR=${1#*=}
      shift
      ;;
    --instance-url)
      [ $# -ge 2 ] || die "Missing value for --instance-url"
      INSTANCE_URL="$2"
      shift 2
      ;;
    --instance-url=*)
      INSTANCE_URL=${1#*=}
      shift
      ;;
    --oidc-client-id)
      [ $# -ge 2 ] || die "Missing value for --oidc-client-id"
      OIDC_CLIENT_ID="$2"
      shift 2
      ;;
    --oidc-client-id=*)
      OIDC_CLIENT_ID=${1#*=}
      shift
      ;;
    --oidc-discovery-url)
      [ $# -ge 2 ] || die "Missing value for --oidc-discovery-url"
      OIDC_DISCOVERY_URL="$2"
      shift 2
      ;;
    --oidc-discovery-url=*)
      OIDC_DISCOVERY_URL=${1#*=}
      shift
      ;;
    --favicon-base-url)
      [ $# -ge 2 ] || die "Missing value for --favicon-base-url"
      FAVICON_BASE_URL="$2"
      shift 2
      ;;
    --favicon-base-url=*)
      FAVICON_BASE_URL=${1#*=}
      shift
      ;;
    --logo-url)
      [ $# -ge 2 ] || die "Missing value for --logo-url"
      LOGO_URL="$2"
      shift 2
      ;;
    --logo-url=*)
      LOGO_URL=${1#*=}
      shift
      ;;
    --background-url)
      [ $# -ge 2 ] || die "Missing value for --background-url"
      BACKGROUND_URL="$2"
      shift 2
      ;;
    --background-url=*)
      BACKGROUND_URL=${1#*=}
      shift
      ;;
    *)
      die "Unknown option: $1"
      ;;
  esac
done

need_cmd sh
need_cmd docker
need_cmd cp
need_cmd mkdir
need_cmd sed
need_cmd grep
need_cmd mktemp
need_cmd find
need_cmd date
need_cmd tail
need_cmd tr
need_cmd wc
need_cmd df
need_cmd awk
need_cmd rm
need_cmd chmod

if ! command -v wget >/dev/null 2>&1 && ! command -v curl >/dev/null 2>&1; then
  die "Need either wget or curl installed."
fi

DEFAULT_CONTAINER_NAME="homeassistant"
DEFAULT_CONFIG_DIR="/opt/homeassistant/config"
DEFAULT_INSTANCE_URL="https://homeassistant.happyiot.com"
DEFAULT_OIDC_DISCOVERY_URL="https://login.happyiot.com/application/o/home-assistant/.well-known/openid-configuration"
DEFAULT_ASSET_BASE_URL="https://iot.kioskterminal.net/happyiot/favicons"
DEFAULT_LOGO_URL="https://iot.kioskterminal.net/happyiot_logo_square.png"
DEFAULT_BACKGROUND_URL="https://iot.kioskterminal.net/happyiot11_background_no_logo.png"

ensure_value CONTAINER_NAME "Home Assistant container name" "$DEFAULT_CONTAINER_NAME"
ensure_value CONFIG_DIR "Home Assistant config directory on host" "$DEFAULT_CONFIG_DIR"
ensure_value INSTANCE_URL "Home Assistant instance URL to allow in cors_allowed_origins" "$DEFAULT_INSTANCE_URL"
ensure_value OIDC_CLIENT_ID "Authentik OIDC Client ID"
ensure_value OIDC_DISCOVERY_URL "OIDC discovery URL" "$DEFAULT_OIDC_DISCOVERY_URL"
ensure_value FAVICON_BASE_URL "Favicon base URL" "$DEFAULT_ASSET_BASE_URL"
ensure_value LOGO_URL "Logo URL for login branding" "$DEFAULT_LOGO_URL"
ensure_value BACKGROUND_URL "Background image URL for login branding" "$DEFAULT_BACKGROUND_URL"

require_nonempty "OIDC Client ID" "$OIDC_CLIENT_ID"
[ -d "$CONFIG_DIR" ] || die "Config directory does not exist: $CONFIG_DIR"
[ -w "$CONFIG_DIR" ] || die "Config directory is not writable: $CONFIG_DIR"

validate_container_name "$CONTAINER_NAME"
validate_simple_token "CONFIG_DIR" "$CONFIG_DIR"
validate_simple_token "OIDC_CLIENT_ID" "$OIDC_CLIENT_ID"
validate_required_url_like "INSTANCE_URL" "$INSTANCE_URL"
validate_required_url_like "OIDC_DISCOVERY_URL" "$OIDC_DISCOVERY_URL"
validate_required_url_like "FAVICON_BASE_URL" "$FAVICON_BASE_URL"
validate_required_url_like "LOGO_URL" "$LOGO_URL"
validate_required_url_like "BACKGROUND_URL" "$BACKGROUND_URL"
validate_no_newlines "INSTANCE_URL" "$INSTANCE_URL"
validate_no_newlines "OIDC_DISCOVERY_URL" "$OIDC_DISCOVERY_URL"
validate_no_newlines "FAVICON_BASE_URL" "$FAVICON_BASE_URL"
validate_no_newlines "LOGO_URL" "$LOGO_URL"
validate_no_newlines "BACKGROUND_URL" "$BACKGROUND_URL"
validate_no_single_quotes "INSTANCE_URL" "$INSTANCE_URL"
validate_no_single_quotes "OIDC_CLIENT_ID" "$OIDC_CLIENT_ID"
validate_no_single_quotes "OIDC_DISCOVERY_URL" "$OIDC_DISCOVERY_URL"
validate_no_single_quotes "FAVICON_BASE_URL" "$FAVICON_BASE_URL"
validate_no_single_quotes "LOGO_URL" "$LOGO_URL"
validate_no_single_quotes "BACKGROUND_URL" "$BACKGROUND_URL"

if ! docker ps --format '{{.Names}}' | grep -Fxq "$CONTAINER_NAME"; then
  docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}' >&2 || true
  die "Home Assistant container is not running: $CONTAINER_NAME"
fi

log "HappyIoT installer version: $SCRIPT_VERSION"
log "Running pre-flight checks..."
debug "Resolved container name: $CONTAINER_NAME"
debug "Resolved config directory: $CONFIG_DIR"
debug "Resolved instance URL: $INSTANCE_URL"
debug "Resolved OIDC discovery URL: $OIDC_DISCOVERY_URL"
debug "Resolved favicon base URL: $FAVICON_BASE_URL"
debug "Resolved logo URL: $LOGO_URL"
debug "Resolved background URL: $BACKGROUND_URL"

log "Checking network connectivity to asset servers..."
for url in "$FAVICON_BASE_URL" "$LOGO_URL" "$BACKGROUND_URL" "$OIDC_DISCOVERY_URL"; do
  if ! check_url "$url"; then
    warn "Could not reach $url (may fail during download)"
  fi
done

DISK_USAGE=$(get_disk_usage_percent "$CONFIG_DIR")
if [ "$DISK_USAGE" -gt 85 ]; then
  warn "Disk usage is high ($DISK_USAGE%)"
fi

if [ -f "$CONFIG_DIR/configuration.yaml" ] && grep -q "auth_oidc:" "$CONFIG_DIR/configuration.yaml" 2>/dev/null; then
  log "auth_oidc configuration already present (file will still be replaced by design)"
fi

WORKDIR=$(mktemp -d /tmp/happyiot-install.XXXXXX)
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="$CONFIG_DIR/.happyiot-backup-$TIMESTAMP"

if [ "$DRY_RUN" -eq 1 ]; then
  log "[DRY-RUN] Would create backup directory: $BACKUP_DIR"
else
  mkdir -p "$BACKUP_DIR"
fi

backup_file "$CONFIG_DIR/configuration.yaml" "$BACKUP_DIR/configuration.yaml"
backup_file "$CONFIG_DIR/automations.yaml" "$BACKUP_DIR/automations.yaml"
backup_file "$CONFIG_DIR/www/happyiot-helpscout-beacon.js" "$BACKUP_DIR/www/happyiot-helpscout-beacon.js"
backup_dir "$CONFIG_DIR/themes" "$BACKUP_DIR/themes"
backup_dir "$CONFIG_DIR/www/favicons" "$BACKUP_DIR/www/favicons"
if [ -d "$CONFIG_DIR/custom_components/auth_oidc/views/templates" ]; then
  backup_dir "$CONFIG_DIR/custom_components/auth_oidc/views/templates" "$BACKUP_DIR/auth_oidc_templates"
else
  warn "OIDC templates directory not found (auth_oidc not yet deployed): $CONFIG_DIR/custom_components/auth_oidc/views/templates"
fi

docker_cp_wrap "$CONTAINER_NAME:/usr/src/homeassistant/homeassistant/auth/providers/homeassistant.py" "$BACKUP_DIR/homeassistant.py" >/dev/null 2>&1 || warn "Failed to backup homeassistant.py"

log "Extracting authorize.html path from container"
if [ "$DRY_RUN" -eq 1 ]; then
  log "[DRY-RUN] Would determine authorize.html path from container"
else
  if docker_exec_wrap "$CONTAINER_NAME" python3 -c "from pathlib import Path; import hass_frontend; p = Path(hass_frontend.__file__).parent / 'authorize.html'; print(p)" > "$WORKDIR/authorize_path.txt" 2>/dev/null; then
    :
  else
    warn "Failed to determine authorize.html path from container"
  fi

  if [ -s "$WORKDIR/authorize_path.txt" ]; then
    AUTHORIZE_PATH=$(tail -n 1 "$WORKDIR/authorize_path.txt" | tr -d '\r')
    debug "Resolved authorize.html path: $AUTHORIZE_PATH"
    docker_cp_wrap "$CONTAINER_NAME:$AUTHORIZE_PATH" "$BACKUP_DIR/authorize.html" >/dev/null 2>&1 || true
  fi
fi

cat <<SUMMARY_EOF

Summary
-------
Container name      : $CONTAINER_NAME
Config directory    : $CONFIG_DIR
Instance URL        : $INSTANCE_URL
OIDC Client ID      : $OIDC_CLIENT_ID
OIDC Discovery URL  : $OIDC_DISCOVERY_URL
Favicon base URL    : $FAVICON_BASE_URL
Logo URL            : $LOGO_URL
Background URL      : $BACKGROUND_URL
Backup directory    : $BACKUP_DIR
Dry-run             : $DRY_RUN
SUMMARY_EOF

confirm "Proceed with HappyIoT installation?" || die "Cancelled by user."

log "Creating required directories"
run_cmd mkdir -p "$CONFIG_DIR/themes"
run_cmd mkdir -p "$CONFIG_DIR/www"
run_cmd mkdir -p "$CONFIG_DIR/www/favicons"

cat > "$WORKDIR/configuration.yaml" <<CONFIG_EOF
# Loads default set of integrations. Do not remove.
default_config:

# Load frontend themes from the themes folder
frontend:
  themes: !include_dir_merge_named themes
  extra_module_url:
    - /local/happyiot-helpscout-beacon.js

automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

http:
  server_host: 0.0.0.0
  use_x_forwarded_for: true
  trusted_proxies:
    - 127.0.0.1
  use_x_frame_options: true
  cors_allowed_origins:
    - $INSTANCE_URL

recorder:
  purge_keep_days: 30

auth_oidc:
  client_id: '$OIDC_CLIENT_ID'
  discovery_url: '$OIDC_DISCOVERY_URL'
  display_name: 'HappyIoT'
  features:
    force_https: true
CONFIG_EOF

validate_yaml_syntax "$WORKDIR/configuration.yaml" || die "configuration.yaml syntax validation failed"
write_text_file "$CONFIG_DIR/configuration.yaml" "$WORKDIR/configuration.yaml"
log "Wrote configuration.yaml"

cat > "$WORKDIR/automations.yaml" <<'AUTOMATIONS_EOF'
- alias: Set default theme on startup
  description: Force LightBrand as the default theme when Home Assistant starts
  id: set_default_theme_on_startup
  trigger:
    - platform: homeassistant
      event: start
  action:
    - service: frontend.set_theme
      data:
        name: LightBrand
  mode: single
AUTOMATIONS_EOF
validate_yaml_syntax "$WORKDIR/automations.yaml" || die "automations.yaml syntax validation failed"
write_text_file "$CONFIG_DIR/automations.yaml" "$WORKDIR/automations.yaml"
log "Wrote automations.yaml"

cat > "$WORKDIR/lightbrand.yaml" <<'THEME_EOF'
LightBrand:
  modes:
    light:
      primary-color: "#eb78aa"
      accent-color: "#feca81"
      background-color: "#e4f2ff"
      card-background-color: "#ffffffcc"
      text-color: "#040707"
      app-header-background-color: "#eb78aa"
      app-header-text-color: "#040707"
      ha-card-box-shadow: "0 4px 12px rgba(0,0,0,0.08)"
      ha-card-border-radius: "16px"
      divider-color: "#f2b1cc"
      paper-item-icon-color: "#eb78aa"
THEME_EOF
validate_yaml_syntax "$WORKDIR/lightbrand.yaml" || die "themes/lightbrand.yaml syntax validation failed"
write_text_file "$CONFIG_DIR/themes/lightbrand.yaml" "$WORKDIR/lightbrand.yaml"
log "Wrote themes/lightbrand.yaml"

cat > "$WORKDIR/happyiot-helpscout-beacon.js" <<'BEACON_EOF'
// Make sure you add this into your configuration.yaml before deploying the Beacon, else nothing will happen.
// frontend:
//   extra_module_url:
//     - /local/happyiot-helpscout-beacon.js

(() => {
    const BEACON_ID = "616290f5-c2f6-4b52-abeb-ff73581c7faa";
    const SCRIPT_ID = "happyiot-helpscout-loader";

    if (window.__happyiotHelpScoutLoaded) {
      return;
    }
    window.__happyiotHelpScoutLoaded = true;

    function initBeacon() {
      if (!window.Beacon) return;

      if (window.__happyiotHelpScoutInited) return;
      window.__happyiotHelpScoutInited = true;

      try {
        window.Beacon("init", BEACON_ID);
      } catch (err) {
        console.error("HelpScout Beacon init failed:", err);
      }
    }

    (function (e, t, n) {
      function load() {
        if (t.getElementById(SCRIPT_ID)) {
          initBeacon();
          return;
        }

        const firstScript = t.getElementsByTagName("script")[0];
        const script = t.createElement("script");
        script.id = SCRIPT_ID;
        script.type = "text/javascript";
        script.async = true;
        script.src = "https://beacon-v2.helpscout.net";
        script.onload = initBeacon;

        if (firstScript && firstScript.parentNode) {
          firstScript.parentNode.insertBefore(script, firstScript);
        } else {
          t.head.appendChild(script);
        }
      }

      if (
        e.Beacon =
          n =
          function (method, options, data) {
            e.Beacon.readyQueue.push({
              method,
              options,
              data,
            });
          }
      ) {
        n.readyQueue = [];
      }

      if (t.readyState === "complete") {
        load();
      } else if (e.attachEvent) {
        e.attachEvent("onload", load);
      } else {
        e.addEventListener("load", load, false);
      }
    })(window, document, window.Beacon || function () {});
  })();
BEACON_EOF
write_text_file "$CONFIG_DIR/www/happyiot-helpscout-beacon.js" "$WORKDIR/happyiot-helpscout-beacon.js"
log "Wrote www/happyiot-helpscout-beacon.js"

log "Downloading favicons from $FAVICON_BASE_URL"
if [ "$DRY_RUN" -eq 1 ]; then
  log "[DRY-RUN] Would clear existing favicon files from $CONFIG_DIR/www/favicons"
else
  rm -f "$CONFIG_DIR"/www/favicons/* 2>/dev/null || true
fi

FAVICON_FILES="apple-touch-icon.png favicon-96x96.png favicon.ico favicon.svg site.webmanifest web-app-manifest-192x192.png web-app-manifest-512x512.png"
FAVICON_FAIL_COUNT=0

for file_name in $FAVICON_FILES; do
  if download_file "$FAVICON_BASE_URL/$file_name" "$CONFIG_DIR/www/favicons/$file_name"; then
    log "Downloaded $file_name"
  else
    warn "Failed to download $file_name from $FAVICON_BASE_URL/$file_name"
    FAVICON_FAIL_COUNT=$((FAVICON_FAIL_COUNT + 1))
  fi
done

if [ "$FAVICON_FAIL_COUNT" -gt 0 ]; then
  warn "$FAVICON_FAIL_COUNT favicon files failed to download"
fi

cat > "$WORKDIR/patch_hass_login.sh" <<'PATCH_LOGIN_EOF'
#!/bin/sh
set -eu

python3 - <<'PY'
from pathlib import Path
import os
import hass_frontend

logo_url = os.environ["LOGO_URL"]
background_url = os.environ["BACKGROUND_URL"]
p = Path(hass_frontend.__file__).parent / "authorize.html"
s = p.read_text(encoding="utf-8")

required_strings = [
    "<title>Home Assistant</title>",
    '<link rel="icon" href="/static/icons/favicon.ico">',
    '<img src="/static/icons/favicon-192x192.png" alt="Home Assistant">',
    '<style>html{background-color:var(--primary-background-color,#fafafa);color:var(--primary-text-color,#212121)}@media (prefers-color-scheme:dark){html{background-color:var(--primary-background-color,#111);color:var(--primary-text-color,#e1e1e1)}}body{box-sizing:border-box;padding:32px 0;display:flex;flex-wrap:wrap;align-items:center}.content{width:100%;max-width:400px;margin:0 auto;padding:0 16px;box-sizing:content-box}.header{display:flex;align-items:center;justify-content:center;margin-bottom:32px;padding-top:var(--safe-area-inset-top)}.header img{height:56px;width:56px}</style>'
]
for item in required_strings:
    if item not in s:
        raise SystemExit(f"Expected authorize.html content not found: {item[:120]}")

for suffix in (".br", ".gz"):
    f = p.with_name(p.name + suffix)
    if f.exists():
        f.unlink()

s = s.replace("<title>Home Assistant</title>", "<title>HappyIoT</title>")
s = s.replace(
    '<link rel="icon" href="/static/icons/favicon.ico">',
    f'<link rel="icon" href="{logo_url}">'
)
s = s.replace(
    '<img src="/static/icons/favicon-192x192.png" alt="Home Assistant">',
    f'<img src="{logo_url}" alt="HappyIoT">'
)
s = s.replace(
    '<style>html{background-color:var(--primary-background-color,#fafafa);color:var(--primary-text-color,#212121)}@media (prefers-color-scheme:dark){html{background-color:var(--primary-background-color,#111);color:var(--primary-text-color,#e1e1e1)}}body{box-sizing:border-box;padding:32px 0;display:flex;flex-wrap:wrap;align-items:center}.content{width:100%;max-width:400px;margin:0 auto;padding:0 16px;box-sizing:content-box}.header{display:flex;align-items:center;justify-content:center;margin-bottom:32px;padding-top:var(--safe-area-inset-top)}.header img{height:56px;width:56px}</style>',
    f'<style>html{{background:#000 url("{background_url}") center center / cover no-repeat fixed;color:#111827 !important;min-height:100%;color-scheme:light}}body{{box-sizing:border-box;padding:32px 0;display:flex;flex-wrap:wrap;align-items:center;min-height:100vh;background:transparent;color:#111827 !important}}.content{{width:100%;max-width:400px;margin:0 auto;padding:0 16px;box-sizing:content-box;color:#111827 !important}}.header{{display:flex;align-items:center;justify-content:center;margin-bottom:56px;padding-top:var(--safe-area-inset-top)}}.header img{{height:220px;width:220px;object-fit:contain}}@media (prefers-color-scheme:dark){{html{{background:#000 url("{background_url}") center center / cover no-repeat fixed;color:#111827 !important}}}}ha-authorize{{--primary-background-color:#f5f7fa;--card-background-color:rgba(255,255,255,0.96);--ha-card-background:rgba(255,255,255,0.96);--primary-text-color:#111827;--secondary-text-color:#374151;--mdc-theme-surface:rgba(255,255,255,0.96);--mdc-text-field-fill-color:#ffffff;--mdc-theme-text-primary-on-background:#111827;--mdc-theme-text-secondary-on-background:#374151;--mdc-theme-primary:#111827;--mdc-typography-body1-color:#111827;--mdc-typography-body2-color:#111827;--mdc-typography-subtitle1-color:#111827;--mdc-typography-subtitle2-color:#111827;--mdc-typography-headline6-color:#111827;--mdc-typography-headline5-color:#111827;--mdc-typography-headline4-color:#111827;color:#111827 !important;color-scheme:light}}ha-authorize *{{color:#111827 !important}}ha-authorize input,ha-authorize label,ha-authorize p,ha-authorize span,ha-authorize div,ha-authorize h1,ha-authorize h2,ha-authorize h3,ha-authorize h4,ha-authorize h5,ha-authorize h6,ha-authorize a,ha-authorize button{{color:#111827 !important}}ha-authorize input::placeholder{{color:#6b7280 !important}}</style>'
)

if 'Welcome to HappyIoT!' not in s:
    marker = '</body></html>'
    if marker not in s:
        raise SystemExit('Expected authorize.html closing marker not found')
    s = s.replace(
        marker,
        '''<script>\nwindow.addEventListener("load", () => {\n  const fixHeading = () => {\n    document.querySelectorAll("h1,h2,h3,.title,.mdc-typography--headline4").forEach(el => {\n      if (el.textContent && el.textContent.trim() === "Welcome home!") {\n        el.textContent = "Welcome to HappyIoT!";\n      }\n    });\n  };\n  fixHeading();\n  setInterval(fixHeading,500);\n});\n</script></body></html>'''
    )

p.write_text(s, encoding="utf-8")
print("Patched:", p)
PY
PATCH_LOGIN_EOF
run_cmd chmod +x "$WORKDIR/patch_hass_login.sh"

cat > "$WORKDIR/patch_local_auth_title.sh" <<'PATCH_LOCAL_AUTH_EOF'
#!/bin/sh
set -eu

CONTAINER_NAME=${CONTAINER_NAME:-homeassistant}
TARGET_FILE=/usr/src/homeassistant/homeassistant/auth/providers/homeassistant.py

docker exec -i "$CONTAINER_NAME" sh -c '
set -eu
if grep -q "DEFAULT_TITLE = \"HappyIoT Local\"" "$0"; then
  echo "DEFAULT_TITLE already set to HappyIoT Local"
else
  if ! grep -q "DEFAULT_TITLE = \"Home Assistant Local\"" "$0"; then
    echo "Expected DEFAULT_TITLE marker not found in $0" >&2
    exit 1
  fi
  sed -i "s/DEFAULT_TITLE = \"Home Assistant Local\"/DEFAULT_TITLE = \"HappyIoT Local\"/" "$0"
fi
grep -n "DEFAULT_TITLE" "$0"
' "$TARGET_FILE"
PATCH_LOCAL_AUTH_EOF
run_cmd chmod +x "$WORKDIR/patch_local_auth_title.sh"

cat > "$WORKDIR/patch_oidc_templates_branding.sh" <<'PATCH_OIDC_EOF'
#!/bin/sh
set -eu

CONFIG_DIR=${CONFIG_DIR:-/opt/homeassistant/config}
FILE_1=$CONFIG_DIR/custom_components/auth_oidc/views/templates/finish.html
FILE_2=$CONFIG_DIR/custom_components/auth_oidc/views/templates/welcome.html

for f in "$FILE_1" "$FILE_2"; do
  if [ ! -f "$f" ]; then
    echo "Missing file: $f" >&2
    exit 1
  fi
  if ! grep -q 'Home Assistant' "$f"; then
    echo "Expected Home Assistant marker not found in $f" >&2
    exit 1
  fi
done

sed -i 's/Home Assistant/HappyIoT/g' "$FILE_1" "$FILE_2"
echo "Patched OIDC templates:"
echo "  - $FILE_1"
echo "  - $FILE_2"
PATCH_OIDC_EOF
run_cmd chmod +x "$WORKDIR/patch_oidc_templates_branding.sh"

log "Applying authorize/login branding patch"
if ! docker_cp_wrap "$WORKDIR/patch_hass_login.sh" "$CONTAINER_NAME:/tmp/patch_hass_login.sh" >/dev/null 2>&1; then
  die "Failed to copy patch script to container"
fi
LOGIN_PATCH_LOG="$WORKDIR/patch_hass_login.log"
if docker_exec_wrap -e LOGO_URL="$LOGO_URL" -e BACKGROUND_URL="$BACKGROUND_URL" "$CONTAINER_NAME" sh -c 'chmod +x /tmp/patch_hass_login.sh && /tmp/patch_hass_login.sh' >"$LOGIN_PATCH_LOG" 2>&1; then
  log "Login branding patch applied"
else
  warn "Login branding patch failed. Details:"
  sed 's/^/[WARN] /' "$LOGIN_PATCH_LOG" >&2 || true
  die "Failed to apply login branding patch"
fi

log "Applying local auth title patch"
LOCAL_AUTH_PATCH_LOG="$WORKDIR/patch_local_auth_title.log"
if CONTAINER_NAME="$CONTAINER_NAME" sh "$WORKDIR/patch_local_auth_title.sh" >"$LOCAL_AUTH_PATCH_LOG" 2>&1; then
  log "Local auth title patch applied"
else
  warn "Local auth title patch failed. Details:"
  sed 's/^/[WARN] /' "$LOCAL_AUTH_PATCH_LOG" >&2 || true
fi

if [ -d "$CONFIG_DIR/custom_components/auth_oidc/views/templates" ]; then
  log "Applying OIDC template branding patch"
  OIDC_PATCH_LOG="$WORKDIR/patch_oidc_templates_branding.log"
  if CONFIG_DIR="$CONFIG_DIR" sh "$WORKDIR/patch_oidc_templates_branding.sh" >"$OIDC_PATCH_LOG" 2>&1; then
    log "OIDC template branding patch applied"
  else
    warn "OIDC template branding patch failed. Details:"
    sed 's/^/[WARN] /' "$OIDC_PATCH_LOG" >&2 || true
  fi
else
  log "Skipping OIDC template patch - auth_oidc component not yet deployed"
fi

validate_ha_config_best_effort

log "Restarting Home Assistant container"
docker_restart_wrap "$CONTAINER_NAME" >/dev/null

log "Validating output files"
if [ "$DRY_RUN" -eq 0 ]; then
  [ -f "$CONFIG_DIR/configuration.yaml" ] || die "configuration.yaml missing"
  [ -f "$CONFIG_DIR/automations.yaml" ] || die "automations.yaml missing"
  [ -f "$CONFIG_DIR/themes/lightbrand.yaml" ] || die "themes/lightbrand.yaml missing"
  [ -f "$CONFIG_DIR/www/happyiot-helpscout-beacon.js" ] || die "www/happyiot-helpscout-beacon.js missing"

  count=$(find "$CONFIG_DIR/www/favicons" -maxdepth 1 -type f | wc -l | tr -d ' ')
  if [ "$count" -lt 5 ]; then
    warn "Expected at least 5 favicon files, but found $count in $CONFIG_DIR/www/favicons"
  elif [ "$count" -gt 0 ]; then
    log "Found $count favicon files"
  fi
else
  log "[DRY-RUN] Would validate output files"
fi

if [ "$DRY_RUN" -eq 1 ]; then
  cat <<DRYRUN_EOF

Dry-run completed successfully.

No changes were made to your system. Run without --dry-run to apply changes:

  happyiot-install.sh

To skip backups (not recommended):
  happyiot-install.sh --no-backup

DRYRUN_EOF
else
  cat <<DONE_EOF

Installation completed successfully.

Backups:
  $BACKUP_DIR

Checks:
  1. Open Home Assistant in the browser
  2. Hard-refresh the page
  3. Confirm LightBrand is active
  4. Confirm the HappyIoT beacon loads
  5. Confirm login branding and OIDC pages are patched

DONE_EOF
fi

