# -*- coding: utf-8 -*-
# gui/containers/_parent_resize_emitter.py
"""Эмиттер resize-событий родительского виджета (один экземпляр на parent)."""

from __future__ import annotations

from PySide6.QtCore import QObject, QEvent, QSize, QTimer, Signal
from PySide6.QtWidgets import QWidget
from shiboken6 import isValid


class ParentResizeEmitter(QObject):
    """
    Перехватывает Resize-события родителя через eventFilter.
    Один экземпляр на каждого родителя, разделяется всеми потомками.

    Эмитирует две фазы:
      1) parent_resized           — сразу после coalesced singleShot(0);
      2) parent_rebuild_finished  — отдельным singleShot(0) после фазы 1.
    Между фазами Qt успевает обработать layout.activate() подписчиков фазы 1,
    поэтому подписчики фазы 2 видят финальную, стабильную геометрию родителя.
    """
    parent_resized = Signal()
    parent_rebuild_finished = Signal()

    def __init__(self, parent_widget: QWidget):
        super().__init__(parent_widget)
        self._parent_widget: QWidget | None = parent_widget
        self._last_size = parent_widget.size()
        self._emit_pending = False
        self._finish_pending = False
        # destroyed.connect(self._on_parent_destroyed) намеренно не используем:
        # порядок разрушения Python-обёрток эмиттера и parent_widget не определён,
        # и Qt может вызвать слот на полу-разрушенном объекте, выдавая
        # "Slot 'ParentResizeEmitter::' not found". Все queued-обработчики
        # (_emit_parent_resized, _emit_rebuild_finished) защищены проверкой
        # _is_valid_qobject(_parent_widget), поэтому ссылка на мёртвый
        # parent безопасна — событий просто не будет.
        parent_widget.installEventFilter(self)

    def eventFilter(self, watched: QObject, event: QEvent) -> bool:
        # Защита от полу-разрушенного состояния: Qt может вызвать eventFilter
        # на эмиттере, чей Python-объект уже частично очищен (атрибуты удалены),
        # но C++ instance ещё жив и обрабатывает события из очереди.
        parent_widget = getattr(self, "_parent_widget", None)
        if not self._is_valid_qobject(parent_widget):
            return False
        if watched is not parent_widget:
            return False
        if not self._is_valid_qobject(watched):
            return False
        try:
            current_size = watched.size()
        except RuntimeError:
            # Underlying C++ QWidget was deleted; shiboken raises RuntimeError on method calls.
            return False
        last_size = getattr(self, "_last_size", None)
        if current_size != last_size:
            self._last_size = current_size
            self._schedule_parent_resized()
        return False

    def _on_parent_destroyed(self) -> None:
        self._parent_widget = None
        self._emit_pending = False
        self._finish_pending = False
        self._last_size = QSize()

    def _schedule_parent_resized(self) -> None:
        if self._emit_pending:
            return
        self._emit_pending = True
        QTimer.singleShot(0, self._emit_parent_resized)

    def _emit_parent_resized(self) -> None:
        self._emit_pending = False
        if not self._is_valid_qobject(self._parent_widget):
            return
        self.parent_resized.emit()
        self._schedule_rebuild_finished()

    def _schedule_rebuild_finished(self) -> None:
        if self._finish_pending:
            return
        self._finish_pending = True
        QTimer.singleShot(0, self._emit_rebuild_finished)

    def _emit_rebuild_finished(self) -> None:
        self._finish_pending = False
        if not self._is_valid_qobject(self._parent_widget):
            return
        self.parent_rebuild_finished.emit()

    @staticmethod
    def _is_valid_qobject(obj: object) -> bool:
        return obj is not None and isValid(obj)
