from PyQt6.QtWidgets import QWidget
from PyQt6.QtCore import QPoint, QTimer, Qt, QRect, QSize
from PyQt6.QtGui import QPainter, QColor, QPen

ITEM_W = 72
ITEM_H = 80
GAP = 4


RUBBER_MIN = 4  # 小于此尺寸的框选视为误触


class FlowContainer(QWidget):
    """手动绝对定位的流式容器，完全绕开 QLayout。"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self._widgets: list[QWidget] = []
        self._pending = False
        self._drag_highlight = False
        self.setMouseTracking(True)
        self._rubber_active = False
        self._rubber_origin = QPoint()
        self._rubber_rect: QRect | None = None

    def add_widget(self, w: QWidget):
        w.setParent(self)
        self._widgets.append(w)
        if self.width() > 0:
            self._relayout()
        else:
            self._schedule_relayout()

    def clear_widgets(self):
        for w in self._widgets:
            w.hide()
            w.setParent(None)
        self._widgets.clear()
        self.setFixedHeight(60)

    def get_widgets(self) -> list:
        return list(self._widgets)

    def set_drag_highlight(self, active: bool):
        if self._drag_highlight != active:
            self._drag_highlight = active
            self.update()

    def showEvent(self, event):
        super().showEvent(event)
        self._schedule_relayout()

    def resizeEvent(self, event):
        self._relayout()
        super().resizeEvent(event)

    def _schedule_relayout(self):
        if not self._pending:
            self._pending = True
            QTimer.singleShot(0, self._delayed_relayout)

    def _delayed_relayout(self):
        self._pending = False
        self._relayout()

    def _relayout(self):
        avail_w = self.width()
        if avail_w <= 0:
            p = self.parent()
            while p:
                if p.width() > 0:
                    avail_w = p.width() - 16
                    break
                p = p.parent()
        if avail_w <= 0:
            self._schedule_relayout()
            return

        x, y = GAP, GAP
        row_h = 0

        for w in self._widgets:
            if not w.isVisible():
                w.move(-9999, -9999)
                continue
            iw = w.width() if w.width() > 0 else ITEM_W
            ih = w.height() if w.height() > 0 else ITEM_H
            if row_h > 0 and x + iw + GAP > avail_w:
                x = GAP
                y += row_h + GAP
                row_h = 0
            w.move(x, y)
            x += iw + GAP
            row_h = max(row_h, ih)

        visible = [w for w in self._widgets if w.isVisible()]
        total_h = (y + row_h + GAP) if visible else 60
        self.setFixedHeight(max(total_h, 0))

    def index_at(self, pos: QPoint) -> int:
        """拖拽插入位置：与流式布局顺序一致，同行每个图标单独判断，不再只认行内第一个。"""
        visible = [w for w in self._widgets if w.isVisible()]
        if not visible:
            return 0

        if pos.y() < visible[0].geometry().top():
            return 0

        for w in visible:
            g = w.geometry()
            if pos.y() < g.top():
                return self._widgets.index(w)
            if g.top() <= pos.y() <= g.bottom():
                if pos.x() < g.left():
                    return self._widgets.index(w)
                if pos.x() <= g.right():
                    mid = (g.left() + g.right()) // 2
                    if pos.x() < mid:
                        return self._widgets.index(w)
                    return self._widgets.index(w) + 1
                # 落在该行、但在本图标右侧，继续看下一个（同行或下一行）
        return len(self._widgets)

    def _clear_selection_except(self, keep):
        for w in self._widgets:
            if w is not keep and hasattr(w, "_set_selected"):
                w._set_selected(False)

    def clear_selection(self):
        self._clear_selection_except(None)

    def _begin_rubber(self, pos: QPoint):
        self.clear_selection()
        self._rubber_active = True
        self._rubber_origin = pos
        self._rubber_rect = QRect(pos, QSize())
        self.grabMouse()
        self.update()

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            ch = self.childAt(event.pos())
            if ch is None:
                self._begin_rubber(event.pos())
                return
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if self._rubber_active:
            self._rubber_rect = QRect(self._rubber_origin, event.pos()).normalized()
            self.update()
            return
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        if self._rubber_active and event.button() == Qt.MouseButton.LeftButton:
            self._rubber_active = False
            self.releaseMouse()
            r = self._rubber_rect
            self._rubber_rect = None
            self.update()
            if r is not None and r.width() >= RUBBER_MIN and r.height() >= RUBBER_MIN:
                self.clear_selection()
                for w in self._widgets:
                    if not w.isVisible():
                        continue
                    if hasattr(w, "_set_selected") and r.intersects(w.geometry()):
                        w._set_selected(True)
            return
        super().mouseReleaseEvent(event)

    def paintEvent(self, event):
        super().paintEvent(event)
        need_drag = self._drag_highlight
        need_rubber = self._rubber_rect is not None and self._rubber_active
        if not need_drag and not need_rubber:
            return
        p = QPainter(self)
        p.setRenderHint(QPainter.RenderHint.Antialiasing)
        if need_drag:
            pen = QPen(QColor("#4a9eff"), 2, Qt.PenStyle.DashLine)
            pen.setDashPattern([6, 4])
            p.setPen(pen)
            p.setBrush(QColor(74, 158, 255, 25))
            p.drawRoundedRect(2, 2, self.width() - 4, self.height() - 4, 6, 6)
        if need_rubber:
            pen = QPen(QColor("#4a9eff"), 1, Qt.PenStyle.DashLine)
            p.setPen(pen)
            p.setBrush(QColor(74, 158, 255, 40))
            p.drawRoundedRect(self._rubber_rect, 2, 2)
        p.end()
