# -*- coding: utf-8 -*-
"""원격 입력 상태를 호스트 PC 화면 위에 작고 반투명하게 표시한다.

목표:
- 원격 제어가 실제 PC에 입력을 넣고 있음을 로컬 사용자에게 알려준다.
- 너무 크지 않게 2줄 정도로 표시한다.
- 드래그 이동 가능.
- X 버튼으로 현재 표시를 닫을 수 있다. 다음 원격 입력이 들어오면 다시 표시된다.
"""

from __future__ import annotations

import time
import tkinter as tk

try:
    import win32api
except Exception:  # pragma: no cover
    win32api = None


class RemoteActivityOverlay:
    def __init__(self, root: tk.Tk, *, hold_ms: int = 1300):
        self.root = root
        self.hold_ms = int(hold_ms)
        self.win: tk.Toplevel | None = None
        self.title_label: tk.Label | None = None
        self.msg_label: tk.Label | None = None
        self.close_label: tk.Label | None = None
        self.hide_after_id = None
        self.last_text = ""
        self.last_at = 0.0
        self.user_pos: tuple[int, int] | None = None
        self._drag_start: tuple[int, int, int, int] | None = None

    def show(self, text: str) -> None:
        # WebSocket thread에서 호출될 수 있으므로 Tk thread로 넘긴다.
        try:
            self.root.after(0, lambda: self._show_in_tk(text))
        except Exception:
            pass

    def _virtual_screen(self) -> tuple[int, int, int, int]:
        if win32api:
            try:
                # 76 SM_XVIRTUALSCREEN, 77 SM_YVIRTUALSCREEN, 78 CXVIRTUALSCREEN, 79 CYVIRTUALSCREEN
                left = int(win32api.GetSystemMetrics(76))
                top = int(win32api.GetSystemMetrics(77))
                width = int(win32api.GetSystemMetrics(78))
                height = int(win32api.GetSystemMetrics(79))
                return left, top, width, height
            except Exception:
                pass
        return 0, 0, int(self.root.winfo_screenwidth()), int(self.root.winfo_screenheight())

    def _build(self) -> None:
        self.win = tk.Toplevel(self.root)
        self.win.overrideredirect(True)
        self.win.attributes("-topmost", True)
        try:
            self.win.attributes("-alpha", 0.82)
        except Exception:
            pass
        self.win.configure(bg="#151515")

        outer = tk.Frame(self.win, bg="#151515", bd=1, relief="solid")
        outer.pack(fill=tk.BOTH, expand=True)

        top = tk.Frame(outer, bg="#151515")
        top.pack(fill=tk.X)
        self.title_label = tk.Label(
            top,
            text="원격 알림",
            bg="#151515",
            fg="#ffdd66",
            font=("Segoe UI", 9, "bold"),
            padx=8,
            pady=2,
            anchor="w",
        )
        self.title_label.pack(side=tk.LEFT, fill=tk.X, expand=True)
        self.close_label = tk.Label(
            top,
            text="×",
            bg="#151515",
            fg="#eeeeee",
            font=("Segoe UI", 10, "bold"),
            padx=7,
            pady=1,
            cursor="hand2",
        )
        self.close_label.pack(side=tk.RIGHT)
        self.close_label.bind("<Button-1>", lambda _e: self.hide())

        self.msg_label = tk.Label(
            outer,
            text="",
            bg="#151515",
            fg="#ffffff",
            font=("Segoe UI", 10),
            padx=10,
            pady=5,
            anchor="w",
            justify="left",
        )
        self.msg_label.pack(fill=tk.X)

        for widget in (self.win, outer, top, self.title_label, self.msg_label):
            widget.bind("<ButtonPress-1>", self._start_drag)
            widget.bind("<B1-Motion>", self._drag)

    def _default_pos(self, width: int, height: int) -> tuple[int, int]:
        vx, vy, vw, _vh = self._virtual_screen()
        return vx + vw - width - 18, vy + 18

    def _show_in_tk(self, text: str) -> None:
        self.last_text = str(text or "원격 입력")
        self.last_at = time.time()

        if self.win is None or not self.win.winfo_exists():
            self._build()

        if self.msg_label:
            # 최대 2줄 정도로 유지
            msg = self.last_text.strip()
            if len(msg) > 42:
                msg = msg[:42] + "…"
            self.msg_label.configure(text=msg)

        assert self.win is not None
        self.win.update_idletasks()
        width = max(210, min(360, self.win.winfo_reqwidth()))
        height = max(52, min(86, self.win.winfo_reqheight()))

        if self.user_pos:
            x, y = self.user_pos
        else:
            x, y = self._default_pos(width, height)

        self.win.geometry(f"{width}x{height}+{x}+{y}")
        self.win.deiconify()
        self.win.lift()

        if self.hide_after_id:
            try:
                self.root.after_cancel(self.hide_after_id)
            except Exception:
                pass
        self.hide_after_id = self.root.after(self.hold_ms, self.hide)

    def _start_drag(self, event) -> None:
        if self.win is None:
            return
        try:
            self._drag_start = (event.x_root, event.y_root, self.win.winfo_x(), self.win.winfo_y())
        except Exception:
            self._drag_start = None

    def _drag(self, event) -> None:
        if self.win is None or not self._drag_start:
            return
        sx, sy, wx, wy = self._drag_start
        nx = wx + (event.x_root - sx)
        ny = wy + (event.y_root - sy)
        self.user_pos = (int(nx), int(ny))
        try:
            self.win.geometry(f"+{int(nx)}+{int(ny)}")
        except Exception:
            pass

    def hide(self) -> None:
        try:
            if self.hide_after_id:
                self.root.after_cancel(self.hide_after_id)
                self.hide_after_id = None
        except Exception:
            pass
        try:
            if self.win is not None and self.win.winfo_exists():
                self.win.withdraw()
        except Exception:
            pass

    def destroy(self) -> None:
        try:
            if self.hide_after_id:
                self.root.after_cancel(self.hide_after_id)
        except Exception:
            pass
        try:
            if self.win is not None and self.win.winfo_exists():
                self.win.destroy()
        except Exception:
            pass
        self.win = None
        self.title_label = None
        self.msg_label = None
        self.close_label = None
