#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
自动重启服务 - 每15天自动重启系统
功能：
1. 每15天自动重启系统
2. 重启前发送邮件通知
3. 记录最后重启时间，支持开机后重新计时
4. 支持作为Windows系统服务运行
"""

import os
import sys
import time
import json
import smtplib
import logging
import socket
from datetime import datetime, timedelta
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("auto_reboot.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

# 配置文件路径
CONFIG_FILE = "reboot_config.json"
STATE_FILE = "reboot_state.json"

class AutoRebootService:
    def __init__(self):
        self.config = self.load_config()
        self.state = self.load_state()
        self.reboot_interval_days = self.config.get("reboot_interval_days", 15)  # 从配置读取重启间隔天数，默认为15天
    
    def load_config(self):
        """加载配置文件"""
        default_config = {
            "email": {
                "smtp_server": "smtp.example.com",
                "smtp_port": 587,
                "username": "your_email@example.com",
                "password": "your_password",
                "sender": "your_email@example.com",
                "recipients": ["admin@example.com"]
            },
            "reboot_warning_hours": 24,  # 提前24小时发送警告邮件
            "reboot_warning_minutes": 30,  # 提前30分钟发送最终警告
            "check_interval_seconds": 3600,  # 每小时检查一次
            "reboot_interval_days": 15  # 重启间隔天数
        }
        
        try:
            if os.path.exists(CONFIG_FILE):
                with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    # 合并默认配置和用户配置
                    updated = False
                    for key, value in default_config.items():
                        if key not in config:
                            config[key] = value
                            updated = True
                        elif isinstance(value, dict):
                            for sub_key, sub_value in value.items():
                                if sub_key not in config[key]:
                                    config[key][sub_key] = sub_value
                                    updated = True
                    
                    # 如果配置有更新，保存更新后的配置
                    if updated:
                        with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
                            json.dump(config, f, indent=4, ensure_ascii=False)
                        logger.info('已更新配置文件，添加了必要的字段')
                    
                    return config
            
            # 如果配置文件不存在，创建默认配置文件
            with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
                json.dump(default_config, f, indent=4, ensure_ascii=False)
            logger.info(f"创建了默认配置文件: {CONFIG_FILE}")
            return default_config
        except Exception as e:
            logger.error(f"加载配置文件失败: {e}")
            return default_config
    
    def load_state(self):
        """加载状态文件，包含最后重启时间"""
        default_state = {
            "last_reboot_timestamp": time.time(),
            "last_warning_sent": 0,
            "final_warning_sent": False
        }
        
        try:
            if os.path.exists(STATE_FILE):
                with open(STATE_FILE, 'r', encoding='utf-8') as f:
                    state = json.load(f)
                    # 验证必要的键是否存在
                    for key, value in default_state.items():
                        if key not in state:
                            state[key] = value
                    return state
            
            # 如果状态文件不存在，创建默认状态文件
            with open(STATE_FILE, 'w', encoding='utf-8') as f:
                json.dump(default_state, f, indent=4, ensure_ascii=False)
            logger.info(f"创建了默认状态文件: {STATE_FILE}")
            return default_state
        except Exception as e:
            logger.error(f"加载状态文件失败: {e}")
            return default_state
    
    def save_state(self):
        """保存状态到文件"""
        try:
            with open(STATE_FILE, 'w', encoding='utf-8') as f:
                json.dump(self.state, f, indent=4, ensure_ascii=False)
            logger.info("状态已保存")
        except Exception as e:
            logger.error(f"保存状态文件失败: {e}")
    
    def send_email(self, subject, message):
        """发送邮件通知"""
        try:
            email_config = self.config['email']
            
            # 创建邮件对象
            msg = MIMEMultipart()
            msg['From'] = email_config['sender']
            msg['To'] = ', '.join(email_config['recipients'])
            msg['Subject'] = subject
            
            # 添加邮件正文
            msg.attach(MIMEText(message, 'plain', 'utf-8'))
            
            # 连接SMTP服务器并发送邮件
            with smtplib.SMTP(email_config['smtp_server'], email_config['smtp_port']) as server:
                server.starttls()  # 启用TLS加密
                server.login(email_config['username'], email_config['password'])
                server.send_message(msg)
            
            logger.info(f"邮件已发送: {subject}")
            return True
        except Exception as e:
            logger.error(f"发送邮件失败: {e}")
            return False
    
    def should_reboot(self):
        """检查是否应该重启系统"""
        # 重新加载配置，确保使用最新的重启间隔
        self.config = self.load_config()
        self.reboot_interval_days = self.config.get("reboot_interval_days", 15)
        
        last_reboot = self.state['last_reboot_timestamp']
        now = time.time()
        interval_seconds = self.reboot_interval_days * 24 * 3600
        
        return now - last_reboot >= interval_seconds
    
    def time_until_reboot(self):
        """计算距离下次重启的时间（秒）"""
        # 重新加载配置，确保使用最新的重启间隔
        self.config = self.load_config()
        self.reboot_interval_days = self.config.get("reboot_interval_days", 15)
        
        last_reboot = self.state['last_reboot_timestamp']
        now = time.time()
        interval_seconds = self.reboot_interval_days * 24 * 3600
        
        time_since_last = now - last_reboot
        time_until = interval_seconds - time_since_last
        
        return max(0, time_until)
    
    def format_time_remaining(self, seconds):
        """格式化剩余时间为可读字符串"""
        days = seconds // (24 * 3600)
        hours = (seconds % (24 * 3600)) // 3600
        minutes = (seconds % 3600) // 60
        
        if days > 0:
            return f"{int(days)}天{int(hours)}小时"
        elif hours > 0:
            return f"{int(hours)}小时{int(minutes)}分钟"
        else:
            return f"{int(minutes)}分钟"
    
    def check_and_notify(self):
        """检查重启时间并发送通知"""
        time_until = self.time_until_reboot()
        warning_hours = self.config['reboot_warning_hours'] * 3600
        final_warning_minutes = self.config['reboot_warning_minutes'] * 60
        
        # 发送24小时警告
        if (time_until <= warning_hours and time_until > warning_hours - 3600 and 
            time.time() - self.state['last_warning_sent'] > 3600):
            hostname = socket.gethostname()
            subject = f"[警告] 服务器 {hostname} 将在24小时内重启"
            message = f"尊敬的管理员：\n\n服务器 {hostname} 计划在约24小时后重启。\n\n"
            message += f"当前时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
            message += f"预计重启时间：{(datetime.now() + timedelta(seconds=time_until)).strftime('%Y-%m-%d %H:%M:%S')}\n\n"
            message += "请确保所有重要工作已保存。\n\n此致\n自动重启服务"
            
            if self.send_email(subject, message):
                self.state['last_warning_sent'] = time.time()
                self.save_state()
        
        # 发送最终警告
        if (time_until <= final_warning_minutes and not self.state['final_warning_sent']):
            hostname = socket.gethostname()
            subject = f"[紧急] 服务器 {hostname} 将在{final_warning_minutes//60}分钟内重启"
            message = f"尊敬的管理员：\n\n服务器 {hostname} 计划在{final_warning_minutes//60}分钟后重启。\n\n"
            message += f"当前时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
            message += f"预计重启时间：{(datetime.now() + timedelta(seconds=time_until)).strftime('%Y-%m-%d %H:%M:%S')}\n\n"
            message += "请立即保存所有工作！\n\n此致\n自动重启服务"
            
            if self.send_email(subject, message):
                self.state['final_warning_sent'] = True
                self.save_state()
    
    def perform_reboot(self):
        """执行系统重启"""
        try:
            hostname = socket.gethostname()
            logger.info(f"执行系统重启: {hostname}")
            
            # 在Windows系统上重启
            if sys.platform == 'win32':
                os.system('shutdown /r /t 60 /c "自动重启服务：定期系统维护"')
            else:
                # Linux/macOS系统
                os.system('shutdown -r +1 "自动重启服务：定期系统维护"')
            
            # 更新重启时间
            self.state['last_reboot_timestamp'] = time.time()
            self.state['final_warning_sent'] = False
            self.save_state()
            
            return True
        except Exception as e:
            logger.error(f"执行重启失败: {e}")
            return False
    
    def run(self):
        """主运行循环"""
        logger.info("自动重启服务已启动")
        
        while True:
            try:
                # 检查并发送通知
                self.check_and_notify()
                
                # 检查是否需要重启
                if self.should_reboot():
                    logger.info("达到重启时间，准备重启系统")
                    # 发送最终重启通知
                    hostname = socket.gethostname()
                    self.send_email(
                        f"[执行中] 服务器 {hostname} 正在重启",
                        f"尊敬的管理员：\n\n服务器 {hostname} 正在重启。\n\n"
                        f"重启时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
                        "重启完成后，服务将自动重新开始计时。\n\n此致\n自动重启服务"
                    )
                    # 执行重启
                    self.perform_reboot()
                else:
                    # 记录日志
                    time_remaining = self.time_until_reboot()
                    logger.info(f"距离下次重启还有: {self.format_time_remaining(time_remaining)}")
                
                # 等待指定的检查间隔
                time.sleep(self.config['check_interval_seconds'])
            
            except KeyboardInterrupt:
                logger.info("服务被手动停止")
                break
            except Exception as e:
                logger.error(f"服务运行出错: {e}")
                # 出错后等待一段时间再继续
                time.sleep(60)
    
    def test_email(self):
        """测试邮件发送功能"""
        hostname = socket.gethostname()
        subject = f"[测试] 服务器 {hostname} 自动重启服务测试邮件"
        message = f"尊敬的管理员：\n\n这是来自服务器 {hostname} 的自动重启服务测试邮件。\n\n"
        message += f"发送时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
        message += "如果您收到此邮件，说明邮件配置正确。\n\n此致\n自动重启服务"
        
        return self.send_email(subject, message)


def main():
    """主函数"""
    service = AutoRebootService()
    
    # 处理命令行参数
    if len(sys.argv) > 1:
        if sys.argv[1] == '--test-email':
            logger.info("发送测试邮件...")
            success = service.test_email()
            sys.exit(0 if success else 1)
        elif sys.argv[1] == '--config':
            logger.info(f"配置文件路径: {os.path.abspath(CONFIG_FILE)}")
            logger.info(f"状态文件路径: {os.path.abspath(STATE_FILE)}")
            # 打印当前配置
            logger.info("当前配置:")
            for key, value in service.config.items():
                if key == 'email' and 'password' in value:
                    # 隐藏密码
                    value = value.copy()
                    value['password'] = '******'
                logger.info(f"  {key}: {value}")
            sys.exit(0)
    
    # 正常运行服务
    service.run()


if __name__ == "__main__":
    main()