"""
AutoPOE2 - Process Supervisor Engine & Shadow Binary Controller
================================================================
Điều phối sinh tử tiến trình tự hành, cách ly nhị phân chạy ngầm (Shadow Binary),
triệt tiêu xung đột build LNK1104, và cung cấp Control Plane thời gian thực qua Shared Memory.

Kiến trúc:
1. Shadow Binary Execution: Tự động copy AutoPOE2_Core.exe -> AutoPOE2_Core_Runtime.exe
   để tiến trình C++ Core chạy độc lập, giải phóng hoàn toàn file gốc cho Dev/AI build liên tục.
2. SSoT Telemetry: Đọc trực tiếp từ Local\\POE2_Auto_SharedMem_v1 (Zero file I/O, Zero Hallucination).
3. Hot-Swap Engine: Tự động nhận diện bản build mới đã qua kiểm thử, chuyển giao an toàn tại Hideout.
4. Local Control Plane: Cung cấp REST/JSON endpoint (mặc định port 8088) phục vụ truy vấn & điều khiển.
"""

import os
import sys
import time
import json
import shutil
import hashlib
import argparse
import subprocess
import threading
import ctypes
from pathlib import Path
from http.server import HTTPServer, BaseHTTPRequestHandler
from typing import Optional, Dict, Any, List

# Đảm bảo đường dẫn import gốc
WORKSPACE_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(WORKSPACE_ROOT))

from src.common.win32_process import is_poe2_running, find_process_by_name, POE2_PROCESS_NAMES
from src.common.win32_window import find_poe2_window
from src.common.virtual_display_manager import (
    get_virtual_monitor,
    is_virtual_display_active,
    move_window_to_virtual_display,
    is_window_on_virtual_display,
    capture_virtual_display_jpeg,
)
from src.assistant_tool.client_launcher import Poe2ClientLauncher
from src.assistant_tool.core_shm_bridge import CoreShmBridge, CharacterLifecycleState
from src.assistant_tool.graphics_optimizer import Poe2GraphicsOptimizer, PROFILES

CANDIDATE_SOURCE_BINS = [
    WORKSPACE_ROOT / "bin" / "Release" / "AutoPOE2_Core.exe",
    WORKSPACE_ROOT / "build" / "bin" / "Release" / "AutoPOE2_Core.exe",
    WORKSPACE_ROOT / "bin" / "AutoPOE2_Core.exe",
]

def find_default_source_bin() -> Path:
    for p in CANDIDATE_SOURCE_BINS:
        if p.exists():
            return p
    return CANDIDATE_SOURCE_BINS[0]

DEFAULT_SOURCE_BIN = find_default_source_bin()
DEFAULT_RUNTIME_BIN = DEFAULT_SOURCE_BIN.parent / "AutoPOE2_Core_Runtime.exe"
DEFAULT_HTTP_PORT = 8088


def calculate_sha256(filepath: Path) -> str:
    """Tính mã băm SHA256 của tệp nhị phân."""
    if not filepath.exists():
        return ""
    h = hashlib.sha256()
    with open(filepath, "rb") as f:
        while chunk := f.read(65536):
            h.update(chunk)
    return h.hexdigest()


def get_standby_jpeg_frame() -> bytes:
    """Tạo frame JPEG standby khi game client offline hoặc chưa render."""
    try:
        from PIL import Image, ImageDraw
        import io
        img = Image.new("RGB", (1280, 720), color=(15, 17, 23))
        draw = ImageDraw.Draw(img)
        draw.text((450, 340), "AutoPOE2 Standby (Game Client Offline / Background)", fill=(148, 163, 184))
        buf = io.BytesIO()
        img.save(buf, format="JPEG", quality=70)
        return buf.getvalue()
    except Exception:
        return b""


class Poe2Supervisor:
    """Bộ điều phối và giám sát tiến trình AutoPOE2."""

    def __init__(
        self,
        source_bin: Path = DEFAULT_SOURCE_BIN,
        runtime_bin: Path = DEFAULT_RUNTIME_BIN,
        http_port: int = DEFAULT_HTTP_PORT,
        auto_launch_game: bool = False,
        use_virtual_display: bool = False,
    ):
        self.source_bin = Path(source_bin)
        self.runtime_bin = Path(runtime_bin)
        self.http_port = http_port
        self.auto_launch_game = auto_launch_game
        self.use_virtual_display = use_virtual_display

        self.shm_bridge = CoreShmBridge()
        self.runtime_proc: Optional[subprocess.Popen] = None
        self._running = False
        self._watchdog_thread: Optional[threading.Thread] = None
        self._http_server: Optional[HTTPServer] = None
        self._http_thread: Optional[threading.Thread] = None

        self._paused = False
        self._lock = threading.Lock()
        self._last_source_hash = ""
        self._game_detected_time: Optional[float] = None

    def prepare_shadow_binary(self, force: bool = False) -> bool:
        """
        Sao chép AutoPOE2_Core.exe sang AutoPOE2_Core_Runtime.exe.
        Giải phóng file gốc để trình biên dịch MSVC/CMake có thể build bất kỳ lúc nào.
        """
        if not self.source_bin.exists():
            return False

        source_hash = calculate_sha256(self.source_bin)
        if not force and self.runtime_bin.exists():
            runtime_hash = calculate_sha256(self.runtime_bin)
            if source_hash == runtime_hash:
                self._last_source_hash = source_hash
                return True

        # Nếu runtime đang chạy, không thể copy đè trực tiếp mà phải dừng hoặc đổi tên
        if self.is_runtime_running():
            self.stop_runtime()

        # Thực hiện copy có retry chống transient lock
        for attempt in range(5):
            try:
                self.runtime_bin.parent.mkdir(parents=True, exist_ok=True)
                shutil.copy2(self.source_bin, self.runtime_bin)
                self._last_source_hash = source_hash
                return True
            except PermissionError:
                time.sleep(0.5)
            except Exception:
                break
        return False

    def is_game_running(self) -> bool:
        """Kiểm tra xem client Path of Exile 2 có đang chạy thật trên OS hay không."""
        return is_poe2_running()

    def get_game_pids(self) -> List[int]:
        """Lấy danh sách PID thật của client POE2."""
        pids: List[int] = []
        for name in POE2_PROCESS_NAMES:
            pids.extend(find_process_by_name(name))
        return list(set(pids))

    def is_runtime_running(self) -> bool:
        """Kiểm tra xem tiến trình Shadow Runtime có đang chạy không."""
        if self.runtime_proc is None:
            return False
        return self.runtime_proc.poll() is None

    def start_runtime(self, extra_args: Optional[List[str]] = None) -> bool:
        """Khởi chạy Shadow Binary AutoPOE2_Core_Runtime.exe độc lập."""
        with self._lock:
            if self.is_runtime_running():
                return True

            if not self.prepare_shadow_binary():
                return False

            cmd = [str(self.runtime_bin)]
            if extra_args:
                cmd.extend(extra_args)

            try:
                creation_flags = 0
                if sys.platform == "win32":
                    creation_flags = subprocess.CREATE_NEW_PROCESS_GROUP

                self.runtime_proc = subprocess.Popen(
                    cmd,
                    cwd=str(self.runtime_bin.parent),
                    creationflags=creation_flags,
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL,
                )
                return True
            except Exception:
                self.runtime_proc = None
                return False

    def stop_runtime(self, timeout_sec: float = 3.0) -> bool:
        """Dừng tiến trình Shadow Runtime một cách êm ái."""
        with self._lock:
            if not self.is_runtime_running():
                self.runtime_proc = None
                return True

            proc = self.runtime_proc
            if proc is None:
                return True

            try:
                proc.terminate()
                proc.wait(timeout=timeout_sec)
            except subprocess.TimeoutExpired:
                try:
                    proc.kill()
                    proc.wait(timeout=1.0)
                except Exception:
                    pass
            except Exception:
                pass
            finally:
                self.runtime_proc = None

            return True

    def hot_swap_runtime(self) -> bool:
        """
        Thực hiện hoán đổi binary nóng:
        1. Kiểm tra xem có bản build mới của AutoPOE2_Core.exe không.
        2. Dừng runtime hiện tại.
        3. Copy shadow binary mới.
        4. Khởi động lại runtime.
        """
        if not self.source_bin.exists():
            return False

        new_hash = calculate_sha256(self.source_bin)
        cur_hash = calculate_sha256(self.runtime_bin) if self.runtime_bin.exists() else ""

        if new_hash == cur_hash and self.is_runtime_running():
            return True  # Không có thay đổi gì mới

        self.stop_runtime()
        if not self.prepare_shadow_binary(force=True):
            return False
        return self.start_runtime()

    def get_status(self) -> Dict[str, Any]:
        """Lấy toàn bộ trạng thái thực tế của hệ thống (Ground Truth)."""
        game_pids = self.get_game_pids()
        game_active = len(game_pids) > 0
        runtime_active = self.is_runtime_running()
        runtime_pid = self.runtime_proc.pid if runtime_active and self.runtime_proc else None

        shm_status = self.shm_bridge.get_status_dict()
        vm = get_virtual_monitor()
        game_hwnd = find_poe2_window() if game_active else 0
        game_on_vd = is_window_on_virtual_display(game_hwnd, vm) if (vm and game_hwnd) else False

        return {
            "supervisor": {
                "active": self._running,
                "paused": self._paused,
                "http_port": self.http_port,
                "timestamp_iso": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
            },
            "game_client": {
                "running": game_active,
                "pids": game_pids,
                "hwnd": game_hwnd,
                "status_str": f"ONLINE (PID {game_pids[0]})" if game_active else "OFFLINE",
            },
            "virtual_display": {
                "active": vm is not None,
                "monitor": f"{vm.width}x{vm.height} at ({vm.left}, {vm.top})" if vm else None,
                "rect": [vm.left, vm.top, vm.width, vm.height] if vm else None,
                "game_on_virtual_display": game_on_vd,
            },
            "shadow_runtime": {
                "running": runtime_active,
                "pid": runtime_pid,
                "source_binary": str(self.source_bin),
                "source_exists": self.source_bin.exists(),
                "runtime_binary": str(self.runtime_bin),
                "runtime_exists": self.runtime_bin.exists(),
                "binary_synced": calculate_sha256(self.source_bin) == calculate_sha256(self.runtime_bin) if self.source_bin.exists() and self.runtime_bin.exists() else False,
            },
            "telemetry": shm_status,
        }

    def get_pip_html(self) -> str:
        """Tạo giao diện HTML Picture-in-Picture Mini Streamer cho Headless Mode."""
        return """<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="utf-8">
  <title>AutoPOE2 - Live Background Headless Stream</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    body { margin: 0; background: #0f1117; color: #e2e8f0; font-family: ui-sans-serif, system-ui, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; }
    .header { display: flex; align-items: center; justify-content: space-between; width: 90vw; max-width: 1280px; margin-bottom: 8px; }
    .title-box { display: flex; align-items: center; gap: 10px; }
    .status-dot { width: 10px; height: 10px; border-radius: 50%; background: #22c55e; box-shadow: 0 0 10px #22c55e; }
    .container { position: relative; width: 90vw; max-width: 1280px; aspect-ratio: 16/9; background: #000; border-radius: 8px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.8); border: 1px solid #334155; }
    #stream { width: 100%; height: 100%; object-fit: contain; display: block; }
    .hud { position: absolute; bottom: 12px; left: 16px; background: rgba(15, 23, 42, 0.85); backdrop-filter: blur(4px); padding: 6px 14px; border-radius: 6px; font-size: 13px; border: 1px solid #475569; }
    .badge { padding: 2px 6px; border-radius: 4px; font-weight: bold; }
    .online { background: #166534; color: #86efac; }
    .offline { background: #991b1b; color: #fca5a5; }
  </style>
</head>
<body>
  <div class="header">
    <div class="title-box">
      <div class="status-dot" id="dot"></div>
      <h3 style="margin:0;">AutoPOE2 Virtual Screen (Headless Background Stream)</h3>
    </div>
    <div id="stats" style="font-size: 12px; color: #94a3b8;">Stream: Active</div>
  </div>
  <div class="container">
    <img id="stream" src="/preview.jpg" alt="Background Game Stream">
    <div class="hud" id="hud">Mode: Headless IPC | Status: Connecting...</div>
  </div>
  <script>
    const img = document.getElementById('stream');
    const hud = document.getElementById('hud');
    const stats = document.getElementById('stats');
    let frames = 0, lastTime = performance.now();
    function refreshFrame() {
      const next = new Image();
      next.onload = () => {
        img.src = next.src;
        frames++;
        const now = performance.now();
        if (now - lastTime >= 1000) {
          stats.innerText = `Stream: ${frames} FPS`;
          frames = 0;
          lastTime = now;
        }
        setTimeout(refreshFrame, 66);
      };
      next.onerror = () => setTimeout(refreshFrame, 1000);
      next.src = '/preview.jpg?t=' + Date.now();
    }
    refreshFrame();
    setInterval(async () => {
      try {
        const res = await fetch('/status');
        const s = await res.json();
        const g = s.game_client;
        hud.innerHTML = `Game: <span class="badge ${g.running ? 'online' : 'offline'}">${g.status_str}</span> | Core: ${s.shadow_runtime.running ? 'PID ' + s.shadow_runtime.pid : 'OFFLINE'} | Stream: HEADLESS IPC`;
      } catch(e) {}
    }, 2000);
  </script>
</body>
</html>"""

    def _watchdog_loop(self):
        """Vòng lặp giám sát sinh tử nền."""
        while self._running:
            try:
                game_running = self.is_game_running()
                if game_running:
                    if self._game_detected_time is None:
                        self._game_detected_time = time.time()
                else:
                    self._game_detected_time = None

                # 1. Nếu game chưa chạy và cấu hình auto_launch_game = True
                if self.auto_launch_game and not game_running:
                    Poe2ClientLauncher.launch_client()

                # 2. Nếu game đang chạy và dùng virtual display, tự động căn chỉnh vào virtual display
                # BẢO VỆ D3D12 SWAPCHAIN: Chỉ dời cửa sổ sau khi game đã chạy ổn định ít nhất 20s (tránh dời trong lúc Streamline/D3D12 init)
                if self.use_virtual_display and game_running:
                    uptime = time.time() - self._game_detected_time if self._game_detected_time else 0.0
                    if uptime >= 20.0:
                        game_hwnd = find_poe2_window()
                        if game_hwnd:
                            user32 = ctypes.windll.user32 if sys.platform == "win32" else None
                            is_hung = user32.IsHungAppWindow(game_hwnd) if user32 else False
                            if not is_hung:
                                vm = get_virtual_monitor()
                                if vm and not is_window_on_virtual_display(game_hwnd, vm):
                                    print(f"[Supervisor Watchdog] Game ổn định {uptime:.1f}s -> Dời cửa sổ HWND {game_hwnd} sang {vm.device}...", flush=True)
                                    ok = move_window_to_virtual_display(game_hwnd, vm, borderless=True, keep_size=False)
                                    print(f"[Supervisor Watchdog] Kết quả dời cửa sổ: {ok}", flush=True)

                # 3. Nếu game đang chạy nhưng runtime bị tắt bất ngờ và không pause
                if game_running and not self._paused and not self.is_runtime_running():
                    uptime = time.time() - self._game_detected_time if self._game_detected_time else 0.0
                    if uptime >= 22.0:
                        print(f"[Supervisor Watchdog] Game ổn định {uptime:.1f}s -> Khởi chạy AutoPOE2_Core_Runtime.exe...", flush=True)
                        self.start_runtime(extra_args=["--user-mode"])

                # 4. Kiểm tra tự động hoán đổi binary nếu source_bin vừa được rebuild mới
                if self.source_bin.exists():
                    current_source_hash = calculate_sha256(self.source_bin)
                    if current_source_hash and current_source_hash != self._last_source_hash:
                        # Có bản build mới, thực hiện hot swap
                        print("[Supervisor Watchdog] Phát hiện binary mới -> Thực hiện Hot Swap Runtime...", flush=True)
                        self.hot_swap_runtime()

            except Exception as e:
                print(f"[Supervisor Watchdog Error] {e}", flush=True)

            time.sleep(2.0)

    def start(self):
        """Bật Supervisor và HTTP server."""
        if self._running:
            return

        self._running = True
        self.prepare_shadow_binary()

        # Khởi động watchdog
        self._watchdog_thread = threading.Thread(target=self._watchdog_loop, daemon=True, name="Poe2SupervisorWatchdog")
        self._watchdog_thread.start()

        # Khởi động HTTP Control Plane
        self._start_http_server()

    def stop(self):
        """Tắt Supervisor và giải phóng tài nguyên."""
        self._running = False
        self.stop_runtime()

        if self._http_server:
            try:
                self._http_server.shutdown()
                self._http_server.server_close()
            except Exception:
                pass
            self._http_server = None

    def _start_http_server(self):
        supervisor_ref = self

        class SupervisorHandler(BaseHTTPRequestHandler):
            def log_message(self, format, *args):
                pass  # Tắt log stdout rác

            def _send_json(self, status_code: int, data: Dict[str, Any]):
                body = json.dumps(data, indent=2).encode("utf-8")
                self.send_response(status_code)
                self.send_header("Content-Type", "application/json; charset=utf-8")
                self.send_header("Content-Length", str(len(body)))
                self.send_header("Access-Control-Allow-Origin", "*")
                self.end_headers()
                self.wfile.write(body)

            def do_GET(self):
                if self.path in ("/status", "/"):
                    status = supervisor_ref.get_status()
                    self._send_json(200, status)
                elif self.path in ("/preview", "/pip"):
                    html = supervisor_ref.get_pip_html()
                    body = html.encode("utf-8")
                    self.send_response(200)
                    self.send_header("Content-Type", "text/html; charset=utf-8")
                    self.send_header("Content-Length", str(len(body)))
                    self.end_headers()
                    self.wfile.write(body)
                elif self.path.startswith("/preview.jpg"):
                    jpeg_data = None
                    # 1. Chụp trực tiếp background game window qua DWM (Headless Mode)
                    game_hwnd = find_poe2_window()
                    if game_hwnd:
                        try:
                            from src.common.screen_capture import capture_window_bitblt
                            import io
                            img = capture_window_bitblt(game_hwnd, client_only=True)
                            if img:
                                buf = io.BytesIO()
                                img.save(buf, format="JPEG", quality=70)
                                jpeg_data = buf.getvalue()
                        except Exception:
                            jpeg_data = None

                    # 2. Fallback nếu virtual display được bật
                    if not jpeg_data and supervisor_ref.use_virtual_display:
                        try:
                            jpeg_data = capture_virtual_display_jpeg(quality=70)
                        except Exception:
                            jpeg_data = None

                    # 3. Fallback sang frame standby nếu game offline
                    if not jpeg_data:
                        jpeg_data = get_standby_jpeg_frame()

                    if jpeg_data:
                        self.send_response(200)
                        self.send_header("Content-Type", "image/jpeg")
                        self.send_header("Content-Length", str(len(jpeg_data)))
                        self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
                        self.send_header("Access-Control-Allow-Origin", "*")
                        self.end_headers()
                        self.wfile.write(jpeg_data)
                    else:
                        self._send_json(503, {"error": "Game screen capture unavailable"})
                else:
                    self._send_json(404, {"error": "Not Found"})

            def do_POST(self):
                if self.path == "/pause":
                    supervisor_ref._paused = True
                    supervisor_ref.stop_runtime()
                    self._send_json(200, {"success": True, "paused": True})
                elif self.path == "/resume":
                    supervisor_ref._paused = False
                    supervisor_ref.start_runtime()
                    self._send_json(200, {"success": True, "paused": False})
                elif self.path == "/swap":
                    success = supervisor_ref.hot_swap_runtime()
                    self._send_json(200, {"success": success, "swapped": True})
                elif self.path == "/stop":
                    self._send_json(200, {"success": True, "stopping": True})
                    threading.Thread(target=supervisor_ref.stop).start()
                else:
                    self._send_json(404, {"error": "Endpoint not found"})

        try:
            self._http_server = HTTPServer(("127.0.0.1", self.http_port), SupervisorHandler)
            self._http_thread = threading.Thread(target=self._http_server.serve_forever, daemon=True, name="Poe2SupervisorHttp")
            self._http_thread.start()
        except Exception as e:
            print(f"[Supervisor] Không thể mở HTTP server tại cổng {self.http_port}: {e}", file=sys.stderr)


# ==============================================================================
# CLI Entrypoint
# ==============================================================================
def main():
    parser = argparse.ArgumentParser(description="AutoPOE2 Process Supervisor & Shadow Binary Controller")
    parser.add_argument("--probe", action="store_true", help="Kiểm tra trạng thái hiện trường (Single-shot) và in ra JSON")
    parser.add_argument("--start", action="store_true", help="Khởi chạy Supervisor Daemon")
    parser.add_argument("--stop", action="store_true", help="Gửi lệnh tắt Supervisor đang chạy")
    parser.add_argument("--swap", action="store_true", help="Gửi lệnh hoán đổi Shadow Binary mới")
    parser.add_argument("--port", type=int, default=DEFAULT_HTTP_PORT, help=f"Cổng HTTP Control Plane (mặc định: {DEFAULT_HTTP_PORT})")
    parser.add_argument("--auto-launch", action="store_true", help="Tự động mở PathOfExile.exe nếu game chưa chạy")
    parser.add_argument("--virtual-display", action="store_true", default=False, help="Kích hoạt tính năng cách ly sang màn hình ảo phụ (mặc định: False - dùng Headless Mode)")
    parser.add_argument("--move-game-to-vd", action="store_true", help="Dời ngay cửa sổ game Path of Exile 2 sang màn hình ảo")
    parser.add_argument("--optimize-graphics", type=str, choices=list(PROFILES.keys()) + ["none"], default="none", help="Tối ưu đồ họa game trước khi vận hành (ultra_low, low_1080p, balanced_1080p)")
    args = parser.parse_args()

    if args.optimize_graphics != "none":
        try:
            print(f"[Supervisor] Đang tối ưu cấu hình đồ họa với profile '{args.optimize_graphics}'...")
            changes = Poe2GraphicsOptimizer.apply_profile(args.optimize_graphics)
            print(f"[Supervisor] Tối ưu đồ họa thành công ({len(changes)} thông số đã cập nhật).")
        except Exception as e:
            print(f"[Supervisor Cảnh báo] Không thể tối ưu đồ họa: {e}", file=sys.stderr)

    use_vd = args.virtual_display
    supervisor = Poe2Supervisor(
        http_port=args.port,
        auto_launch_game=args.auto_launch,
        use_virtual_display=use_vd,
    )

    if args.move_game_to_vd:
        game_hwnd = find_poe2_window()
        if not game_hwnd:
            print("[Supervisor Error] Không tìm thấy cửa sổ game Path of Exile 2 đang chạy.", file=sys.stderr)
            sys.exit(1)
        vm = get_virtual_monitor()
        if not vm:
            print("[Supervisor Error] Không tìm thấy màn hình ảo phụ (Monitor 2).", file=sys.stderr)
            sys.exit(1)
        ok = move_window_to_virtual_display(game_hwnd, vm, borderless=False, keep_size=True)
        print(f"[Supervisor] Dời cửa sổ game HWND {game_hwnd} sang {vm.device} ({vm.width}x{vm.height} tại {vm.left},{vm.top}): {'THÀNH CÔNG' if ok else 'THẤT BÀI'}")
        sys.exit(0 if ok else 1)

    if args.probe:
        status = supervisor.get_status()
        print(json.dumps(status, indent=2))
        sys.exit(0)

    if args.swap:
        import urllib.request
        try:
            req = urllib.request.Request(f"http://127.0.0.1:{args.port}/swap", data=b"{}", headers={"Content-Type": "application/json"})
            with urllib.request.urlopen(req, timeout=3) as resp:
                print(resp.read().decode("utf-8"))
        except Exception as e:
            print(f"Lỗi gửi lệnh hoán đổi: {e}", file=sys.stderr)
            sys.exit(1)
        sys.exit(0)

    if args.stop:
        import urllib.request
        try:
            req = urllib.request.Request(f"http://127.0.0.1:{args.port}/stop", data=b"{}", headers={"Content-Type": "application/json"})
            with urllib.request.urlopen(req, timeout=3) as resp:
                print(resp.read().decode("utf-8"))
        except Exception as e:
            print(f"Lỗi gửi lệnh dừng: {e}", file=sys.stderr)
            sys.exit(1)
        sys.exit(0)

    if args.start:
        print(f"[Supervisor] Khởi động AutoPOE2 Supervisor tại http://127.0.0.1:{args.port}...")
        supervisor.start()
        try:
            while True:
                time.sleep(1.0)
        except KeyboardInterrupt:
            print("\n[Supervisor] Đang dừng tiến trình...")
            supervisor.stop()
            print("[Supervisor] Đã dừng an toàn.")
        sys.exit(0)

    # Mặc định in probe nếu không truyền cờ
    status = supervisor.get_status()
    print(json.dumps(status, indent=2))


if __name__ == "__main__":
    main()
