#!/bin/bash
# XNode SSL Certificate Manager
# 支持阿里云和 Cloudflare DNS API 的自动化证书申请脚本
# 使用纯 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"
ACME_SH_URL="https://get.acme.sh"
ACME_SH_INSTALL_DIR="$HOME/.acme.sh"
# 默认证书输出目录为脚本所在目录下的 certs 目录
DEFAULT_OUTPUT_DIR="${SCRIPT_DIR}/certs"

# 加载 .env 文件（如果存在）
if [ -f "$ENV_FILE" ]; then
    # 读取 .env 文件，忽略注释和空行
    while IFS= read -r line || [ -n "$line" ]; do
        # 跳过注释和空行
        [[ "$line" =~ ^[[:space:]]*# ]] && continue
        [[ -z "${line// }" ]] && continue
        
        # 解析 KEY=VALUE 格式
        if [[ "$line" =~ ^[[:space:]]*([^=]+)=(.*)$ ]]; then
            key="${BASH_REMATCH[1]}"
            value="${BASH_REMATCH[2]}"
            
            # 移除 key 和 value 两端的空格
            key=$(echo "$key" | xargs)
            value=$(echo "$value" | xargs)
            
            # 移除 value 的引号（如果存在）
            value="${value#\"}"
            value="${value%\"}"
            value="${value#\'}"
            value="${value%\'}"
            
            # 如果环境变量未设置，则设置它（.env 文件优先级低于已存在的环境变量）
            if [ -z "${!key}" ]; then
                export "$key=$value"
            fi
        fi
    done < "$ENV_FILE"
fi

# 检查并安装 acme.sh
install_acme_sh() {
    if [ -d "$ACME_SH_INSTALL_DIR" ]; then
        echo -e "${GREEN}acme.sh 已安装${NC}"
        return 0
    fi

    echo -e "${YELLOW}正在安装 acme.sh...${NC}"
    curl "$ACME_SH_URL" | sh -s email="${CERT_EMAIL}"
    
    if [ ! -d "$ACME_SH_INSTALL_DIR" ]; then
        echo -e "${RED}acme.sh 安装失败${NC}"
        exit 1
    fi
    
    # 添加到 PATH
    export PATH="$ACME_SH_INSTALL_DIR:$PATH"
    echo -e "${GREEN}acme.sh 安装成功${NC}"
}

# 纯 Bash JSON 解析函数（简单实现，适用于 cert-config.json 结构）
parse_json() {
    local config_file="${1:-$CONFIG_FILE}"
    
    if [ ! -f "$config_file" ]; then
        echo "ERROR: Config file not found: $config_file" >&2
        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="${DEFAULT_OUTPUT_DIR}"
    fi
    
    # 提取密钥长度
    KEY_LENGTH=$(echo "$content" | grep -o '"key_length"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"key_length"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    KEY_LENGTH="${KEY_LENGTH:-ec-256}"
    
    # 提取续期天数
    RENEW_DAYS=$(echo "$content" | grep -o '"renew_days"[[:space:]]*:[[:space:]]*[0-9]*' | grep -o '[0-9]*$')
    RENEW_DAYS="${RENEW_DAYS:-30}"
    
    # 提取阿里云配置
    ALIYUN_ACCESS_KEY_ID=$(echo "$content" | sed -n '/"aliyun"/,/}/p' | grep -o '"access_key_id"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"access_key_id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    ALIYUN_ACCESS_KEY_SECRET=$(echo "$content" | sed -n '/"aliyun"/,/}/p' | grep -o '"access_key_secret"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"access_key_secret"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    
    # 提取 Cloudflare 配置
    CF_API_TOKEN=$(echo "$content" | sed -n '/"cf"/,/}/p' | grep -o '"api_token"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"api_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    CF_API_KEY=$(echo "$content" | sed -n '/"cf"/,/}/p' | grep -o '"api_key"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"api_key"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    CF_API_EMAIL=$(echo "$content" | sed -n '/"cf"/,/}/p' | grep -o '"api_email"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"api_email"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    
    # 提取域名列表（支持字符串和对象格式）
    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 KEY_LENGTH
    export RENEW_DAYS
    export ALIYUN_ACCESS_KEY_ID
    export ALIYUN_ACCESS_KEY_SECRET
    export CF_API_TOKEN
    export CF_API_KEY
    export CF_API_EMAIL
    
    # 输出域名列表（每行一个，格式: DOMAIN:domain:provider）
    for domain in "${DOMAINS_ARRAY[@]}"; do
        echo "DOMAIN:$domain"
    done
}

# 加载配置
load_config() {
    if [ ! -f "$CONFIG_FILE" ]; then
        echo -e "${RED}错误: 配置文件不存在: $CONFIG_FILE${NC}"
        exit 1
    fi
    
    # 解析配置文件
    local parse_output
    parse_output=$(parse_json "$CONFIG_FILE")
    
    if [ $? -ne 0 ]; then
        echo -e "${RED}错误: 无法解析配置文件${NC}"
        exit 1
    fi
    
    # 处理解析输出
    DOMAINS_ARRAY=()
    while IFS= read -r line; do
        if [[ "$line" =~ ^DOMAIN:(.+)$ ]]; then
            DOMAINS_ARRAY+=("${BASH_REMATCH[1]}")
        fi
    done <<< "$parse_output"
    
    # 如果环境变量已设置，优先使用环境变量
    CERT_EMAIL="${CERT_EMAIL:-${CERT_EMAIL_ENV:-}}"
    OUTPUT_DIR="${OUTPUT_DIR:-${CERT_OUTPUT_DIR:-${DEFAULT_OUTPUT_DIR}}}"
    KEY_LENGTH="${KEY_LENGTH:-ec-256}"
    RENEW_DAYS="${RENEW_DAYS:-30}"
    
    # 阿里云凭证（优先使用环境变量）
    ALIYUN_ACCESS_KEY_ID="${ALIYUN_KEY:-${ALIYUN_ACCESS_KEY_ID}}"
    ALIYUN_ACCESS_KEY_SECRET="${ALIYUN_SECRET:-${ALIYUN_ACCESS_KEY_SECRET}}"
    
    # Cloudflare 凭证（优先使用环境变量）
    CF_API_TOKEN="${CF_TOKEN:-${CF_API_TOKEN}}"
    CF_API_KEY="${CF_API_KEY:-${CF_API_KEY}}"
    CF_API_EMAIL="${CF_API_EMAIL:-${CF_API_EMAIL}}"
    
    if [ ${#DOMAINS_ARRAY[@]} -eq 0 ]; then
        echo -e "${YELLOW}警告: 未找到域名配置${NC}"
    fi
}

# 设置 DNS API
setup_dns_api() {
    local provider="$1"
    
    case "$provider" in
        aliyun)
            if [ -z "$ALIYUN_ACCESS_KEY_ID" ] || [ -z "$ALIYUN_ACCESS_KEY_SECRET" ]; then
                echo -e "${RED}错误: 阿里云 API 凭证未配置${NC}"
                return 1
            fi
            export Ali_Key="$ALIYUN_ACCESS_KEY_ID"
            export Ali_Secret="$ALIYUN_ACCESS_KEY_SECRET"
            PROVIDER_DNS="dns_ali"
            ;;
        cf|cloudflare)
            if [ -n "$CF_API_TOKEN" ]; then
                export CF_Token="$CF_API_TOKEN"
                PROVIDER_DNS="dns_cf"
            elif [ -n "$CF_API_KEY" ] && [ -n "$CF_API_EMAIL" ]; then
                export CF_Key="$CF_API_KEY"
                export CF_Email="$CF_API_EMAIL"
                PROVIDER_DNS="dns_cf"
            else
                echo -e "${RED}错误: Cloudflare API 凭证未配置${NC}"
                return 1
            fi
            ;;
        *)
            echo -e "${RED}错误: 不支持的 DNS 提供商: $provider${NC}"
            return 1
            ;;
    esac
}

# 申请/续期证书
issue_cert() {
    local domain_provider="$1"
    local domain=$(echo "$domain_provider" | cut -d':' -f1)
    local provider=$(echo "$domain_provider" | cut -d':' -f2)
    
    echo -e "${BLUE}处理域名: $domain (提供商: $provider)${NC}"
    
    # 设置 DNS API
    setup_dns_api "$provider"
    
    # 设置 acme.sh 路径
    export PATH="$ACME_SH_INSTALL_DIR:$PATH"
    source "$ACME_SH_INSTALL_DIR/acme.sh.env" 2>/dev/null || true

    # 检查证书是否已存在
    local cert_dir="$ACME_SH_INSTALL_DIR/$domain"
    if [ -d "$cert_dir" ]; then
        echo -e "${YELLOW}证书已存在，检查是否需要续期...${NC}"
        
        # 检查证书有效期
        local cert_file="$cert_dir/$domain.cer"
        if [ -f "$cert_file" ]; then
            local days_left=$(openssl x509 -in "$cert_file" -noout -checkend $((RENEW_DAYS * 86400)) 2>/dev/null && echo "valid" || echo "expired")
            if [ "$days_left" = "valid" ]; then
                echo -e "${GREEN}证书仍然有效，跳过${NC}"
                copy_cert "$domain"
                return 0
            fi
        fi
        
        echo -e "${YELLOW}证书即将过期，开始续期...${NC}"
        "$ACME_SH_INSTALL_DIR/acme.sh" --renew -d "$domain" --dns "$PROVIDER_DNS" --force
    else
        echo -e "${YELLOW}申请新证书...${NC}"
        "$ACME_SH_INSTALL_DIR/acme.sh" --issue -d "$domain" --dns "$PROVIDER_DNS" --keylength "$KEY_LENGTH"
    fi

    if [ $? -eq 0 ]; then
        echo -e "${GREEN}证书申请/续期成功: $domain${NC}"
        copy_cert "$domain"
        return 0
    else
        echo -e "${RED}证书申请/续期失败: $domain${NC}"
        return 1
    fi
}

# 复制证书到目标目录（每个域名独立文件夹）
copy_cert() {
    local domain=$1
    local cert_dir="$ACME_SH_INSTALL_DIR/$domain"
    
    if [ ! -d "$cert_dir" ]; then
        echo -e "${RED}错误: 证书目录不存在: $cert_dir${NC}"
        return 1
    fi

    # 创建域名专用目录（每个域名一个文件夹）
    local target_dir="$OUTPUT_DIR/$domain"
    mkdir -p "$target_dir"
    
    echo -e "${BLUE}证书目录: $target_dir${NC}"

    # 复制证书文件
    if [ -f "$cert_dir/$domain.cer" ]; then
        cp "$cert_dir/$domain.cer" "$target_dir/fullchain.pem"
        echo -e "${GREEN}  ✓ 证书: $target_dir/fullchain.pem${NC}"
    fi

    if [ -f "$cert_dir/$domain.key" ]; then
        cp "$cert_dir/$domain.key" "$target_dir/privkey.pem"
        chmod 600 "$target_dir/privkey.pem"
        echo -e "${GREEN}  ✓ 私钥: $target_dir/privkey.pem${NC}"
    fi

    if [ -f "$cert_dir/ca.cer" ]; then
        cp "$cert_dir/ca.cer" "$target_dir/chain.pem"
        echo -e "${GREEN}  ✓ 链证书: $target_dir/chain.pem${NC}"
    fi

    # 创建符号链接（方便使用，可选）
    ln -sf "$target_dir/fullchain.pem" "$OUTPUT_DIR/$domain.pem" 2>/dev/null || true
    ln -sf "$target_dir/privkey.pem" "$OUTPUT_DIR/$domain.key" 2>/dev/null || true
    
    echo -e "${GREEN}证书已保存到: $target_dir${NC}"
}

# 续期单个证书（用于自动续期脚本）
renew_single_cert() {
    local domain_provider="$1"
    local domain=$(echo "$domain_provider" | cut -d':' -f1)
    local provider=$(echo "$domain_provider" | cut -d':' -f2)
    
    # 设置 DNS API
    setup_dns_api "$provider"
    
    # 续期证书
    "$ACME_SH_INSTALL_DIR/acme.sh" --renew -d "$domain" --dns "$PROVIDER_DNS" --force
    
    # 复制证书
    copy_cert "$domain"
}

# 主函数
main() {
    # 检查是否是续期模式
    if [ "$1" = "--renew-only" ] && [ -n "$2" ]; then
        load_config
        install_acme_sh
        export PATH="$ACME_SH_INSTALL_DIR:$PATH"
        source "$ACME_SH_INSTALL_DIR/acme.sh.env" 2>/dev/null || true
        renew_single_cert "$2"
        exit $?
    fi
    
    echo -e "${BLUE}========================================${NC}"
    echo -e "${BLUE}  XNode SSL Certificate Manager${NC}"
    echo -e "${BLUE}========================================${NC}"
    echo ""

    # 加载配置
    load_config

    # 安装 acme.sh
    install_acme_sh

    # 设置 acme.sh 路径
    export PATH="$ACME_SH_INSTALL_DIR:$PATH"
    source "$ACME_SH_INSTALL_DIR/acme.sh.env" 2>/dev/null || true

    # 批量处理域名
    local success_count=0
    local fail_count=0
    local total_count=0

    # 处理域名列表
    for domain_provider in "${DOMAINS_ARRAY[@]}"; do
        total_count=$((total_count + 1))
        if issue_cert "$domain_provider"; then
            success_count=$((success_count + 1))
        else
            fail_count=$((fail_count + 1))
        fi
    done

    # 输出统计
    echo ""
    echo -e "${BLUE}========================================${NC}"
    echo -e "${BLUE}处理完成${NC}"
    echo -e "${BLUE}========================================${NC}"
    echo -e "总计: $total_count"
    echo -e "${GREEN}成功: $success_count${NC}"
    echo -e "${RED}失败: $fail_count${NC}"
    echo ""
    echo -e "${GREEN}证书保存目录结构:${NC}"
    echo -e "${YELLOW}$OUTPUT_DIR/${NC}"
    for domain_provider in "${DOMAINS_ARRAY[@]}"; do
        domain=$(echo "$domain_provider" | cut -d':' -f1)
        if [ -d "$OUTPUT_DIR/$domain" ]; then
            echo -e "  ├── ${GREEN}$domain/${NC}"
            echo -e "  │   ├── fullchain.pem"
            echo -e "  │   ├── privkey.pem"
            echo -e "  │   └── chain.pem"
        fi
    done
    echo ""
    echo -e "${BLUE}提示: 每个域名都有独立的文件夹，方便管理${NC}"
    echo ""
}

# 运行主函数
main "$@"
