#!/bin/bash
# XNode SSL Certificate Auto Renewal Script
# 自动续期证书脚本，用于 cron 定时任务
# 使用纯 Bash 解析 JSON，无需 Python3 或 jq

set -e

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# 获取脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/cert-config.json"
ENV_FILE="${SCRIPT_DIR}/.env"
LOG_FILE="${SCRIPT_DIR}/cert-renew.log"

# 日志函数（需要在文件存在前定义）
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# 加载 .env 文件（如果存在）
if [ -f "$ENV_FILE" ]; then
    while IFS= read -r line || [ -n "$line" ]; do
        [[ "$line" =~ ^[[:space:]]*# ]] && continue
        [[ -z "${line// }" ]] && continue
        
        if [[ "$line" =~ ^[[:space:]]*([^=]+)=(.*)$ ]]; then
            key="${BASH_REMATCH[1]}"
            value="${BASH_REMATCH[2]}"
            key=$(echo "$key" | xargs)
            value=$(echo "$value" | xargs)
            value="${value#\"}"
            value="${value%\"}"
            value="${value#\'}"
            value="${value%\'}"
            
            if [ -z "${!key}" ]; then
                export "$key=$value"
            fi
        fi
    done < "$ENV_FILE"
fi

# 纯 Bash JSON 解析函数（简单实现，适用于 cert-config.json 结构）
parse_cert_config() {
    local config_file="${1:-$CONFIG_FILE}"
    
    if [ ! -f "$config_file" ]; then
        log "错误: 配置文件不存在: $config_file"
        return 1
    fi
    
    # 读取文件内容，移除注释和多余空格
    local content=$(cat "$config_file" | sed 's|//.*||g' | tr -d '\n' | sed 's/  */ /g')
    
    # 提取证书邮箱
    CERT_EMAIL=$(echo "$content" | grep -o '"email"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"email"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    
    # 提取输出目录
    OUTPUT_DIR=$(echo "$content" | grep -o '"output_dir"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"output_dir"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    if [ -z "$OUTPUT_DIR" ]; then
        OUTPUT_DIR="${SCRIPT_DIR}/certs"
    fi
    
    # 提取续期天数
    RENEW_DAYS=$(echo "$content" | grep -o '"renew_days"[[:space:]]*:[[:space:]]*[0-9]*' | grep -o '[0-9]*$')
    RENEW_DAYS="${RENEW_DAYS:-30}"
    
    # 提取域名列表（支持字符串和对象格式）
    DOMAINS_ARRAY=()
    # 提取所有域名项（字符串格式: "domain:provider"）
    while IFS= read -r line; do
        if [[ "$line" =~ \"([^\"]+:[^\"]+)\" ]]; then
            DOMAINS_ARRAY+=("${BASH_REMATCH[1]}")
        fi
    done < <(echo "$content" | grep -o '"[^"]*:[^"]*"' | grep ':')
    
    # 提取对象格式的域名 {"domain": "...", "provider": "..."}
    while IFS= read -r domain_line; do
        if [[ "$domain_line" =~ \"domain\"[[:space:]]*:[[:space:]]*\"([^\"]+)\" ]]; then
            local domain="${BASH_REMATCH[1]}"
            # 查找对应的 provider
            local provider_line=$(echo "$content" | grep -A 5 "\"domain\"[[:space:]]*:[[:space:]]*\"$domain\"" | grep -o '"provider"[[:space:]]*:[[:space:]]*"[^"]*"')
            if [[ "$provider_line" =~ \"provider\"[[:space:]]*:[[:space:]]*\"([^\"]+)\" ]]; then
                local provider="${BASH_REMATCH[1]}"
                DOMAINS_ARRAY+=("$domain:$provider")
            fi
        fi
    done < <(echo "$content" | grep -o '\{[^}]*"domain"[^}]*\}')
    
    # 导出变量
    export CERT_EMAIL
    export OUTPUT_DIR
    export RENEW_DAYS
    
    return 0
}

# 检查证书是否需要续期
check_cert_expiry() {
    local cert_file="$1"
    local renew_days="${2:-30}"
    
    if [ ! -f "$cert_file" ]; then
        return 1
    fi
    
    # 检查证书到期时间
    local expiry_date=$(openssl x509 -in "$cert_file" -noout -enddate 2>/dev/null | cut -d= -f2)
    if [ -z "$expiry_date" ]; then
        return 1
    fi
    
    # 转换为时间戳
    local expiry_timestamp=$(date -d "$expiry_date" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$expiry_date" +%s 2>/dev/null || echo "0")
    local current_timestamp=$(date +%s)
    local days_until_expiry=$(( (expiry_timestamp - current_timestamp) / 86400 ))
    
    # 如果剩余天数小于等于续期天数，需要续期
    if [ "$days_until_expiry" -le "$renew_days" ]; then
        echo "$days_until_expiry"
        return 0
    fi
    
    return 1
}

# 续期证书
renew_cert() {
    local domain_provider="$1"
    local domain=$(echo "$domain_provider" | cut -d':' -f1)
    local provider=$(echo "$domain_provider" | cut -d':' -f2)
    
    log "开始续期证书: $domain"
    
    # 调用 cert-manager.sh 进行续期
    if "$SCRIPT_DIR/cert-manager.sh" --renew-only "$domain_provider" 2>&1 | tee -a "$LOG_FILE"; then
        log "证书续期成功: $domain"
        
        # 重启 xnode 服务（如果服务正在运行）
        if systemctl is-active --quiet xnode 2>/dev/null; then
            log "重启 xnode 服务以加载新证书..."
            systemctl reload xnode 2>/dev/null || systemctl restart xnode 2>/dev/null || true
            log "xnode 服务已重启"
        fi
        
        return 0
    else
        log "证书续期失败: $domain"
        return 1
    fi
}

# 主函数
main() {
    log "=========================================="
    log "XNode SSL Certificate Auto Renewal"
    log "=========================================="
    
    # 检查配置文件是否存在
    if [ ! -f "$CONFIG_FILE" ]; then
        log "错误: 配置文件不存在: $CONFIG_FILE"
        exit 1
    fi
    
    # 解析配置文件
    if ! parse_cert_config "$CONFIG_FILE"; then
        log "错误: 无法解析配置文件"
        exit 1
    fi
    
    if [ ${#DOMAINS_ARRAY[@]} -eq 0 ]; then
        log "警告: 未找到域名配置"
        exit 0
    fi
    
    log "续期检查天数: $RENEW_DAYS 天"
    log "证书目录: $OUTPUT_DIR"
    log "域名数量: ${#DOMAINS_ARRAY[@]}"
    
    # 检查每个域名
    local renewed_count=0
    local skipped_count=0
    local failed_count=0
    
    for domain_provider in "${DOMAINS_ARRAY[@]}"; do
        domain=$(echo "$domain_provider" | cut -d':' -f1)
        cert_file="$OUTPUT_DIR/$domain/fullchain.pem"
        
        log "检查证书: $domain"
        
        # 检查证书是否存在
        if [ ! -f "$cert_file" ]; then
            log "警告: 证书文件不存在: $cert_file，跳过"
            skipped_count=$((skipped_count + 1))
            continue
        fi
        
        # 检查是否需要续期
        if days_left=$(check_cert_expiry "$cert_file" "$RENEW_DAYS"); then
            log "证书将在 $days_left 天后过期，开始续期..."
            if renew_cert "$domain_provider"; then
                renewed_count=$((renewed_count + 1))
            else
                failed_count=$((failed_count + 1))
            fi
        else
            log "证书未到期，跳过续期: $domain"
            skipped_count=$((skipped_count + 1))
        fi
    done
    
    # 输出统计
    log "=========================================="
    log "续期完成"
    log "续期: $renewed_count"
    log "跳过: $skipped_count"
    log "失败: $failed_count"
    log "=========================================="
}

# 运行主函数
main "$@"
