#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
SSL 证书申请与自动续期脚本
使用 certbot 工具申请 Let's Encrypt 证书并配置 nginx

Author: emotion-museum
Created: 2026-03-17
Purpose: SSL 证书申请与自动续期 - 使用 certbot 配置 Let's Encrypt 证书
"""

import argparse
import subprocess
import sys
from pathlib import Path

DOMAIN = "lifescript.happylifeos.com"
CERT_PATH = f"/etc/letsencrypt/live/{DOMAIN}/fullchain.pem"
KEY_PATH = f"/etc/letsencrypt/live/{DOMAIN}/privkey.pem"


def log_info(msg):
    """输出信息日志"""
    print(f"\033[32m[INFO]\033[0m {msg}")


def log_warn(msg):
    """输出警告日志"""
    print(f"\033[33m[WARN]\033[0m {msg}")


def log_error(msg):
    """输出错误日志"""
    print(f"\033[31m[ERROR]\033[0m {msg}")


def run_command(cmd, check=True):
    """执行 shell 命令

    Args:
        cmd: 要执行的命令
        check: 是否检查返回码，False 时总是返回 True

    Returns:
        bool: 命令是否执行成功
    """
    try:
        result = subprocess.run(
            cmd, shell=True, check=check, capture_output=False, text=True
        )
        return result.returncode == 0
    except subprocess.CalledProcessError as e:
        log_error(f"命令执行失败：{e}")
        return False


def check_certbot():
    """检查 certbot 是否安装

    Returns:
        bool: certbot 是否已安装
    """
    log_info("检查 certbot 是否安装...")
    if not run_command("which certbot", check=False):
        log_error("certbot 未安装，请先安装：apt install certbot python3-certbot-nginx")
        return False
    log_info("certbot 已安装")
    return True


def check_nginx():
    """检查 nginx 配置是否正确

    Returns:
        bool: nginx 配置是否正确
    """
    log_info("检查 nginx 配置...")
    if not run_command("nginx -t", check=False):
        log_error("nginx 配置有误，请先修复")
        return False
    log_info("nginx 配置正确")
    return True


def apply_certificate(dry_run=False):
    """申请 SSL 证书

    Args:
        dry_run: 是否使用 dry-run 模式

    Returns:
        bool: 证书申请是否成功
    """
    log_info(f"开始为 {DOMAIN} 申请 SSL 证书...")

    cmd = f"certbot --nginx -d {DOMAIN} --non-interactive --agree-tos --email admin@happylifeos.com"
    if dry_run:
        cmd += " --dry-run"
        log_info(f"[DRY RUN] 执行：{cmd}")

    if run_command(cmd):
        log_info("证书申请成功")
        return True
    else:
        log_error("证书申请失败")
        return False


def setup_auto_renewal():
    """配置自动续期

    Returns:
        bool: 配置是否成功
    """
    log_info("配置证书自动续期...")

    # 检查是否已存在 systemd timer
    if run_command("systemctl list-timers | grep certbot", check=False):
        log_info("certbot 自动续期 timer 已存在")
        return True

    # 启用 systemd timer
    if run_command("systemctl enable certbot.timer && systemctl start certbot.timer"):
        log_info("已启用 certbot 自动续期 timer")
        return True
    else:
        log_warn("systemd timer 配置失败，尝试配置 cron 任务")
        return setup_cron_renewal()


def setup_cron_renewal():
    """配置 cron 自动续期

    Returns:
        bool: cron 配置是否成功
    """
    cron_job = "0 3 1 * * certbot renew --quiet --deploy-hook 'systemctl reload nginx'"
    log_info(f"添加 cron 任务：{cron_job}")

    # 添加到 crontab
    result = subprocess.run(
        f"(crontab -l 2>/dev/null | grep -v '{DOMAIN}'; echo '{cron_job}') | crontab -",
        shell=True, capture_output=True, text=True
    )

    if result.returncode == 0:
        log_info("cron 任务添加成功")
        return True
    else:
        log_error("cron 任务添加失败")
        return False


def verify_certificate():
    """验证证书是否有效

    Returns:
        bool: 证书是否有效
    """
    log_info("验证 SSL 证书...")
    cmd = f'echo | openssl s_client -connect {DOMAIN}:443 -servername {DOMAIN} 2>/dev/null | openssl x509 -noout -dates'

    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    if result.returncode == 0:
        log_info(f"证书信息:\n{result.stdout}")
        return True
    else:
        log_warn("无法验证证书（可能域名 DNS 尚未生效）")
        return False


def main():
    """主函数

    Returns:
        int: 退出码，0 表示成功，非 0 表示失败
    """
    parser = argparse.ArgumentParser(description="SSL 证书申请与自动续期脚本")
    parser.add_argument("--dry-run", action="store_true", help="模拟运行，不实际申请证书")
    parser.add_argument("--renew", action="store_true", help="手动续期证书")
    parser.add_argument("--verify", action="store_true", help="验证证书状态")
    parser.add_argument("--setup-renewal", action="store_true", help="仅配置自动续期")
    args = parser.parse_args()

    # 验证模式
    if args.verify:
        verify_certificate()
        return 0

    # 仅配置自动续期
    if args.setup_renewal:
        setup_auto_renewal()
        return 0

    # 手动续期
    if args.renew:
        run_command("certbot renew --force-renewal")
        run_command("systemctl reload nginx")
        return 0

    # 完整流程
    log_info("=== SSL 证书申请流程 ===")

    if not check_certbot():
        return 1

    if not check_nginx():
        return 1

    if not apply_certificate(dry_run=args.dry_run):
        return 1

    if not args.dry_run:
        setup_auto_renewal()
        verify_certificate()

    log_info("=== SSL 证书申请完成 ===")
    return 0


if __name__ == "__main__":
    sys.exit(main())
