#!/usr/bin/env python3
from gevent import pywsgi
from flask import Flask, request, jsonify, render_template_string
import yaml
import subprocess
import threading
import os
import re
import socket

NETPLAN_FILE = "/etc/netplan/01-netcfg.yaml"
PORT = 6000  # 定义端口常量

app = Flask(__name__)

# 前端 HTML 模板
HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>网络配置</title>
<style>
    body { font-family: Arial, sans-serif; margin: 20px; }
    h2 { color: #333; }
    input { 
        margin: 5px 0 15px 0; 
        padding: 8px; 
        width: 300px; 
        border: 1px solid #ccc; 
        border-radius: 4px; 
    }
    button { 
        padding: 10px 20px; 
        background: #007bff; 
        color: white; 
        border: none; 
        border-radius: 4px; 
        cursor: pointer; 
        font-size: 16px;
    }
    button:hover { background: #0056b3; }
    .info { color: #666; margin-top: 10px; font-size: 14px; }
    .countdown { color: #dc3545; font-weight: bold; }
</style>
</head>
<body>
<h2>网络配置</h2>
<form id="netForm">
    IP地址: <br><input id="ip" value="{{ ip }}"><br>
    子网掩码位数: <br><input id="mask" value="{{ mask }}"><br>
    网关: <br><input id="gateway" value="{{ gateway }}"><br>
    DNS服务器: <br><input id="dns" value="{{ dns }}"><br>
    <button type="button" onclick="save()">保存配置</button>
    <div class="info">注意：保存后会有20秒时间确认，超时自动回滚</div>
</form>

<script>
function save() {
    const saveBtn = event.target;
    const originalText = saveBtn.textContent;
    const newIp = document.getElementById('ip').value;
    
    // 禁用按钮防止重复点击
    saveBtn.disabled = true;
    saveBtn.textContent = '保存中...';
    saveBtn.style.background = '#6c757d';
    
    fetch('/api/network', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
            ip: newIp,
            mask: document.getElementById('mask').value,
            gateway: document.getElementById('gateway').value,
            dns: document.getElementById('dns').value
        })
    })
    .then(res => res.json())
    .then(data => {
        if (data.success) {
            // 保存成功，显示倒计时并跳转
            let countdown = 15;
            const message = data.status + '\\n\\n将在 ' + countdown + ' 秒后跳转到新地址...';
            alert(message);
            
            // 创建倒计时显示
            const countdownDiv = document.createElement('div');
            countdownDiv.className = 'countdown';
            countdownDiv.innerHTML = '正在跳转到新IP地址，请稍候... <span id="countdown">' + countdown + '</span> 秒';
            document.body.appendChild(countdownDiv);
            
            // 倒计时并跳转
            const countdownInterval = setInterval(() => {
                countdown--;
                document.getElementById('countdown').textContent = countdown;
                
                if (countdown <= 0) {
                    clearInterval(countdownInterval);
                    // 跳转到新的IP地址，包含端口号
                    window.location.href = 'http://' + newIp + ':' + {{ port }};
                }
            }, 1000);
            
            // 同时尝试自动跳转（如果网络已生效）
            setTimeout(() => {
                window.location.href = 'http://' + newIp + ':' + {{ port }};
            }, 3000);
            
        } else {
            alert('保存失败: ' + data.status);
        }
    })
    .catch(error => {
        alert('保存失败: ' + error);
    })
    .finally(() => {
        // 恢复按钮状态
        saveBtn.disabled = false;
        saveBtn.textContent = originalText;
        saveBtn.style.background = '#007bff';
    });
}
</script>
</body>
</html>
"""

def is_valid_ip(ip):
    """验证IP地址格式"""
    if not ip:
        return False
    pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
    if re.match(pattern, ip):
        parts = ip.split('.')
        return all(0 <= int(part) <= 255 for part in parts)
    return False

def get_current_ip():
    """获取当前服务器的IP地址"""
    try:
        # 获取本机IP
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        current_ip = s.getsockname()[0]
        s.close()
        return current_ip
    except:
        return "127.0.0.1"

def read_netplan():
    """读取当前网络配置"""
    try:
        with open(NETPLAN_FILE) as f:
            data = yaml.safe_load(f)
        
        eth = data["network"]["ethernets"]["eth0"]
        addresses = eth.get("addresses", [""])
        
        if addresses and addresses[0]:
            ip = addresses[0].split("/")[0]
            mask = addresses[0].split("/")[1]
        else:
            ip = ""
            mask = "24"
            
        gateway = eth.get("routes", [{}])[0].get("via", "")
        dns = eth.get("nameservers", {}).get("addresses", [""])[0]
        
        return {"ip": ip, "mask": mask, "gateway": gateway, "dns": dns}
    except Exception as e:
        print(f"读取配置文件出错: {e}")
        return {"ip": "", "mask": "24", "gateway": "", "dns": "8.8.8.8"}

def apply_netplan_async(new_ip):
    """异步应用网络配置"""
    try:
        print(f"开始应用网络配置，新IP: {new_ip}")
        
        # 使用 netplan try 应用配置（20秒超时）
        process = subprocess.Popen(
            ["netplan", "try", "--timeout", "20"],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        
        # 自动确认（按回车键）
        try:
            stdout, stderr = process.communicate(input='\n', timeout=5)
            print(f"网络配置已应用，新IP: {new_ip}")
            print(f"输出: {stdout}")
            if stderr:
                print(f"错误: {stderr}")
                
            # 重启网络服务确保配置生效
            subprocess.run(["systemctl", "restart", "systemd-networkd"], 
                         capture_output=True, text=True)
            print("网络服务已重启")
            
        except subprocess.TimeoutExpired:
            # 如果用户没有手动确认，会自动回滚
            print("等待用户确认中...配置将在20秒后自动回滚")
            process.kill()
        except Exception as e:
            print(f"应用网络配置时出错: {e}")
            
    except Exception as e:
        print(f"启动netplan进程出错: {e}")

@app.route('/')
def index():
    net = read_netplan()
    current_ip = get_current_ip()
    print(f"当前访问IP: {request.remote_addr}, 服务器IP: {current_ip}")
    return render_template_string(HTML_TEMPLATE, **net, port=PORT)

@app.route('/api/network', methods=['POST'])
def set_network():
    try:
        data = request.json
        ip = data.get("ip", "").strip()
        mask = data.get("mask", "").strip()
        gateway = data.get("gateway", "").strip()
        dns = data.get("dns", "").strip()
        
        # 验证IP地址格式
        if not is_valid_ip(ip):
            return jsonify({
                "success": False,
                "status": "IP地址格式不正确"
            }), 400
            
        if not is_valid_ip(gateway):
            return jsonify({
                "success": False,
                "status": "网关地址格式不正确"
            }), 400
            
        if dns and not is_valid_ip(dns):
            return jsonify({
                "success": False,
                "status": "DNS地址格式不正确"
            }), 400
            
        # 基础验证
        if not all([ip, mask, gateway, dns]):
            return jsonify({
                "success": False,
                "status": "所有字段都必须填写"
            }), 400
        
        # 读取并更新配置
        with open(NETPLAN_FILE) as f:
            config = yaml.safe_load(f)
        
        # 更新网络配置
        eth = config["network"]["ethernets"]["eth0"]
        eth["dhcp4"] = False
        eth["addresses"] = [f"{ip}/{mask}"]
        eth["routes"] = [{"to": "default", "via": gateway}]
        eth["nameservers"] = {"addresses": [dns]}
        
        # 保存配置文件
        with open(NETPLAN_FILE, "w") as f:
            yaml.safe_dump(config, f, default_flow_style=False)
        
        print(f"配置文件已保存，新IP: {ip}")
        
        # 在后台异步应用网络配置
        threading.Thread(target=apply_netplan_async, args=(ip,), daemon=True).start()
        
        return jsonify({
            "success": True,
            "status": f"配置已保存并正在应用！\n\n请在20秒内按Enter键确认，否则配置会自动回滚。\n跳转地址: http://{ip}:{PORT}",
            "new_ip": ip,
            "port": PORT,
            "redirect_url": f"http://{ip}:{PORT}"
        })
        
    except PermissionError:
        return jsonify({
            "success": False,
            "status": "权限不足，请使用sudo运行此程序"
        }), 403
    except FileNotFoundError:
        return jsonify({
            "success": False,
            "status": f"配置文件 {NETPLAN_FILE} 不存在"
        }), 404
    except Exception as e:
        return jsonify({
            "success": False,
            "status": f"保存失败: {str(e)}"
        }), 500

@app.route('/check_connection')
def check_connection():
    """检查连接状态"""
    return jsonify({
        "status": "connected", 
        "message": "连接成功",
        "server_ip": get_current_ip(),
        "port": PORT
    })

def main():
    """主函数"""
    # 检查权限
    if os.geteuid() != 0:
        print("警告：请使用sudo运行此程序以获取必要的权限")
        return
    
    current_ip = get_current_ip()
    
    print("=" * 60)
    print("网络配置服务启动")
    print("=" * 60)
    print(f"当前服务器IP: {current_ip}")
    print(f"服务端口: {PORT}")
    print(f"访问地址: http://{current_ip}:{PORT}")
    print("=" * 60)
    print("保存配置后，会自动跳转到新的IP地址")
    print("注意：请确保防火墙允许端口访问")
    print("=" * 60)
    
    # 检查防火墙设置
    try:
        result = subprocess.run(["ufw", "status"], capture_output=True, text=True)
        if "Status: active" in result.stdout:
            print("检测到UFW防火墙，请确保端口已开放:")
            print(f"  sudo ufw allow {PORT}")
            print(f"  sudo ufw allow 22  # SSH端口（可选）")
    except:
        pass
    
    # 使用gevent WSGI服务器
    server = pywsgi.WSGIServer(('0.0.0.0', PORT), app)
    print(f"服务器已启动，监听端口: {PORT}")
    print("按 Ctrl+C 停止服务")
    
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\n服务已停止")
    except Exception as e:
        print(f"服务器错误: {e}")

if __name__ == "__main__":
    main()