from PySide6.QtWidgets import QLabel, QMenu, QVBoxLayout, QWidget
from PySide6.QtCore import Qt,Signal, QPoint
from PySide6.QtGui import QMouseEvent, QEnterEvent, QDragLeaveEvent, QCursor
from core.logger import get_logger

logger = get_logger(__name__)

class ClickableLabel(QLabel):
    """
    클릭 / 더블클릭 / 우클릭 컨텍스트 메뉴 / 마우스 호버링 / 드래그 / 그리드 스냅 지원 QLabel

    [Signals]
    - clicked(): 좌클릭
    - double_clicked(): 좌측 더블클릭
    - right_clicked(pos: QPoint): 우클릭 (로컬 좌표)
    - hover_entered(): 마우스 진입
    - hover_left(): 마우스 이탈
    - drag_started(pos: QPoint): 드래그 시작
    - dragging(delta: QPoint): 드래그 중 (이동량)
    - drag_finished(): 드래그 종료

    /사용예시
    label = ClickableLabel("클릭해보세요")
    label.clicked.connect(lambda: logger.info("원클릭"))
    label.double_clicked.connect(lambda: logger.info("더블클릭"))
    label.hover_entered.connect(lambda: logger.info("마우스 진입"))
    label.hover_left.connect(lambda: logger.info("마우스 이탈"))

    /우클릭 컨텍스트
    label.right_clicked.connect(
        lambda pos: logger.info(f"우클릭 위치: {pos}")
    )

    /드래그
    label.drag_started.connect(
        lambda pos: logger.info(f"드래그 시작 위치: {pos}")
    )
    label.dragging.connect(
        lambda delta: logger.info(f"드래그 이동량: {delta}")
    )
    label.drag_finished.connect(lambda: logger.info("드래그 종료"))

    def on_drag(delta: QPoint):
        label.move(label.pos() + delta)
    label.dragging.connect(on_drag)

    /드래그 시작 시 UI 반응 정의 : 배경색 변경
    label.drag_started.connect(lambda _: label.setStyleSheet("background:#d0ebff"))
    label.drag_finished.connect(lambda: label.setStyleSheet(""))

    호버링 (호버링 사용시 setMouseTracking(True) 필요)
    def on_hover_enter():
        label.setStyleSheet("background-color: #f0f0f0;")

    def on_hover_leave():
        label.setStyleSheet("background-color: none;")

    label.hover_entered.connect(on_hover_enter)
    label.hover_left.connect(on_hover_leave)


    """

    clicked = Signal()                     # 좌클릭
    double_clicked = Signal()              # 더블클릭
    right_clicked = Signal(QPoint)         # 우클릭

    hover_entered = Signal()               # 마우스 진입
    hover_left = Signal()                  # 마우스 이탈

    drag_started = Signal(QPoint)          # 드래그 시작 위치
    dragging = Signal(QPoint)              # 드래그 이동량
    drag_finished = Signal()               # 드래그 종료

    def __init__(
        self,
        text: str = "",
        parent=None,
        hover_text=None,
        *,
        enable_click: bool = True,
        enable_double_click: bool = True,
        enable_right_click: bool = False,
        enable_hover: bool = True,
        enable_drag: bool = False,
        enable_context_menu: bool = False,
        context_menu_builder=None,
        enable_grid_snap: bool = False,
        grid_size: int = 20,
        **kwargs
        ):

        super().__init__(text, parent)


        # -----------------------------
        # 기능 활성화 플래그
        # -----------------------------
        self.enable_click = enable_click
        self.enable_double_click = enable_double_click
        self.enable_right_click = enable_right_click
        self.enable_hover = enable_hover
        self.enable_drag = enable_drag
        self.enable_context_menu = enable_context_menu
        self._context_menu_builder = context_menu_builder
        self.enable_grid_snap = enable_grid_snap

        # -----------------------------
        # 드래그 / 그리드 설정
        # -----------------------------
        self.grid_size = grid_size
        self._drag_threshold = 5  # px (클릭 vs 드래그 구분)

        self._press_pos: QPoint | None = None
        self._dragging = False

        # -----------------------------
        # UI 기본 설정
        # -----------------------------
        self.setCursor(Qt.PointingHandCursor)
        
        # 텍스트 중앙 정렬
        self.setAlignment(Qt.AlignCenter)

        self._hover_text = hover_text
        self._hover_text_provider: callable | None = None
        self._hover_popup = None

        # hover는 mouse tracking 필수
        if self.enable_hover:   
            self.setMouseTracking(True)
        else:
            self.setMouseTracking(False)

    # -------------------------------------------------
    # 마우스 누름
    # -------------------------------------------------
    def mousePressEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            if self.enable_click:
                self.clicked.emit()

            if self.enable_drag:
                self._press_pos = event.pos()
                self._dragging = False

        elif event.button() == Qt.RightButton:
            if self.enable_right_click:
                self.right_clicked.emit(event.pos())

            if self.enable_context_menu:
                self._show_context_menu(event.globalPos())

        super().mousePressEvent(event)

    # -------------------------------------------------
    # 마우스 이동 (드래그)
    # -------------------------------------------------
    def mouseMoveEvent(self, event: QMouseEvent):
        if not self.enable_drag or self._press_pos is None:
            super().mouseMoveEvent(event)
            return

        delta = event.pos() - self._press_pos

        if not self._dragging and delta.manhattanLength() >= self._drag_threshold:
            self._dragging = True
            self.drag_started.emit(self._press_pos)

        if self._dragging:
            self.dragging.emit(delta)
            self._move_with_grid_snap(delta)

        super().mouseMoveEvent(event)

    # -------------------------------------------------
    # 마우스 해제
    # -------------------------------------------------
    def mouseReleaseEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton and self.enable_drag:
            if self._dragging:
                self.drag_finished.emit()

            self._dragging = False
            self._press_pos = None

        super().mouseReleaseEvent(event)

    # -------------------------------------------------
    # 더블클릭
    # -------------------------------------------------
    def mouseDoubleClickEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton and self.enable_double_click:
            self.double_clicked.emit()
        super().mouseDoubleClickEvent(event)

    # -------------------------------------------------
    # 호버

    # 사용법 (주의:provider 함수에서 DB 조회, 네트워크 요청, 긴 계산 등을 포함하면 안됨,필요한 경우 캐시+갱신 이벤트구조로 구현)

    # label.set_hover_text_provider(
    #     lambda: f"""
    # 상태: {node.status}
    # 전압: {node.voltage}V
    # 최근 점검: {node.last_check}
    # """.strip()
    # )

    # -------------------------------------------------
    def enterEvent(self, event: QEnterEvent):
        if self.enable_hover:
            self.hover_entered.emit()
            text = self._get_hover_text()
            if text:
                self._hover_popup = HoverInfoPopup(text)
                pos = QCursor.pos() + QPoint(10, 10)  # 마우스 커서 위치에서 약간 오프셋
                self._hover_popup.move(pos)
                self._hover_popup.show()

        super().enterEvent(event)

    def leaveEvent(self, event: QDragLeaveEvent):

        if not self.enable_hover:
            super().leaveEvent(event)
            return
                
        # popup이 있고, 마우스가 popup 안에 있으면 닫지 않음
        if self._hover_popup and self._hover_popup.is_mouse_inside():
            return

        self.hover_left.emit()

        if self.enable_hover:
            if self._hover_popup:
                self._hover_popup.close()
                self._hover_popup = None
        super().leaveEvent(event)

    def set_hover_text(self, text: str):
        """
        hover 정보 텍스트 설정 (즉시 반영)
        """
        self._hover_text = text
        self._hover_text_provider = None

        if self._hover_popup:
            self._hover_popup.set_text(text)

    def _get_hover_text(self) -> str | None:
        if self._hover_text_provider:
            return self._hover_text_provider()
        return self._hover_text

    def clear_hover_text(self):
        """
        hover 정보 제거
        """
        self._hover_text = None
        self._hover_text_provider = None

        if self._hover_popup:
            self._hover_popup.close()
            self._hover_popup = None

    def set_hover_text_provider(self, provider: callable):
        """
        hover 시점에 호출되는 함수 등록
        """
        self._hover_text_provider = provider
        self._hover_text = None

        if self._hover_popup:
            self._hover_popup.set_text(provider())


    # =================================================
    # 내부 기능
    # =================================================
    def _show_context_menu(self, global_pos):
        if not self._context_menu_builder:
            return
        
        menu = QMenu(self)
        self._context_menu_builder(menu, self) # 컨텍스트 메뉴 빌더 호출
        menu.exec(global_pos)

        ''' 외부에서 컨텍스트 메뉴 빌더 정의
        def build_label_menu(menu: QMenu, label: ClickableLabel):
            menu.addAction("편집", lambda: edit_label(label))
            menu.addAction("삭제", lambda: delete_label(label))
            menu.addSeparator()
            menu.addAction("정보", lambda: show_info(label))

        def edit_label(label):
            logger.info(f"편집: {label.text()}")

        def delete_label(label):
            label.deleteLater()

        def show_info(label):
            logger.info(f"위치: {label.pos()}")

        객체 생성시 컨텍스트 메뉴 빌더 전달
        label = ClickableLabel(
            "NODE-1",
            context_menu_builder=build_label_menu
        )
        '''

    def _move_with_grid_snap(self, delta: QPoint):
        new_pos = self.pos() + delta

        if self.enable_grid_snap:
            x = round(new_pos.x() / self.grid_size) * self.grid_size
            y = round(new_pos.y() / self.grid_size) * self.grid_size
            self.move(QPoint(x, y))
        else:
            self.move(new_pos)



class PopupTheme:
    LIGHT = "light"
    DARK = "dark"


class HoverInfoPopup(QWidget):
    """
    마우스 호버 시 정보 팝업 표시 QWidget
    
    사용예시
    popup = HoverInfoPopup("호버 정보")
    popup.show()
    """
    def __init__(self, text: str, theme=PopupTheme.DARK, parent=None):
        super().__init__(parent)

        # -----------------------------
        # 테마 설정
        # -----------------------------
        self._theme = theme
        self.setWindowFlags(
            Qt.ToolTip |
            Qt.FramelessWindowHint |
            Qt.BypassWindowManagerHint
        )

        self.setAttribute(Qt.WA_ShowWithoutActivating)

        self.apply_theme(theme)

        # -----------------------------
        # 윈도우 설정
        # -----------------------------
        self.setAttribute(Qt.WA_TransparentForMouseEvents, False)

        # -----------------------------
        # 드래그 설정
        # -----------------------------
        self._drag_start_pos = None

        # -----------------------------
        # UI 설정
        # -----------------------------
        layout = QVBoxLayout(self)
        layout.setContentsMargins(8, 6, 8, 6)

        # -----------------------------
        # 라벨 설정
        # -----------------------------
        self.label = QLabel(text)
        self.label.setTextInteractionFlags(
            Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard
        )

        layout.addWidget(self.label)

        self.setStyleSheet("""
            QWidget {
                background-color: #ffffe1;
                border: 1px solid #c8c8a9;
                border-radius: 4px;
                font-size: 12px;
            }
        """)

    # -----------------------------
    # 드래그 이동
    # -----------------------------
    def mousePressEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            self._drag_start_pos = event.globalPos() - self.frameGeometry().topLeft()

    def mouseMoveEvent(self, event: QMouseEvent):
        if self._drag_start_pos:
            self.move(event.globalPos() - self._drag_start_pos)

    def mouseReleaseEvent(self, event: QMouseEvent):
        self._drag_start_pos = None

    # -------------------------------------------------
    # 텍스트 설정
    # -------------------------------------------------
    def set_text(self, text: str):
        self.label.setText(text)
        self.adjustSize()  # 내용 바뀌면 크기 자동 조정

    # -------------------------------------------------
    # 마우스 위치 확인
    # -------------------------------------------------
    def is_mouse_inside(self) -> bool:
        pos = QCursor.pos()
        return self.geometry().contains(pos)

    # -------------------------------------------------
    # 마우스 이탈 시 팝업 닫기
    # -------------------------------------------------
    def leaveEvent(self, event):
        self.close()
        super().leaveEvent(event)

    # -------------------------------------------------
    # 테마 적용
    # -------------------------------------------------
    def apply_theme(self, theme: str):
        if theme == PopupTheme.DARK:
            self.setStyleSheet("""
                QWidget {
                    background-color: #2b2b2b;
                    color: #eaeaea;
                    border: 1px solid #555;
                    border-radius: 6px;
                }
            """)
        else:
            self.setStyleSheet("""
                QWidget {
                    background-color: #ffffe1;
                    color: #202020;
                    border: 1px solid #c8c8a9;
                    border-radius: 6px;
                }
            """)