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

"""
Geyser Standalone Auto-Updater & MCSM Auto-Restart Script 
Server: Linux / MCSManager 10+
"""

import os
import sys
import json
import time
import hashlib
import urllib.request
import urllib.error
import urllib.parse

# 1. 基础路径配置（根据你的实际部署路径修改）
GEYSER_DIR = "YOUR_GEYSER_DIR_KEY_HERE"
JAR_PATH = os.path.join(GEYSER_DIR, "Geyser-Standalone.jar")
VERSION_FILE = os.path.join(GEYSER_DIR, ".geyser_version.json")
LOG_FILE = "/var/log/geyser_autoupdate.log"

# 2. Geyser 官方构建与下载 API
API_URL = "https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest"
DOWNLOAD_URL = "https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/standalone"

# 3. MCSManager Web 面板配置
MCSM_WEB_URL = "http://localhost:23333"              # MCSManager Web 访问地址
MCSM_API_KEY = "YOUR_MCSM_API_KEY_HERE"             # 在 MCSManager 用户管理中生成的 API Key
DAEMON_ID = "YOUR_DAEMON_ID_HERE"                   # 实例所在的节点 ID (Daemon ID)
INSTANCE_UUID = "YOUR_INSTANCE_UUID_HERE"           # Geyser 实例的 UUID

def log(msg):
    ts = time.strftime("%Y-%m-%d %H:%M:%S")
    entry = f"[{ts}] {msg}"
    print(entry)
    try:
        os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
        with open(LOG_FILE, "a", encoding="utf-8") as f:
            f.write(entry + "\n")
    except Exception as e:
        print(f"Failed to write log: {e}")

def get_current_local_info():
    if os.path.exists(VERSION_FILE):
        try:
            with open(VERSION_FILE, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception:
            pass
    return {"build": 0, "version": "", "sha256": ""}

def save_local_info(info):
    try:
        with open(VERSION_FILE, "w", encoding="utf-8") as f:
            json.dump(info, f, indent=2)
    except Exception as e:
        log(f"Failed to save local version info: {e}")

def fetch_remote_build_info():
    req = urllib.request.Request(API_URL, headers={"User-Agent": "GeyserAutoUpdater/2.0"})
    with urllib.request.urlopen(req, timeout=15) as resp:
        if resp.status == 200:
            return json.loads(resp.read().decode("utf-8"))
    return None

def calculate_sha256(file_path):
    h = hashlib.sha256()
    with open(file_path, "rb") as f:
        while chunk := f.read(65536):
            h.update(chunk)
    return h.hexdigest()

def restart_geyser_instance():
    log("Triggering Geyser restart via MCSM Web API...")
    params = urllib.parse.urlencode({
        "daemonId": DAEMON_ID,
        "uuid": INSTANCE_UUID,
        "apikey": MCSM_API_KEY
    })
    url = f"{MCSM_WEB_URL}/api/protected_instance/restart?{params}"
    
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "GeyserAutoUpdater/2.0"})
        with urllib.request.urlopen(req, timeout=15) as resp:
            data = json.loads(resp.read().decode("utf-8"))
            if data.get("status") == 200:
                log("MCSManager restart command executed successfully via Web API.")
                return True
            else:
                log(f"MCSManager restart Web API returned non-200: {data}")
                return False
    except Exception as e:
        log(f"Error calling MCSM Web API: {e}")
        return False

def main():
    force = "--force" in sys.argv
    log("=== Checking for Geyser updates ===")

    try:
        remote_info = fetch_remote_build_info()
    except Exception as e:
        log(f"Failed to query remote build info: {e}")
        return

    if not remote_info:
        log("Empty response from Geyser API.")
        return

    remote_build = remote_info.get("build")
    remote_version = remote_info.get("version")
    remote_sha256 = remote_info.get("downloads", {}).get("standalone", {}).get("sha256", "")

    local_info = get_current_local_info()
    local_build = local_info.get("build", 0)

    log(f"Remote version: {remote_version} (Build {remote_build}) | Local build: {local_build}")

    needs_update = force or (remote_build > local_build) or not os.path.exists(JAR_PATH)

    if not needs_update:
        log("Geyser is already up-to-date. No action needed.")
        return

    log(f"New build detected: Build {remote_build} (Current: {local_build}). Downloading update...")

    temp_jar = JAR_PATH + ".tmp"
    try:
        req = urllib.request.Request(DOWNLOAD_URL, headers={"User-Agent": "GeyserAutoUpdater/2.0"})
        with urllib.request.urlopen(req, timeout=60) as resp, open(temp_jar, "wb") as out_file:
            while chunk := resp.read(65536):
                out_file.write(chunk)

        # SHA-256 完整性校验
        if remote_sha256:
            downloaded_sha256 = calculate_sha256(temp_jar)
            if downloaded_sha256.lower() != remote_sha256.lower():
                log(f"Checksum mismatch! Expected {remote_sha256}, got {downloaded_sha256}. Aborting.")
                if os.path.exists(temp_jar):
                    os.remove(temp_jar)
                return

        # 替换现有 Jar
        os.replace(temp_jar, JAR_PATH)
        save_local_info({
            "build": remote_build,
            "version": remote_version,
            "sha256": remote_sha256,
            "updated_at": time.strftime("%Y-%m-%d %H:%M:%S")
        })
        log(f"Geyser-Standalone.jar successfully updated to Build {remote_build} ({remote_version}).")

        # 触发面板热重启
        restart_geyser_instance()

    except Exception as e:
        log(f"Update failed: {e}")
        if os.path.exists(temp_jar):
            try:
                os.remove(temp_jar)
            except Exception:
                pass

if __name__ == "__main__":
    main()
