import os
from PyQt6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QLabel,
    QPushButton, QMessageBox, QMenu, QSizePolicy
)
from PyQt6.QtCore import Qt, pyqtSignal, QSize, QPoint, QMimeData, QByteArray
from PyQt6.QtGui import (
    QDragEnterEvent, QDragMoveEvent, QDropEvent, QCursor, QDrag, QPainter, QColor, QPen,
    QShortcut, QKeySequence,
)
from db import database
from ui.item import ItemWidget
from ui.flow_layout import FlowContainer
import ui.theme as theme
import qtawesome as qta
import shortcut_target
import ui.dialog_style as dialog_style


def item_count_for_group(group_id: int) -> int:
    """分组内程序/文件总数（文件夹分组数磁盘文件，普通分组数数据库条目）。"""
    groups = database.get_groups()
    group_info = next((g for g in groups if g["id"] == group_id), {})
    folder_path = (group_info.get("folder_path", "") or "").strip()
    if folder_path and os.path.isdir(folder_path):
        n = 0
        try:
            import stat as _stat
            for fname in os.listdir(folder_path):
                fpath = os.path.join(folder_path, fname)
                try:
                    if _stat.S_ISREG(os.stat(fpath).st_mode):
                        n += 1
                except OSError:
                    continue
        except OSError:
            pass
        return n
    return len(database.get_items(group_id))


class DragHandle(QLabel):
    """分组拖拽排序手柄"""

    def __init__(self, group_widget, parent=None):
        super().__init__(parent)
        self.group_widget = group_widget
        self.setFixedSize(18, 18)
        self.setCursor(Qt.CursorShape.SizeVerCursor)
        self.setToolTip("拖拽排序")
        self.setPixmap(qta.icon("fa5s.grip-vertical", color="#888").pixmap(14, 14))

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self._drag_start = event.pos()

    def mouseMoveEvent(self, event):
        if not (event.buttons() & Qt.MouseButton.LeftButton):
            return
        if (event.pos() - self._drag_start).manhattanLength() < 6:
            return
        drag = QDrag(self)
        mime = QMimeData()
        gid = self.group_widget.group_data["id"]
        mime.setData("application/x-group-id", QByteArray(str(gid).encode()))
        drag.setMimeData(mime)
        px = self.group_widget.header.grab()
        drag.setPixmap(px)
        drag.setHotSpot(QPoint(px.width() // 2, px.height() // 2))
        drag.exec(Qt.DropAction.MoveAction)


class FlowWidget(QWidget):
    item_dropped = pyqtSignal(int)
    refreshed = pyqtSignal()

    def __init__(self, group_id, parent=None):
        super().__init__(parent)
        self.group_id = group_id
        self.setAcceptDrops(True)
        self._drop_indicator = -1

        layout = QVBoxLayout(self)
        layout.setContentsMargins(2, 2, 2, 2)
        layout.setSpacing(0)

        self._container = FlowContainer(self)
        layout.addWidget(self._container)
        layout.addStretch()

        self.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
        _del_sc = QShortcut(QKeySequence.StandardKey.Delete, self)
        _del_sc.setContext(Qt.ShortcutContext.WidgetWithChildrenShortcut)
        _del_sc.activated.connect(self._delete_selected_items)
        _esc_sc = QShortcut(QKeySequence(Qt.Key.Key_Escape), self)
        _esc_sc.setContext(Qt.ShortcutContext.WidgetWithChildrenShortcut)
        _esc_sc.activated.connect(self._container.clear_selection)

        self.refresh()

    def refresh(self, keyword=""):
        self._container.clear_widgets()

        # 判断是否是文件夹分组
        groups = database.get_groups()
        group_info = next((g for g in groups if g["id"] == self.group_id), {})
        folder_path = (group_info.get("folder_path", "") or "").strip()

        if folder_path and os.path.isdir(folder_path):
            # 文件夹分组：直接读目录，不走数据库
            items = []
            try:
                for fname in sorted(os.listdir(folder_path)):
                    fpath = os.path.join(folder_path, fname)
                    try:
                        import stat as _stat
                        if _stat.S_ISREG(os.stat(fpath).st_mode):
                            name = os.path.splitext(fname)[0] or fname
                            items.append({"id": None, "name": name,
                                          "path": fpath, "group_id": self.group_id})
                    except Exception:
                        continue
            except Exception:
                pass
        else:
            # 普通分组：走数据库
            items = database.get_items(self.group_id)

        for data in items:
            iw = ItemWidget(data, self._container)
            iw.show()
            if keyword and not iw.matches(keyword):
                iw.hide()
            self._container.add_widget(iw)

        self._container._schedule_relayout()
        self.updateGeometry()
        self.refreshed.emit()

    def apply_theme(self):
        for w in self._container.get_widgets():
            w._apply_theme()

    def filter(self, keyword: str):
        # 不重建，只切换可见性，然后重新布局
        for w in self._container.get_widgets():
            if keyword:
                w.setVisible(w.matches(keyword))
            else:
                w.show()
        self._container._relayout()
        self.updateGeometry()

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            cg = self._container.geometry()
            if not cg.contains(event.pos()):
                lp = self._container.mapFrom(self, event.pos())
                w, h = max(1, self._container.width()), max(1, self._container.height())
                lp.setX(max(0, min(lp.x(), w - 1)))
                lp.setY(max(0, min(lp.y(), h - 1)))
                self._container._begin_rubber(lp)
                return
        super().mousePressEvent(event)

    def _delete_selected_items(self):
        widgets = [w for w in self._container.get_widgets() if getattr(w, "_selected", False)]
        if not widgets:
            return
        ret = dialog_style.question(
            self,
            "批量删除",
            f"确定删除选中的 {len(widgets)} 项？",
            default_button=QMessageBox.StandardButton.No,
        )
        if ret != QMessageBox.StandardButton.Yes:
            return
        for w in widgets:
            w._delete_item(skip_confirm=True, skip_refresh=True)
        self.refresh()

    def dragEnterEvent(self, event: QDragEnterEvent):
        if event.mimeData().hasFormat("application/x-item-id") or \
                event.mimeData().hasUrls():
            self._container.set_drag_highlight(True)
            event.acceptProposedAction()
        else:
            event.ignore()

    def dragMoveEvent(self, event: QDragMoveEvent):
        if event.mimeData().hasFormat("application/x-item-id"):
            pos = self._container.mapFrom(self, event.position().toPoint())
            self._drop_indicator = self._container.index_at(pos)
            self.update()
        event.acceptProposedAction()

    def dragLeaveEvent(self, event):
        self._container.set_drag_highlight(False)
        self._drop_indicator = -1
        self.update()

    def paintEvent(self, event):
        super().paintEvent(event)
        if self._drop_indicator < 0:
            return
        from PyQt6.QtGui import QPainter, QColor, QPen
        all_w = self._container.get_widgets()
        if not all_w:
            return
        pos = self._drop_indicator
        p = QPainter(self)
        p.setPen(QPen(QColor("#4a9eff"), 2))
        offset = self._container.pos()

        def _draw_vline(x_local: int, g):
            x = offset.x() + x_local
            y1 = offset.y() + g.top()
            y2 = offset.y() + g.bottom()
            p.drawLine(x, y1, x, y2)

        if pos >= len(all_w):
            for j in range(len(all_w) - 1, -1, -1):
                if all_w[j].isVisible():
                    g = all_w[j].geometry()
                    _draw_vline(g.right() + 2, g)
                    break
            p.end()
            return

        w = all_w[pos]
        if w.isVisible():
            g = w.geometry()
            _draw_vline(g.left() - 2, g)
        else:
            placed = False
            for j in range(pos, len(all_w)):
                if all_w[j].isVisible():
                    g = all_w[j].geometry()
                    _draw_vline(g.left() - 2, g)
                    placed = True
                    break
            if not placed:
                for j in range(pos - 1, -1, -1):
                    if all_w[j].isVisible():
                        g = all_w[j].geometry()
                        _draw_vline(g.right() + 2, g)
                        break
        p.end()

    def dropEvent(self, event: QDropEvent):
        self._container.set_drag_highlight(False)
        self._drop_indicator = -1
        self.update()
        mime = event.mimeData()
        if mime.hasFormat("application/x-item-id"):
            item_id = int(mime.data("application/x-item-id").data().decode())
            pos = self._container.mapFrom(self, event.position().toPoint())
            insert_pos = self._container.index_at(pos)
            database.move_item(item_id, self.group_id, 999)
            items = database.get_items(self.group_id)
            ids = [it["id"] for it in items if it["id"] != item_id]
            ids.insert(min(insert_pos, len(ids)), item_id)
            database.reorder_items(self.group_id, ids)
            self.refresh()
            self.item_dropped.emit(item_id)
            event.acceptProposedAction()
        elif mime.hasUrls():
            for url in mime.urls():
                path = url.toLocalFile()
                if path:
                    store = shortcut_target.path_for_storage(path)
                    name = shortcut_target.item_name_from_sources(path, store)
                    database.add_item(self.group_id, name, store)
            self.refresh()
            event.acceptProposedAction()
        else:
            event.ignore()

    def contextMenuEvent(self, event):
        from PyQt6.QtWidgets import QApplication
        t = theme.current()
        menu_style = f"""
            QMenu {{ background:{t['menu_bg']}; color:{t['menu_color']};
                     border:1px solid {t['menu_border']}; padding:4px; border-radius:6px; }}
            QMenu::item {{ padding:5px 16px; border-radius:3px; }}
            QMenu::item:selected {{ background:{t['menu_selected']}; }}
            QMenu::item:disabled {{ color:#666; }}
        """
        menu = QMenu(self)
        menu.setStyleSheet(menu_style)

        refresh_act = menu.addAction("🔄  刷新分组")
        menu.addSeparator()

        # 检测剪贴板
        clipboard = QApplication.clipboard()
        mime = clipboard.mimeData()
        paste_act = None
        clipboard_paths = []

        if mime and mime.hasUrls():
            # 获取本分组信息
            groups = database.get_groups()
            group_info = next((g for g in groups if g["id"] == self.group_id), {})
            is_folder_group = bool(group_info.get("folder_path", ""))

            # 快捷方式扩展名
            shortcut_exts = {".exe", ".lnk", ".bat", ".cmd", ".url"}

            for url in mime.urls():
                path = url.toLocalFile()
                if not path:
                    continue
                ext = os.path.splitext(path)[1].lower()
                if is_folder_group:
                    # 文件夹分组：接受所有文件
                    if os.path.isfile(path):
                        clipboard_paths.append(path)
                else:
                    # 普通分组：只接受快捷方式/程序
                    if ext in shortcut_exts:
                        clipboard_paths.append(path)

            if clipboard_paths:
                label = f"📋  粘贴 {len(clipboard_paths)} 个文件" if len(clipboard_paths) > 1 \
                    else f"📋  粘贴「{os.path.basename(clipboard_paths[0])}」"
                paste_act = menu.addAction(label)
            else:
                act = menu.addAction("📋  粘贴（无可用文件）")
                act.setEnabled(False)
        else:
            act = menu.addAction("📋  粘贴（剪贴板为空）")
            act.setEnabled(False)

        action = menu.exec(event.globalPos())
        if action == refresh_act:
            self.refresh()
        elif paste_act and action == paste_act:
            groups = database.get_groups()
            group_info = next((g for g in groups if g["id"] == self.group_id), {})
            folder_path = group_info.get("folder_path", "")
            is_folder_group = bool(folder_path)

            for path in clipboard_paths:
                if is_folder_group:
                    # 真正复制文件到文件夹目录
                    import shutil
                    dest_path = os.path.join(folder_path, os.path.basename(path))
                    # 避免覆盖同名文件，自动重命名
                    if os.path.exists(dest_path) and dest_path != path:
                        base, ext = os.path.splitext(os.path.basename(path))
                        i = 1
                        while os.path.exists(dest_path):
                            dest_path = os.path.join(folder_path, f"{base} ({i}){ext}")
                            i += 1
                    try:
                        if path != dest_path:
                            shutil.copy2(path, dest_path)
                        final_path = dest_path
                    except Exception as e:
                        print(f"复制失败: {path} -> {e}")
                        final_path = path  # 复制失败就用原路径
                    name = os.path.splitext(os.path.basename(final_path))[0] or os.path.basename(final_path)
                else:
                    final_path = shortcut_target.path_for_storage(path)
                    name = shortcut_target.item_name_from_sources(path, final_path)

                database.add_item(self.group_id, name, final_path)
            self.refresh()


class GroupWidget(QWidget):
    request_refresh_all = pyqtSignal()

    def __init__(self, group_data: dict, parent=None):
        super().__init__(parent)
        self.group_data = group_data
        self.collapsed = False
        self._build_ui()
        self._apply_theme()

    def _build_ui(self):
        self.main_layout = QVBoxLayout(self)
        self.main_layout.setContentsMargins(0, 0, 0, 4)
        self.main_layout.setSpacing(0)

        self.header = QWidget()
        self.header.setFixedHeight(32)
        self.header.setCursor(Qt.CursorShape.PointingHandCursor)
        self.header.mousePressEvent = lambda e: self._toggle() \
            if e.button() == Qt.MouseButton.LeftButton else None

        h_layout = QHBoxLayout(self.header)
        h_layout.setContentsMargins(4, 0, 6, 0)
        h_layout.setSpacing(4)

        # 拖拽排序手柄
        self._drag_handle = DragHandle(self)

        self.toggle_icon = QLabel()
        self.toggle_icon.setFixedSize(14, 14)
        self.toggle_icon.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)

        self.title_label = QLabel()
        self.title_label.setStyleSheet("font-size:12px; font-weight:bold; background:transparent;")
        self.title_label.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)

        folder_path = self.group_data.get("folder_path", "") or ""
        # 只显示最后两级路径，避免太长
        if folder_path:
            parts = folder_path.replace("\\", "/").rstrip("/").split("/")
            display_path = "/".join(parts[-2:]) if len(parts) >= 2 else folder_path
        else:
            display_path = ""
        self.path_label = QLabel(display_path)
        self.path_label.setStyleSheet("font-size:9px; color:#888; background:transparent;")
        self.path_label.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
        self.path_label.setVisible(bool(folder_path))
        self.path_label.setMaximumWidth(110)
        self.path_label.setToolTip(folder_path)
        self.path_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)

        self._add_btn = QPushButton()
        self._add_btn.setFixedSize(22, 22)
        self._add_btn.setToolTip("添加程序")
        self._add_btn.setStyleSheet("border:none; background:transparent;")
        self._add_btn.clicked.connect(self._add_item_dialog)
        self._add_btn.mousePressEvent = lambda e: (
            self._add_btn.click() if e.button() == Qt.MouseButton.LeftButton else None
        )

        self._more_btn = QPushButton()
        self._more_btn.setFixedSize(22, 22)
        self._more_btn.setStyleSheet("border:none; background:transparent;")
        self._more_btn.clicked.connect(self._group_menu)

        h_layout.addWidget(self._drag_handle)
        h_layout.addWidget(self.toggle_icon)
        h_layout.addWidget(self.title_label)
        h_layout.addStretch()
        h_layout.addWidget(self.path_label)
        h_layout.addWidget(self._add_btn)
        h_layout.addWidget(self._more_btn)

        self.flow = FlowWidget(self.group_data["id"])
        self.flow.item_dropped.connect(lambda _: self.request_refresh_all.emit())
        self.flow.refreshed.connect(self._update_title_count)
        self._update_title_count()

        self.main_layout.addWidget(self.header)
        self.main_layout.addWidget(self.flow)

    def _update_title_count(self):
        n = item_count_for_group(self.group_data["id"])
        self.title_label.setText(f"{self.group_data['name']} ({n})")

    def _apply_theme(self):
        t = theme.current()
        ic = "#cccccc" if theme.name() == "dark" else "#555555"

        self.header.setStyleSheet(f"""
            QWidget {{ background:{t['header_bg']}; border-radius:5px; }}
            QWidget:hover {{ background:{t['header_hover']}; }}
        """)
        self.title_label.setStyleSheet(
            f"color:{t['item_name_color']}; font-size:12px; font-weight:bold; background:transparent;"
        )
        # 展开/收缩图标
        arrow = "fa5s.chevron-down" if not self.collapsed else "fa5s.chevron-right"
        self.toggle_icon.setPixmap(qta.icon(arrow, color=ic).pixmap(14, 14))

        self._add_btn.setIcon(qta.icon("fa5s.plus", color=ic))
        self._add_btn.setIconSize(QSize(11, 11))
        self._more_btn.setIcon(qta.icon("fa5s.ellipsis-h", color=ic))
        self._more_btn.setIconSize(QSize(11, 11))
        self._drag_handle.setPixmap(qta.icon("fa5s.grip-vertical", color=ic).pixmap(14, 14))
        path_color = "#777" if theme.name() == "dark" else "#999"
        self.path_label.setStyleSheet(
            f"font-size:9px; color:{path_color}; background:transparent;"
        )

        self.flow.apply_theme()

    def _toggle(self):
        self.collapsed = not self.collapsed
        self.flow.setVisible(not self.collapsed)
        if not self.collapsed:
            self.flow.updateGeometry()
            self.updateGeometry()
        # 更新箭头图标
        t = theme.current()
        ic = "#cccccc" if theme.name() == "dark" else "#555555"
        arrow = "fa5s.chevron-down" if not self.collapsed else "fa5s.chevron-right"
        self.toggle_icon.setPixmap(qta.icon(arrow, color=ic).pixmap(14, 14))

    def _add_item_dialog(self):
        path, _ = dialog_style.get_open_file_name(
            self,
            "选择程序或快捷方式",
            "",
            "程序/快捷方式 (*.exe *.lnk *.bat *.cmd *.url);;所有文件 (*)",
        )
        if path:
            store = shortcut_target.path_for_storage(path)
            name = shortcut_target.item_name_from_sources(path, store)
            database.add_item(self.group_data["id"], name, store)
            self.flow.refresh()

    def _group_menu(self):
        t = theme.current()
        menu = QMenu(self)
        menu.setStyleSheet(f"""
            QMenu {{ background:{t['menu_bg']}; color:{t['menu_color']};
                     border:1px solid {t['menu_border']}; padding:4px; border-radius:6px; }}
            QMenu::item {{ padding:5px 16px; border-radius:3px; }}
            QMenu::item:selected {{ background:{t['menu_selected']}; }}
        """)
        rename_act = menu.addAction("重命名")
        del_act = menu.addAction("删除分组")
        action = menu.exec(QCursor.pos())
        if action == rename_act:
            name, ok = dialog_style.get_text(
                self, "重命名", "新名称:", text=self.group_data["name"]
            )
            if ok and name.strip():
                database.rename_group(self.group_data["id"], name.strip())
                self.group_data["name"] = name.strip()
                self._update_title_count()
        elif action == del_act:
            ret = dialog_style.question(
                self,
                "确认",
                f"删除分组「{self.group_data['name']}」及其所有程序？",
                default_button=QMessageBox.StandardButton.No,
            )
            if ret == QMessageBox.StandardButton.Yes:
                database.delete_group(self.group_data["id"])
                self.request_refresh_all.emit()

    def refresh(self, keyword=""):
        self.flow.refresh(keyword)

    def filter(self, keyword: str):
        self.flow.filter(keyword)
        if keyword and self.collapsed:
            self._toggle()
