#!/bin/bash
# Description: 更新系统

# 开启严格模式：遇到错误、未定义变量、管道错误即停止运行
set -euo pipefail

#export SHELL="$(command -v bash || echo /bin/bash)"

# ===============================================================
# Arch Linux 系统更新助手 (完美版 - 集成镜像源检查)
# ===============================================================
# 【整体功能概述】
# 01. 支持从 Arch Linux 官方/中文社区获取最新新闻资讯
# 02. 智能解析新闻并高亮显示需要"手动干预"的警告信息
# 03. 等待用户确认后执行系统更新
# 04. 自动检测并优先使用 AUR 助手 (paru 或 yay)
# 05. 智能检测镜像源时效 (超过30天提示更新) -> 调用 mirror-update
# 06. 仅在检测到签名错误时自动修复 GPG 密钥环并重试更新
# 07. 自动静默更新 Flatpak 应用 (如果已安装)
# 08. 更新后自动执行 GRUB 配置更新 (grub-mkconfig)
# 09. 自动请求并后台保持 sudo 权限 (使用 trap 机制安全退出)
# 10. 自动检测系统语言 (zh_CN) 实现中英双语支持
# 11. 更新完成后静默刷新 Waybar 模块

# ------------------------------
# 1. Default parameters & KISS Language Detection
# ------------------------------
COUNT_LIMIT=15
NEWS_SOURCE="official"
# 直接利用 Bash 内置的环境变量匹配，不调用外部进程
if [[ "${LC_MESSAGES:-${LANG:-}}" == *zh_CN* ]]; then
  UI_LANG="zh"
else
  UI_LANG="en"
fi

# ------------------------------
# 2. Define Colors
# ------------------------------
NC='\033[0m'
H_RED='\033[1;31m'
H_GREEN='\033[1;32m'
H_YELLOW='\033[1;33m'
H_BLUE='\033[1;34m'
H_CYAN='\033[1;36m'

# ------------------------------
# 3. Argument parsing
# ------------------------------
usage() {
  cat <<EOF
Usage: $(basename "$0") [options]

Options:
  --ui-lang zh|en          UI language (default: auto)
  --news-source official|cn  News source (default: official)
  --count N                Max number of news items (default: 15)
  --help, -h               Show this help and exit
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
  --ui-lang) [[ -n "${2:-}" ]] && UI_LANG="$2" && shift 2 || exit 1 ;;
  --news-source) [[ -n "${2:-}" ]] && NEWS_SOURCE="$2" && shift 2 || exit 1 ;;
  --count) [[ -n "${2:-}" ]] && COUNT_LIMIT="$2" && shift 2 || exit 1 ;;
  --help | -h)
    usage
    exit 0
    ;;
  *)
    printf "Unknown option: %s\n" "$1"
    usage
    exit 1
    ;;
  esac
done

# ------------------------------
# 4. Localization Logic (Associative Arrays - Safe & Fast)
# ------------------------------
declare -A MSG
if [[ "$UI_LANG" == "zh" ]]; then
  TAG_INFO="[消息]"
  TAG_SUCCESS="[成功]"
  TAG_WARN="[注意]"
  TAG_ERROR="[错误]"

  MSG[INTRO_HEADER]="=== Arch Linux 系统更新助手 ==="
  MSG[INTRO_DESC]="本脚本将执行以下操作："
  MSG[INTRO_1]="1. 获取最新 Arch Linux 新闻"
  MSG[INTRO_2]="2. 检查镜像源时效并按需更新"
  MSG[INTRO_3]="3. 升级系统软件包"
  MSG[INTRO_4]="4. 若遇签名错误则自动修复密钥环并重试"
  MSG[INTRO_5]="5. 更新 Flatpak 应用"
  MSG[INTRO_6]="6. 重新生成 GRUB 配置文件"

  MSG[REQ_SUDO]="正在请求管理员权限以更新系统..."
  MSG[SUDO_FAIL]="需要管理员权限才能更新系统。已退出。"
  MSG[NO_HELPER]="未找到 AUR 助手 (paru 或 yay)"
  MSG[PREPARING]="准备使用 %s 更新系统..."
  MSG[FETCHING]="正在获取 Arch Linux 新闻..."
  MSG[NEWS_HEADER]="最近 {0} 条 Arch Linux 新闻："
  MSG[CONFIRM]="请阅读上述新闻。确认使用 %s 继续更新吗？[Y/n]"
  MSG[CANCEL]="更新已取消。"
  MSG[ERR_FETCH]="获取新闻失败（网络或源错误）"
  MSG[FORCE_ASK]="是否忽略新闻强制更新？[y/N]"
  MSG[FORCING]="正在强制更新..."
  MSG[EXIT]="安全退出。"

  MSG[SNAP]="[0/6] 正在创建 Btrfs 快照..."
  MSG[STEP_MIRROR]="[1/6] 检查镜像源时效性..."
  MSG[STEP_1]="[2/6] 正在升级系统..."
  MSG[SIG_DETECTED]="检测到 pacman 签名错误，开始自动修复密钥环..."
  MSG[SIG_REPAIR_ASK]="检测到签名问题，是否执行自动密钥修复？[Y/n]"
  MSG[SIG_REPAIR_ABORT]="用户拒绝自动密钥修复，已退出。"
  MSG[SIG_RETRY]="密钥修复完成，正在重试系统升级..."
  MSG[SIG_NOT_DETECTED]="升级失败，但未检测到签名相关错误。"
  MSG[SIG_RETRY_FAIL]="签名修复后重试仍失败。"
  MSG[SIG_FIX_DONE]="签名修复流程执行完成。"
  MSG[STEP_2]="[3/6] 检查 Flatpak 更新..."
  MSG[STEP_3]="[4/6] 更新 GRUB 配置..."
  MSG[GRUB_OK]="GRUB 更新成功。"
  MSG[GRUB_FAIL]="GRUB 更新失败。"

  MSG[MIR_OLD]="镜像源已 %s 天未更新 (>30天)。是否更新？[Y/n]"
  MSG[MIR_FRESH]="镜像源很新鲜 (生成于 %s 天前)。"
  MSG[MIR_UPDATING]="正在运行镜像更新脚本..."
  MSG[MIR_SKIP]="跳过镜像源更新。"
  MSG[MIR_NO_FILE]="未找到 Reflector 时间标记，跳过检查。"
  MSG[MIR_NO_SCRIPT]="错误：未在 %s 找到更新脚本。"
else
  TAG_INFO="[INFO]"
  TAG_SUCCESS="[OK]"
  TAG_WARN="[WARN]"
  TAG_ERROR="[ERROR]"

  MSG[INTRO_HEADER]="=== Arch Linux System Update Assistant ==="
  MSG[INTRO_DESC]="Actions to perform:"
  MSG[INTRO_1]="1. Fetch latest Arch Linux news"
  MSG[INTRO_2]="2. Check mirrorlist age & Update if needed"
  MSG[INTRO_3]="3. Upgrade system packages"
  MSG[INTRO_4]="4. Auto-fix keyrings and retry on signature errors"
  MSG[INTRO_5]="5. Update Flatpak apps"
  MSG[INTRO_6]="6. Re-generate GRUB config"

  MSG[REQ_SUDO]="Requesting sudo..."
  MSG[SUDO_FAIL]="Sudo privileges required. Exiting..."
  MSG[NO_HELPER]="No AUR helper found (paru or yay)"
  MSG[PREPARING]="Preparing update with %s..."
  MSG[FETCHING]="Fetching news..."
  MSG[NEWS_HEADER]="Recent {0} Arch Linux news items:"
  MSG[CONFIRM]="Proceed with %s? [Y/n]"
  MSG[CANCEL]="Update cancelled."
  MSG[ERR_FETCH]="Failed to fetch news"
  MSG[FORCE_ASK]="Force update anyway? [y/N]"
  MSG[FORCING]="Forcing update..."
  MSG[EXIT]="Safe exit."

  MSG[SNAP]="[0/6] Creating Btrfs Snapshot..."
  MSG[STEP_MIRROR]="[1/6] Checking mirrorlist age..."
  MSG[STEP_1]="[2/6] Upgrading system"
  MSG[SIG_DETECTED]="Pacman signature error detected. Running automatic keyring repair..."
  MSG[SIG_REPAIR_ASK]="Signature issue detected. Run automatic keyring repair? [Y/n]"
  MSG[SIG_REPAIR_ABORT]="Automatic keyring repair declined by user. Exiting."
  MSG[SIG_RETRY]="Keyring repair finished. Retrying system upgrade..."
  MSG[SIG_NOT_DETECTED]="Upgrade failed but no signature-related error was detected."
  MSG[SIG_RETRY_FAIL]="Upgrade still failed after signature repair."
  MSG[SIG_FIX_DONE]="Signature repair workflow completed."
  MSG[STEP_2]="[3/6] Checking Flatpak updates"
  MSG[STEP_3]="[4/6] Updating GRUB"
  MSG[GRUB_OK]="GRUB updated."
  MSG[GRUB_FAIL]="GRUB update failed."

  MSG[MIR_OLD]="Mirrors are %s days old (>30). Update? [Y/n]"
  MSG[MIR_FRESH]="Mirrors are fresh (%s days old)."
  MSG[MIR_UPDATING]="Running mirror-update script..."
  MSG[MIR_SKIP]="Skipping mirror update."
  MSG[MIR_NO_FILE]="No Reflector timestamp found. Skipping."
  MSG[MIR_NO_SCRIPT]="Error: mirror-update script not found at %s"
fi

# ------------------------------
# 5. Logging Functions (支持类似 printf 的格式化输出)
# ------------------------------
_log()     { local fmt=$1; shift; printf "${H_BLUE}${TAG_INFO}${NC} ${fmt}\n" "$@"; }
_success() { local fmt=$1; shift; printf "${H_GREEN}${TAG_SUCCESS}${NC} ${fmt}\n" "$@"; }
_warn()    { local fmt=$1; shift; printf "${H_YELLOW}${TAG_WARN}${NC} ${fmt}\n" "$@"; }
_error()   { local fmt=$1; shift; printf "${H_RED}${TAG_ERROR}${NC} ${fmt}\n" "$@"; exit 1; }

# ------------------------------
# 6. Intro & Request Privileges
# ------------------------------
printf "\n${H_CYAN}%s${NC}\n" "${MSG[INTRO_HEADER]}"
printf "%s\n" "${MSG[INTRO_DESC]}"
printf "  %s\n" "${MSG[INTRO_1]}"
printf "  %s\n" "${MSG[INTRO_2]}"
printf "  %s\n" "${MSG[INTRO_3]}"
printf "  %s\n" "${MSG[INTRO_4]}"
printf "  %s\n" "${MSG[INTRO_5]}"
printf "  %s\n\n" "${MSG[INTRO_6]}"

_log "${MSG[REQ_SUDO]}"
if ! sudo -v; then
  _error "${MSG[SUDO_FAIL]}"
fi

# Keep sudo alive in background (添加 || true 避免严格模式下 kill 失败导致报错)
while true; do
  sudo -n true
  sleep 60
done 2>/dev/null &
SUDO_PID=$!
trap 'kill $SUDO_PID 2>/dev/null || true' EXIT

# ------------------------------
# 7. Check AUR Helper
# ------------------------------
UPDATE_CMD=""
if command -v paru >/dev/null 2>&1; then
  UPDATE_CMD="paru"
elif command -v yay >/dev/null 2>&1; then
  UPDATE_CMD="yay"
else
  _error "${MSG[NO_HELPER]}"
fi

# ------------------------------
# Mirror Check Function
# ------------------------------
check_mirror_age() {
  _log "${MSG[STEP_MIRROR]}"

  local MIRROR_LIST="/etc/pacman.d/mirrorlist"
  local THRESHOLD=$((30 * 24 * 60 * 60))
  local UPDATE_SCRIPT="$HOME/.local/bin/mirror-update"

  if [[ -f /etc/os-release ]]; then . /etc/os-release; fi
  if [[ "${ID:-}" != "arch" ]] && [[ ! "${ID_LIKE:-}" =~ arch ]]; then return; fi

  if [[ -f "$MIRROR_LIST" ]] && grep -q "^# When:" "$MIRROR_LIST"; then
    local when_str
    when_str=$(grep "^# When:" "$MIRROR_LIST" | cut -d':' -f2- | sed 's/ UTC//g' | xargs || true)
    
    local file_ts current_ts
    file_ts=$(date -u -d "$when_str" +%s 2>/dev/null || echo 0)
    current_ts=$(date +%s)

    if [[ "$file_ts" -gt 0 ]]; then
      local diff=$((current_ts - file_ts))
      local days_old=$((diff / 86400))

      if [[ "$diff" -gt "$THRESHOLD" ]]; then
        printf "${H_YELLOW}${TAG_WARN}${NC} "
        printf "${MSG[MIR_OLD]} " "$days_old"
        local choice
        read -r choice || true
        choice=${choice:-Y}

        if [[ "$choice" =~ ^[Yy]$ ]]; then
          if [[ -x "$UPDATE_SCRIPT" ]]; then
            _log "${MSG[MIR_UPDATING]}"
            "$UPDATE_SCRIPT"
          else
            _error "${MSG[MIR_NO_SCRIPT]}" "$UPDATE_SCRIPT"
          fi
        else
          _log "${MSG[MIR_SKIP]}"
        fi
      else
        printf "${H_BLUE}${TAG_INFO}${NC} "
        printf "${MSG[MIR_FRESH]}\n" "$days_old"
      fi
    else
      _warn "${MSG[MIR_NO_FILE]}"
    fi
  else
    _log "${MSG[MIR_NO_FILE]}"
  fi
}

# ------------------------------
# Signature Error Detection & Auto Repair
# ------------------------------
is_signature_error_log() {
  local log_file="$1"
  grep -Eqi \
    "invalid or corrupted package \(PGP signature\)|one or more PGP signatures could not be verified|required key missing from keyring|unknown trust|signature from .* is invalid|signature from .* is unknown trust|密钥环|签名|无法验证.*签名|PGP 签名" \
    "$log_file"
}

repair_keyring_for_signature_issue() {
  _warn "${MSG[SIG_DETECTED]}"

  printf "${H_YELLOW}${TAG_WARN}${NC} %s " "${MSG[SIG_REPAIR_ASK]}"
  local repair_confirm
  read -r repair_confirm || true
  case "${repair_confirm:-Y}" in
  [Nn] | [Nn][Oo])
    _warn "${MSG[SIG_REPAIR_ABORT]}"
    exit 0
    ;;
  esac

  sudo pacman-key --init

  sudo pacman-key --recv-keys 3A9917BF0DED5C13F69AC68FABEC0A1208037BE9
  sudo pacman-key --lsign-key 3A9917BF0DED5C13F69AC68FABEC0A1208037BE9
  sudo pacman-key --recv-keys 7931B6D628C8D3BA
  sudo pacman-key --lsign-key 7931B6D628C8D3BA

  local i
  for ((i = 0; i < 5; i++)); do
    sudo pacman -Sy --noconfirm --needed archlinux-keyring && break
  done
  for ((i = 0; i < 5; i++)); do
    sudo pacman -Sy --noconfirm --needed archlinuxcn-keyring && break
  done

  sudo pacman-key --populate
  _success "${MSG[SIG_FIX_DONE]}"
}

run_upgrade_with_signature_auto_fix() {
  local update_log
  update_log=$(mktemp /tmp/sysup_upgrade_XXXX.log)

  set +e
  "$UPDATE_CMD" -Syu 2>&1 | tee "$update_log"
  local first_status=${PIPESTATUS[0]}
  set -e

  if [[ "$first_status" -eq 0 ]]; then
    rm -f "$update_log"
    return 0
  fi

  if ! is_signature_error_log "$update_log"; then
    rm -f "$update_log"
    _error "${MSG[SIG_NOT_DETECTED]}"
  fi

  repair_keyring_for_signature_issue
  _log "${MSG[SIG_RETRY]}"

  set +e
  "$UPDATE_CMD" -Syu
  local retry_status=$?
  set -e

  rm -f "$update_log"
  if [[ "$retry_status" -ne 0 ]]; then
    _error "${MSG[SIG_RETRY_FAIL]}"
  fi
}

# ------------------------------
# 8. Update Function
# ------------------------------
perform_update() {
if command -v quicksave >/dev/null 2>&1 && [[ "$(findmnt -no FSTYPE /)" == "btrfs" ]]; then 
    _log "${MSG[SNAP]}"
    quicksave -d quicksave-sysup
fi

  check_mirror_age

  _log "${MSG[STEP_1]}"
  run_upgrade_with_signature_auto_fix

  if command -v flatpak >/dev/null 2>&1; then
    _log "${MSG[STEP_2]}"
    flatpak update -y
  fi

  _log "${MSG[STEP_3]}"
  if command -v grub-mkconfig >/dev/null 2>&1; then
    if sudo env LANG=en_US.UTF-8 grub-mkconfig -o /boot/grub/grub.cfg; then
      _success "${MSG[GRUB_OK]}"
    else
      _warn "${MSG[GRUB_FAIL]}"
    fi
  else
    _warn "grub-mkconfig not found. Skipping GRUB update."
  fi

  # 使用 || true 避免 Waybar 未运行时 pkill 返回 1 触发 set -e 中断
  pkill -SIGUSR1 -f ~/.config/waybar/scripts/check-updates.sh || true
}

# ------------------------------
# 9. Main Execution
# ------------------------------
_log "${MSG[PREPARING]}" "$UPDATE_CMD"
_log "${MSG[FETCHING]}"

PYTHON_SCRIPT=$(
  cat <<'EOF'
import sys
import xml.etree.ElementTree as ET
try:
    limit = int(sys.argv[1])
    header_template = sys.argv[2]
    sys.stdin.reconfigure(encoding='utf-8')
    raw_data = sys.stdin.read()
    if not raw_data.strip(): sys.exit(1)
    root = ET.fromstring(raw_data)
    items = root.findall('./channel/item')[:limit]
    print(f'\n\033[1;33m{header_template.format(len(items))}\033[0m\n')
    for item in items:
        title = item.find('title').text
        pub_date = item.find('pubDate').text
        date_str = pub_date[:16]
        check_text = title.lower()
        if any(x in check_text for x in ['intervention', 'manual', '手动', '干预']):
            color = '\033[1;31m'; prefix = '!!! '
        else:
            color = '\033[1;32m'; prefix = ''
        print(f'{color}[{date_str}] {prefix}{title}\033[0m')
except Exception as e:
    sys.stderr.write(f'\nParse Error: {e}\n')
    sys.exit(1)
EOF
)

if [[ "$NEWS_SOURCE" == "cn" ]]; then
  NEWS_URL="https://www.archlinuxcn.org/category/news/feed/"
else
  NEWS_URL="https://archlinux.org/feeds/news/"
fi

# 抑制 Python 的输出流可能导致的管道错误异常
if curl -sS -L --connect-timeout 15 -A "Mozilla/5.0" "$NEWS_URL" |
  python -c "$PYTHON_SCRIPT" "$COUNT_LIMIT" "${MSG[NEWS_HEADER]}"; then

  printf "\n"
  printf "${MSG[CONFIRM]} " "$UPDATE_CMD"
  read -r confirm || true
  case "$confirm" in
  [Yy]* | "") perform_update ;;
  *)
    _warn "${MSG[CANCEL]}"
    exit 0
    ;;
  esac
else
  printf "\n"
  _warn "${MSG[ERR_FETCH]}"
  printf "${MSG[FORCE_ASK]} "
  read -r force_confirm || true
  case "$force_confirm" in
  [Yy]*)
    _warn "${MSG[FORCING]}"
    perform_update
    ;;
  *)
    _log "${MSG[EXIT]}"
    exit 1
    ;;
  esac
fi
