#!/bin/bash
# ==========================================
# 检查 OpenVPN 客户端是否吊销 + 是否在线
# ==========================================

PKI_DIR="${1:-/etc/easy-rsa/pki}"
CLIENT_DIR="${2:-/etc/openvpn/client_configs}"
STATUS_FILE="${3:-openvpn-status.log}"
INDEX_FILE="$PKI_DIR/index.txt"

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
BLINK='\033[5m'
GREEN_BLINK='\033[5;32m'
NC='\033[0m'

# 检查文件
if [ ! -f "$INDEX_FILE" ]; then
  printf "${RED}❌ 找不到 index.txt: $INDEX_FILE${NC}\n"
  exit 1
fi

if [ ! -d "$CLIENT_DIR" ]; then
  printf "${RED}❌ 找不到 client_configs 目录: $CLIENT_DIR${NC}\n"
  exit 1
fi

if [ ! -f "$STATUS_FILE" ]; then
  printf "${YELLOW}⚠️  未找到状态文件: $STATUS_FILE (可能 OpenVPN 未启用 status)${NC}\n"
fi

# 计算显示宽度（考虑中文和emoji占2个字符宽度）
calc_display_width() {
  local str="$1"
  # 移除所有ANSI颜色代码
  str=$(printf "%b" "$str" | sed -E 's/\x1b\[[0-9;]*m//g')
  
  local width=0
  local len=${#str}
  
  for ((i=0; i<len; i++)); do
    local char="${str:$i:1}"
    # 获取字符的ASCII值
    if [ -z "$char" ]; then
      continue
    fi
    local ord=$(LC_CTYPE=C printf '%d' "'$char" 2>/dev/null || echo 128)
    
    # ASCII可打印字符 (32-126) 宽度为1，其他为2
    if [ "$ord" -ge 32 ] && [ "$ord" -le 126 ]; then
      width=$((width + 1))
    else
      width=$((width + 2))
    fi
  done
  
  echo $width
}

# 生成指定数量的空格
spaces() {
  printf '%*s' "$1" ''
}

# 统计数据
total=0
valid=0
revoked=0
expired=0
online=0
offline=0

# 第一遍：收集所有数据并计算最大宽度
declare -a clients
declare -a cert_statuses
declare -a online_statuses
declare -a dates
declare -a is_online_flags

# 初始化最小宽度（表头文字的宽度）
max_name_width=$(calc_display_width "客户端名称")
max_cert_width=$(calc_display_width "证书状态")
max_online_width=$(calc_display_width "在线状态")
max_date_width=$(calc_display_width "过期/吊销时间")

for cfg in "$CLIENT_DIR"/*.ovpn; do
  [ -e "$cfg" ] || continue
  client_name=$(basename "$cfg" .ovpn)
  
  # 计算客户端名称宽度
  name_width=$(calc_display_width "$client_name")
  [ $name_width -gt $max_name_width ] && max_name_width=$name_width
  
  line=$(grep "/CN=${client_name}$" "$INDEX_FILE" | tail -n 1)
  
  # 检查是否在线
  is_online=0
  if [ -f "$STATUS_FILE" ] && grep -q "^${client_name}," "$STATUS_FILE"; then
    is_online=1
    online=$((online + 1))
  else
    offline=$((offline + 1))
  fi
  
  if [ -z "$line" ]; then
    cert_status_text="❌ 未找到"
    online_status_text="🔴 离线"
    date_text="未在 index.txt 中"
  else
    status=$(echo "$line" | cut -c1)
    case "$status" in
      V)
        cert_status_text="✅ 有效"
        valid=$((valid + 1))
        exp=$(echo "$line" | awk '{print $2}')
        date_text=$(echo "$exp" | sed 's/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)Z/20\1-\2-\3 \4:\5:\6/')
        ;;
      R)
        cert_status_text="❌ 已吊销"
        revoked=$((revoked + 1))
        exp=$(echo "$line" | awk '{print $3}')
        date_text=$(echo "$exp" | sed 's/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)Z/20\1-\2-\3 \4:\5:\6/')
        ;;
      E)
        cert_status_text="⚠️  已过期"
        expired=$((expired + 1))
        exp=$(echo "$line" | awk '{print $2}')
        date_text=$(echo "$exp" | sed 's/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)Z/20\1-\2-\3 \4:\5:\6/')
        ;;
      *)
        cert_status_text="❓ 未知"
        date_text="未知"
        ;;
    esac
    
    if [ $is_online -eq 1 ]; then
      online_status_text="🟢 在线"
    else
      online_status_text="🔴 离线"
    fi
  fi
  
  # 计算各列最大宽度
  cert_width=$(calc_display_width "$cert_status_text")
  [ $cert_width -gt $max_cert_width ] && max_cert_width=$cert_width
  
  online_width=$(calc_display_width "$online_status_text")
  [ $online_width -gt $max_online_width ] && max_online_width=$online_width
  
  date_width=$(calc_display_width "$date_text")
  [ $date_width -gt $max_date_width ] && max_date_width=$date_width
  
  # 保存数据
  clients+=("$client_name")
  cert_statuses+=("$cert_status_text")
  online_statuses+=("$online_status_text")
  dates+=("$date_text")
  is_online_flags+=("$is_online")
  
  total=$((total + 1))
done

# 添加边距（每列左右各1个空格）
max_name_width=$((max_name_width + 2))
max_cert_width=$((max_cert_width + 2))
max_online_width=$((max_online_width + 2))
max_date_width=$((max_date_width + 2))

# 计算总宽度（表格宽度）
# 格式: ║ col1 ║ col2 ║ col3 ║ col4 ║
# 5个║符号 = 5个字符
table_total_width=$((max_name_width + max_cert_width + max_online_width + max_date_width + 5))

# 生成表格线
generate_line() {
  local type=$1  # top, middle, bottom
  case $type in
    top)
      printf "${BOLD}╔"
      printf '═%.0s' $(seq 1 $max_name_width)
      printf "╦"
      printf '═%.0s' $(seq 1 $max_cert_width)
      printf "╦"
      printf '═%.0s' $(seq 1 $max_online_width)
      printf "╦"
      printf '═%.0s' $(seq 1 $max_date_width)
      printf "╗${NC}\n"
      ;;
    middle)
      printf "${BOLD}╠"
      printf '═%.0s' $(seq 1 $max_name_width)
      printf "╬"
      printf '═%.0s' $(seq 1 $max_cert_width)
      printf "╬"
      printf '═%.0s' $(seq 1 $max_online_width)
      printf "╬"
      printf '═%.0s' $(seq 1 $max_date_width)
      printf "╣${NC}\n"
      ;;
    bottom)
      printf "${BOLD}╚"
      printf '═%.0s' $(seq 1 $max_name_width)
      printf "╩"
      printf '═%.0s' $(seq 1 $max_cert_width)
      printf "╩"
      printf '═%.0s' $(seq 1 $max_online_width)
      printf "╩"
      printf '═%.0s' $(seq 1 $max_date_width)
      printf "╝${NC}\n"
      ;;
  esac
}

# 生成简单边框
generate_simple_line() {
  local type=$1  # top or bottom
  local width=$2
  
  if [ "$type" = "top" ]; then
    printf "${BOLD}${CYAN}╔"
    printf '═%.0s' $(seq 1 $width)
    printf "╗${NC}\n"
  else
    printf "${BOLD}${CYAN}╚"
    printf '═%.0s' $(seq 1 $width)
    printf "╝${NC}\n"
  fi
}

# 打印居中的标题行
print_centered_title() {
  local text="$1"
  local width=$2
  local text_width=$(calc_display_width "$text")
  local padding=$(( (width - text_width) / 2 ))
  local padding_right=$((width - text_width - padding))
  
  printf "${BOLD}${CYAN}║${NC}"
  spaces $padding
  printf "%b" "$text"
  spaces $padding_right
  printf "${BOLD}${CYAN}║${NC}\n"
}

# 打印标题
printf "\n"

generate_simple_line top $table_total_width
print_centered_title "${BOLD}${CYAN}OpenVPN 客户端状态检查报告${NC}" $table_total_width
generate_simple_line bottom $table_total_width

printf "\n"
printf "${BLUE}📁 PKI目录:${NC} $PKI_DIR\n"
printf "${BLUE}📂 客户端配置目录:${NC} $CLIENT_DIR\n"
printf "${BLUE}📊 OpenVPN 状态文件:${NC} $STATUS_FILE\n"
printf "\n"

# 打印表格
generate_line top

# 打印表头
header_name_width=$(calc_display_width "客户端名称")
header_cert_width=$(calc_display_width "证书状态")
header_online_width=$(calc_display_width "在线状态")
header_date_width=$(calc_display_width "过期/吊销时间")

printf "${BOLD}║${NC} "
printf "客户端名称"
spaces $((max_name_width - header_name_width - 2))
printf " ${BOLD}║${NC} "
printf "证书状态"
spaces $((max_cert_width - header_cert_width - 2))
printf " ${BOLD}║${NC} "
printf "在线状态"
spaces $((max_online_width - header_online_width - 2))
printf " ${BOLD}║${NC} "
printf "过期/吊销时间"
spaces $((max_date_width - header_date_width - 2))
printf " ${BOLD}║${NC}\n"

generate_line middle

# 打印数据行
for i in "${!clients[@]}"; do
  client_name="${clients[$i]}"
  cert_status="${cert_statuses[$i]}"
  online_status="${online_statuses[$i]}"
  date_text="${dates[$i]}"
  is_online="${is_online_flags[$i]}"
  
  # 计算实际显示宽度
  name_width=$(calc_display_width "$client_name")
  cert_width=$(calc_display_width "$cert_status")
  online_width=$(calc_display_width "$online_status")
  date_width=$(calc_display_width "$date_text")
  
  # 应用颜色
  if [ "$is_online" -eq 1 ]; then
    name_display="${GREEN_BLINK}${client_name}${NC}"
    online_display="${GREEN_BLINK}${online_status}${NC}"
  else
    name_display="${client_name}"
    online_display="${RED}${online_status}${NC}"
  fi
  
  case "$cert_status" in
    "✅ 有效")
      cert_display="${GREEN}${cert_status}${NC}"
      ;;
    *"吊销"*)
      cert_display="${RED}${cert_status}${NC}"
      ;;
    *"过期"*)
      cert_display="${YELLOW}${cert_status}${NC}"
      ;;
    *"未找到"*)
      cert_display="${RED}${cert_status}${NC}"
      ;;
    *)
      cert_display="${YELLOW}${cert_status}${NC}"
      ;;
  esac
  
  # 打印行
  printf "${BOLD}║${NC} %b" "$name_display"
  spaces $((max_name_width - name_width - 2))
  printf " ${BOLD}║${NC} %b" "$cert_display"
  spaces $((max_cert_width - cert_width - 2))
  printf " ${BOLD}║${NC} %b" "$online_display"
  spaces $((max_online_width - online_width - 2))
  printf " ${BOLD}║${NC} %s" "$date_text"
  spaces $((max_date_width - date_width - 2))
  printf " ${BOLD}║${NC}\n"
done

generate_line bottom

# 统计摘要
printf "\n"

generate_simple_line top $table_total_width
print_centered_title "${BOLD}${CYAN}统计摘要${NC}" $table_total_width
generate_simple_line bottom $table_total_width

printf "\n"
printf "  ${BOLD}📊 总客户端数:${NC} ${CYAN}${BOLD}%d${NC}\n" "$total"
printf "\n"
printf "  ${BOLD}📜 证书状态:${NC}\n"
printf "     ${GREEN}✅ 有效:     ${BOLD}%2d${NC} 个\n" "$valid"
printf "     ${RED}❌ 已吊销:   ${BOLD}%2d${NC} 个\n" "$revoked"
printf "     ${YELLOW}⚠️  已过期:   ${BOLD}%2d${NC} 个\n" "$expired"
printf "\n"
printf "  ${BOLD}🔌 连接状态:${NC}\n"
printf "     ${GREEN_BLINK}🟢 在线:     ${BOLD}%2d${NC} 个 ${GREEN_BLINK}●${NC}\n" "$online"
printf "     ${RED}🔴 离线:     ${BOLD}%2d${NC} 个\n" "$offline"
printf "\n"

# 健康度评分
if [ $total -gt 0 ]; then
  health_score=$((online * 100 / total))
  if [ $health_score -ge 80 ]; then
    health_color="$GREEN_BLINK"
    health_icon="😊"
  elif [ $health_score -ge 50 ]; then
    health_color="$YELLOW"
    health_icon="😐"
  else
    health_color="$RED"
    health_icon="😟"
  fi
  printf "  ${BOLD}💚 在线率:${NC} ${health_color}${BOLD}%d%%${NC} %s\n" "$health_score" "$health_icon"
  printf "\n"
fi

printf "${GREEN}✅ 检查完成${NC}\n"
printf "\n"
