"""S-CANVAS InlinePanel — `ctk.CTkToplevel` 호환 인라인 오버레이 (#4).

피드백 #4: "프로세스를 클릭할 때마다 새로운 창이 뜨는 것이 아니라 한 화면에서
바로 구동되게끔 적용".

본 모듈은 `ctk.CTkToplevel(parent)` 자리에 그대로 들어가는 drop-in 대체. main 앱
창의 `main_frame` 안에 floating overlay 로 렌더되어 별도 OS 창을 만들지 않는다.

호환 API (Toplevel subset):
    - title(str)
    - geometry("WxH" | "WxH+X+Y")  — X,Y 는 무시 (centering)
    - transient(parent)              — no-op
    - grab_set() / grab_release()    — lift + focus 시뮬레이트
    - protocol("WM_DELETE_WINDOW", fn)
    - wait_window()                  — tk.Misc.wait_window 그대로 동작 (Frame 도 OK)
    - destroy()
    - iconbitmap / iconphoto / wm_*  — no-op (Toplevel 전용)

사용:
    from harness.inline_panel import InlinePanel
    win = InlinePanel(self)            # ctk.CTkToplevel(self) 와 동일하게
    win.title("DXF 레이어 분류")
    win.geometry("900x650")
    win.grab_set()
    # ... 자식 위젯 .pack() / .grid()
    btn_close = ctk.CTkButton(win, text="닫기", command=win.destroy)
    btn_close.pack()
    win.wait_window()                  # 패널 destroy 까지 블록

기존 Toplevel 와 거의 동일한 코드 변경 = 1줄 (`CTkToplevel` → `InlinePanel`).
"""
from __future__ import annotations

import contextlib
from collections.abc import Callable

import customtkinter as ctk


# Mastercard 팔레트 (scanvas_maker.py 와 일관) — 타이틀 바 색상.
_MC_RED = "#EB001B"
_MC_RED_DARK = "#A30013"
_MC_WHITE = "#FFFFFF"


class InlinePanel(ctk.CTkFrame):
    """`ctk.CTkToplevel` 인터페이스를 구현한 inline overlay frame.

    상위 SCanvasApp 인스턴스의 `main_frame` 안에 `place(relx=0.5, rely=0.5)` 로
    중앙 배치. 같은 시점에 여러 패널이 열리면 가장 마지막 것이 위 (lift).

    구현 노트:
        - `tk.Misc.wait_window(self)` 는 widget destruction 을 기다리는 메커니즘
          이라 Toplevel 외 Frame 에서도 동작. CTkFrame → tk.Frame → tk.Widget.
        - `grab_set()` 은 OS-level focus 잡는 게 본래 의미인데, 인라인 오버레이는
          하나의 창 안이라 무의미 → lift + focus 로 시뮬레이트.
        - geometry 의 X+Y 좌표는 무시 (panel 은 항상 main_frame 중앙).
    """

    _z_counter = 0  # 다중 패널 z-order 카운터

    def __init__(self, parent: ctk.CTkBaseClass, **kwargs):
        # 상위 SCanvasApp 찾기 — winfo_toplevel() 이 root window 반환.
        # parent 가 SCanvasApp 또는 다른 InlinePanel 둘 다 OK.
        # 안전 폴백: main_frame 없는 root 면 parent 자체에 그림.
        app = parent.winfo_toplevel()
        host = app.main_frame if hasattr(app, "main_frame") else parent

        super().__init__(
            host,
            fg_color=("#FFFFFF", "#1A1A1A"),
            border_width=2,
            border_color=("#E0E0E0", "#333333"),
            corner_radius=10,
            **kwargs,
        )

        self._app = app
        self._host = host
        self._title_text: str = ""
        self._title_bar: ctk.CTkFrame | None = None
        self._title_label: ctk.CTkLabel | None = None
        self._content_frame: ctk.CTkFrame | None = None
        self._on_close: Callable[[], None] | None = None
        self._closed = False
        self._geom_w = 640
        self._geom_h = 480

        InlinePanel._z_counter += 1
        self._z = InlinePanel._z_counter

        self._build_chrome()
        self._reposition()

    # --- chrome (title bar + content area) -----------------------------------

    def _build_chrome(self) -> None:
        """타이틀 바 (MC red) + 컨텐츠 프레임. 자식 위젯은 컨텐츠 프레임에 들어감."""
        # title_bar: MC red 배경, 흰 텍스트, ✕ 버튼
        self._title_bar = ctk.CTkFrame(
            self, height=34, fg_color=(_MC_RED, _MC_RED_DARK), corner_radius=8,
        )
        self._title_bar.pack(side="top", fill="x", padx=4, pady=(4, 0))
        self._title_bar.pack_propagate(False)

        self._title_label = ctk.CTkLabel(
            self._title_bar, text="", text_color=_MC_WHITE,
            font=ctk.CTkFont(size=13, weight="bold"),
        )
        self._title_label.pack(side="left", padx=12, pady=4)

        close_btn = ctk.CTkButton(
            self._title_bar, text="✕", width=28, height=24,
            fg_color="transparent", hover_color=_MC_RED_DARK,
            text_color=_MC_WHITE, corner_radius=4,
            font=ctk.CTkFont(size=14, weight="bold"),
            command=self._user_close,
        )
        close_btn.pack(side="right", padx=4, pady=4)

        # content frame: 자식 위젯 컨테이너. 사용자가 .pack/.grid(self)로 추가하면
        # 자동으로 여기 자식이 됨 (CTkFrame 의 _children 위임). 직접 호출은 따로.
        # 다만 기존 코드는 `widget(win, ...)` 처럼 win 자체를 parent 로 쓰니 그게
        # InlinePanel 의 직접 자식이 됨 — 그래서 content_frame 은 reserved 안 만들고
        # title_bar 만 packed 후 자식들이 그 아래로 채워짐 (pack 자동 layout).
        # title bar 와 content 사이 분리선
        sep = ctk.CTkFrame(self, height=1, fg_color=("#E0E0E0", "#333333"))
        sep.pack(side="top", fill="x", padx=8, pady=(0, 4))

    def _reposition(self) -> None:
        """geometry str 파싱 + 중앙 배치 (main_frame 의 중앙)."""
        try:
            mw = self._host.winfo_width() or 1200
            mh = self._host.winfo_height() or 800
            # main_frame 의 95% 안으로 cap
            w = min(self._geom_w, int(mw * 0.95))
            h = min(self._geom_h, int(mh * 0.95))
            self.place(relx=0.5, rely=0.5, anchor="center", width=w, height=h)
            self.lift()
        except Exception:
            with contextlib.suppress(Exception):
                self.place(relx=0.5, rely=0.5, anchor="center")
                self.lift()

    # --- ctk.CTkToplevel 호환 메서드 -----------------------------------------

    def title(self, t: str) -> None:
        self._title_text = t
        if self._title_label is not None:
            self._title_label.configure(text=t)

    def geometry(self, geom_str: str) -> None:
        """\"WxH\" 또는 \"WxH+X+Y\" 형식 파싱. X,Y 는 무시 (always center)."""
        try:
            size_part = geom_str.split("+", 1)[0]
            w_str, h_str = size_part.split("x")
            self._geom_w = int(w_str)
            self._geom_h = int(h_str)
            self._reposition()
        except (ValueError, AttributeError):
            pass

    def transient(self, master) -> None:
        """no-op (이미 main_frame 안)."""

    def grab_set(self) -> None:
        """모달 시뮬레이트 — lift + focus."""
        with contextlib.suppress(Exception):
            self.lift()
            self.focus_set()

    def grab_release(self) -> None:
        """no-op."""

    def protocol(self, name: str, handler: Callable[[], None]) -> None:
        if name == "WM_DELETE_WINDOW":
            self._on_close = handler

    def iconbitmap(self, *args, **kwargs) -> None:
        """Toplevel 전용 — no-op (인라인 패널은 OS 창이 아님)."""

    def iconphoto(self, *args, **kwargs) -> None:
        """Toplevel 전용 — no-op."""

    def wm_iconbitmap(self, *args, **kwargs) -> None:
        """Toplevel 전용 — no-op."""

    def wm_iconphoto(self, *args, **kwargs) -> None:
        """Toplevel 전용 — no-op."""

    def attributes(self, *args, **kwargs):
        """\"-topmost\", \"-alpha\" 등 — no-op (인라인 패널은 z-order 관리만)."""
        return None

    def overrideredirect(self, *args, **kwargs) -> None:
        """no-op."""

    # --- 종료 처리 -----------------------------------------------------------

    def _user_close(self) -> None:
        """타이틀 바 ✕ 또는 protocol(WM_DELETE_WINDOW) 호출 시 진입점."""
        if self._on_close is not None:
            try:
                self._on_close()
            except Exception:
                self.destroy()
        else:
            self.destroy()

    def destroy(self) -> None:
        if self._closed:
            return
        self._closed = True
        with contextlib.suppress(Exception):
            self.place_forget()
        super().destroy()

    # wait_window() 는 별도 정의 안 함 — tk.Misc.wait_window(self) 가 widget
    # destruction 을 기다리는데, CTkFrame 이 tk.Widget 상속이라 Frame 에서도 동작.
