#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import json
import ssl
import urllib.request
import subprocess
import configparser
import pwd
from tkinter import Tk, Label, Entry, Button, Frame, LabelFrame, messagebox, StringVar

# 自动修复 pkexec 下运行 GUI 缺少 X11 授权与 DISPLAY 的问题
if os.name != 'nt' and os.geteuid() == 0 and 'PKEXEC_UID' in os.environ:
    try:
        pkexec_uid = int(os.environ['PKEXEC_UID'])
        user_info = pwd.getpwuid(pkexec_uid)
        username = user_info.pw_name
        user_home = user_info.pw_dir
        
        # 自动补全 XAUTHORITY 凭证路径
        xauth_path = os.path.join(user_home, '.Xauthority')
        if os.path.exists(xauth_path):
            os.environ['XAUTHORITY'] = xauth_path
            
        # 自动补全 DISPLAY 变量
        if 'DISPLAY' not in os.environ or not os.environ['DISPLAY']:
            os.environ['DISPLAY'] = ':0'
            
        print(f"[Polkit-Helper] Setup X11: DISPLAY={os.environ.get('DISPLAY')} XAUTHORITY={os.environ.get('XAUTHORITY')}")
    except Exception as e:
        sys.stderr.write(f"[Polkit-Helper] Failed to setup X11 environment: {e}\n")

# 忽略 SSL 证书
ssl_context = ssl._create_unverified_context()

class RegisterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Kylin 资产管理客户端注册")
        self.root.geometry("500x480")
        self.root.resizable(False, False)
        
        # 居中显示窗口
        self.center_window()
        
        # 设置配色：科技感深蓝底色与白底卡片
        self.bg_color = "#f3f4f6"
        self.primary_color = "#1e3a8a" # 科技深蓝
        self.text_color = "#1f2937"
        self.root.config(bg=self.bg_color)
        
        # 加载已有配置
        self.config_data = self.load_existing_config()
        
        # 绘制 UI 界面
        self.create_widgets()
        
    def center_window(self):
        self.root.update_idletasks()
        width = 500
        height = 480
        x = (self.root.winfo_screenwidth() // 2) - (width // 2)
        y = (self.root.winfo_screenheight() // 2) - (height // 2)
        self.root.geometry(f'{width}x{height}+{x}+{y}')

    def load_existing_config(self):
        config = {
            'server_url': 'http://localhost:5000',
            'asset_no': '',
            'department': '',
            'user': '',
            'location': '',
            'remark': ''
        }
        paths = ['/etc/asset-agent/agent.conf', 'agent.conf', 'agent/agent.conf']
        for p in paths:
            if os.path.exists(p):
                try:
                    parser = configparser.ConfigParser()
                    parser.read(p, encoding='utf-8')
                    if 'agent' in parser:
                        sect = parser['agent']
                        config['server_url'] = sect.get('server_url', config['server_url'])
                        config['asset_no'] = sect.get('asset_no', config['asset_no'])
                        config['department'] = sect.get('department', config['department'])
                        config['user'] = sect.get('user', config['user'])
                        config['location'] = sect.get('location', config['location'])
                        config['remark'] = sect.get('remark', config['remark'])
                    break
                except Exception:
                    pass
        return config

    def create_widgets(self):
        # 顶部 Banner
        banner_frame = Frame(self.root, bg=self.primary_color, height=60)
        banner_frame.pack(fill="x", side="top")
        banner_frame.pack_propagate(False)
        
        banner_label = Label(banner_frame, text="💻 Kylin 硬件资产安全注册终端", fg="white", bg=self.primary_color, font=("Helvetica", 14, "bold"))
        banner_label.pack(pady=15)

        # 表单区域
        form_frame = Frame(self.root, bg=self.bg_color, padx=20, pady=15)
        form_frame.pack(fill="both", expand=True)

        # 1. 服务端配置
        server_group = LabelFrame(form_frame, text=" 基础网络配置 ", bg=self.bg_color, fg=self.primary_color, font=("Helvetica", 10, "bold"), padx=10, pady=10)
        server_group.pack(fill="x", pady=(0, 10))

        Label(server_group, text="服务端 IP/域名及端口:", bg=self.bg_color, fg=self.text_color).grid(row=0, column=0, sticky="w")
        self.server_url_var = StringVar(value=self.config_data['server_url'])
        self.server_url_entry = Entry(server_group, textvariable=self.server_url_var, width=32, font=("Helvetica", 10))
        self.server_url_entry.grid(row=0, column=1, padx=(10, 0), sticky="ew")

        # 2. 静态业务属性配置
        biz_group = LabelFrame(form_frame, text=" 资产业务归属录入 (可选) ", bg=self.bg_color, fg=self.primary_color, font=("Helvetica", 10, "bold"), padx=10, pady=10)
        biz_group.pack(fill="x", pady=5)

        labels = [
            ("资产编号 (Asset ID):", 'asset_no'),
            ("使用部门 (Department):", 'department'),
            ("责任人 (Responsible User):", 'user'),
            ("物理位置 (Location):", 'location'),
            ("备注信息 (Remark):", 'remark')
        ]

        self.vars = {}
        for idx, (label_text, key) in enumerate(labels):
            Label(biz_group, text=label_text, bg=self.bg_color, fg=self.text_color).grid(row=idx, column=0, sticky="w", pady=4)
            self.vars[key] = StringVar(value=self.config_data[key])
            Entry(biz_group, textvariable=self.vars[key], width=32, font=("Helvetica", 10)).grid(row=idx, column=1, padx=(10, 0), sticky="ew", pady=4)

        # 状态显示区
        self.status_label = Label(form_frame, text="准备就绪，请填写配置后点击下方按钮开始注册。", bg=self.bg_color, fg="#64748b", font=("Helvetica", 9))
        self.status_label.pack(pady=10)

        # 操作按钮
        btn_frame = Frame(form_frame, bg=self.bg_color)
        btn_frame.pack(fill="x", side="bottom")

        btn_register = Button(btn_frame, text="🚀 开始资产注册并进行首次上报", bg="#1d4ed8", fg="white", font=("Helvetica", 11, "bold"), activebackground="#1e40af", activeforeground="white", cursor="hand2", command=self.handle_register, relief="flat", padx=0, pady=6)
        btn_register.pack(fill="x", side="top", pady=5)

        btn_close = Button(btn_frame, text="安全退出", bg="#64748b", fg="white", font=("Helvetica", 10), activebackground="#475569", activeforeground="white", cursor="hand2", command=self.root.quit, relief="flat", padx=0, pady=4)
        btn_close.pack(fill="x", side="top")

    def handle_register(self):
        server_url = self.server_url_var.get().strip().rstrip('/')
        if not server_url.startswith("http://") and not server_url.startswith("https://"):
            messagebox.showerror("输入错误", "服务端 URL 格式错误！必须以 http:// 或 https:// 开头。")
            return

        self.status_label.config(text="正在检测服务端网络握手...", fg="#2563eb")
        self.root.update()

        # 1. 预先测试向服务端发送空测试或版本校验
        try:
            req = urllib.request.Request(
                f"{server_url}/api/report",
                data=json.dumps({"mac": "test-connection"}).encode('utf-8'),
                headers={'Content-Type': 'application/json'}
            )
            with urllib.request.urlopen(req, context=ssl_context, timeout=5) as resp:
                resp.read()
        except Exception as e:
            self.status_label.config(text="网络异常：无法连接至指定的服务端", fg="#dc2626")
            messagebox.showerror("连接失败", f"无法连通服务端管理节点:\n{e}\n\n请核实服务端 IP 与端口是否填写正确，且服务端已开启。")
            return

        # 2. 准备配置文件内容
        config_text = f"""[agent]
server_url = {server_url}
report_interval = 300
asset_no = {self.vars['asset_no'].get().strip()}
department = {self.vars['department'].get().strip()}
user = {self.vars['user'].get().strip()}
location = {self.vars['location'].get().strip()}
remark = {self.vars['remark'].get().strip()}
"""

        has_root_permission = (os.name != 'nt' and os.geteuid() == 0)

        if has_root_permission:
            self.status_label.config(text="正在写入系统配置文件...", fg="#2563eb")
            self.root.update()
            try:
                os.makedirs("/etc/asset-agent", exist_ok=True)
                with open("/etc/asset-agent/agent.conf", 'w', encoding='utf-8') as f:
                    f.write(config_text)
                
                # 3. 触发首次硬件采集上报 (调用系统命令)
                self.status_label.config(text="正在进行首次数据上报，请稍候...", fg="#2563eb")
                self.root.update()
                cmd = "/usr/bin/asset-agent"
                if not os.path.exists(cmd):
                    cmd = f"{sys.executable} agent/asset-agent.py"
                
                res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=20)
                if res.returncode == 0:
                    self.status_label.config(text="注册与上报成功！定时任务已拉起。", fg="#16a34a")
                    messagebox.showinfo("注册成功", "资产注册成功！已完成首次硬件数据上报。\n后台 systemd 服务将自动维持定时上报任务。")
                    self.root.quit()
                else:
                    err_msg = res.stderr.decode('utf-8', errors='ignore').strip() or res.stdout.decode('utf-8', errors='ignore').strip()
                    self.status_label.config(text="首次上报失败，操作未闭环", fg="#dc2626")
                    messagebox.showerror("上报失败", f"注册配置成功，但首次采集上报命令执行错误：\n{err_msg}")
            except Exception as e:
                messagebox.showerror("写入失败", f"写入系统配置时发生异常: {e}")
                self.status_label.config(text="配置写入失败", fg="#dc2626")
        else:
            # 普通用户身份启动，必须通过命令行 pkexec 提权来覆盖写入 /etc/ 目录与触发采集
            self.status_label.config(text="正在通过管理员凭据保存并上报...", fg="#2563eb")
            self.root.update()

            tmp_conf = "/tmp/asset-agent-temp.conf"
            try:
                with open(tmp_conf, 'w', encoding='utf-8') as f:
                    f.write(config_text)
            except Exception as e:
                messagebox.showerror("本地保存失败", f"无法创建本地临时配置文件: {e}")
                self.status_label.config(text="本地临时保存失败", fg="#dc2626")
                return

            messagebox.showinfo("管理员授权", "保存全局配置文件与注册资产服务需要管理员权限。\n点击确认后，系统将弹出密码验证窗口，请输入管理员/普通用户密码以完成注册。")
            
            # 使用 pkexec 命令行包装以完美避开 X11 授权限制
            cmd = 'pkexec bash -c "mkdir -p /etc/asset-agent && cp /tmp/asset-agent-temp.conf /etc/asset-agent/agent.conf && chmod 644 /etc/asset-agent/agent.conf && /usr/bin/asset-agent"'
            try:
                res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30)
                if res.returncode == 0:
                    self.status_label.config(text="管理员授权注册与上报成功！", fg="#16a34a")
                    messagebox.showinfo("注册成功", "资产注册成功！已通过管理员凭据完成全局配置并触发首次硬件上报。")
                    self.root.quit()
                else:
                    err_msg = res.stderr.decode('utf-8', errors='ignore').strip() or res.stdout.decode('utf-8', errors='ignore').strip()
                    if res.returncode in [126, 127]:
                        self.status_label.config(text="身份验证未通过：操作已取消", fg="#dc2626")
                        messagebox.showwarning("验证未通过", "未提供正确的管理员密码，无法保存配置。")
                    else:
                        self.status_label.config(text="提权写入失败", fg="#dc2626")
                        messagebox.showerror("授权失败", f"管理员提权操作失败 (状态码 {res.returncode})：\n{err_msg}")
            except Exception as e:
                self.status_label.config(text="无法调起管理员验证", fg="#dc2626")
                messagebox.showerror("提权执行异常", f"无法唤起身份验证工具: {e}")
            finally:
                try:
                    if os.path.exists(tmp_conf):
                        os.remove(tmp_conf)
                except Exception:
                    pass

def main():
    root = Tk()
    app = RegisterApp(root)
    root.mainloop()

if __name__ == '__main__':
    main()