# -*- coding: utf-8 -*-
"""主窗口：整合侧边栏、网格/列表视图、详情面板、工具栏与状态栏。"""
from __future__ import annotations

import os
import subprocess
import sys
from pathlib import Path

from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QAction, QDragEnterEvent, QDropEvent, QKeySequence
from PySide6.QtWidgets import (
    QComboBox, QFileDialog, QHBoxLayout, QLabel, QLineEdit, QMainWindow,
    QMessageBox, QPushButton, QSplitter, QStackedWidget, QStatusBar,
    QVBoxLayout, QWidget,
)

from .. import __version__, APP_DISPLAY_NAME
from ..db import Database
from ..library import export_library
from ..models import AssetType
from .detail_panel import DetailPanel
from .grid_view import GridView, PAGE_SIZE
from .list_view import ListView
from .sidebar import Sidebar
from .widgets import Pager

SORT_OPTIONS = [
    ("最近更新", "updated_at", True),
    ("最早更新", "updated_at", False),
    ("创建时间", "created_at", True),
    ("标题 A-Z", "title", False),
    ("页序", "page_index", False),
    ("类型", "type", False),
]


class MainWindow(QMainWindow):
    """应用主窗口。"""

    def __init__(self, db: Database):
        super().__init__()
        self.db = db
        self.setWindowTitle(APP_DISPLAY_NAME)
        self.resize(1400, 880)
        self.setMinimumSize(1024, 680)

        # 状态
        self._filter = None          # None / AssetType / ("series", id) / ("tag", name)
        self._search = ""
        self._offset = 0
        self._limit = PAGE_SIZE
        self._sort = SORT_OPTIONS[0]
        self._selected_id = None
        self._page_assets: list = []

        thumb_root = Path(db.db_path).parent
        self._build_ui(thumb_root)
        self._build_menu()
        self.refresh_all()

    # ==================================================================
    # UI 构建
    # ==================================================================
    def _build_ui(self, thumb_root: Path):
        central = QWidget()
        central.setObjectName("Root")
        root = QVBoxLayout(central)
        root.setContentsMargins(0, 0, 0, 0)
        root.setSpacing(0)

        # ── 工具栏（单行）──
        tb = QWidget()
        tb.setStyleSheet("background: #23262E; border-bottom: 1px solid #333845;")
        tb_lay = QHBoxLayout(tb)
        tb_lay.setContentsMargins(12, 7, 12, 7)
        tb_lay.setSpacing(8)

        # 搜索框（占据主要弹性空间）
        self.search = QLineEdit()
        self.search.setPlaceholderText("搜索标题、描述、正文、标签…")
        self.search.setClearButtonEnabled(True)
        self.search.setFixedHeight(30)
        self._search_timer = QTimer(self)
        self._search_timer.setSingleShot(True)
        self._search_timer.setInterval(280)
        self._search_timer.timeout.connect(self._on_search)
        self.search.textChanged.connect(lambda _: self._search_timer.start())
        tb_lay.addWidget(self.search, 1)

        # 排序
        self.sort_combo = QComboBox()
        for label, _, _ in SORT_OPTIONS:
            self.sort_combo.addItem(label)
        self.sort_combo.setFixedWidth(104)
        self.sort_combo.setToolTip("排序方式")
        self.sort_combo.currentIndexChanged.connect(self._on_sort_changed)
        tb_lay.addWidget(self.sort_combo)

        # 视图切换（紧凑图标组）
        view_box = QWidget()
        vl = QHBoxLayout(view_box)
        vl.setContentsMargins(0, 0, 0, 0)
        vl.setSpacing(2)
        self.btn_grid = QPushButton("▦")
        self.btn_grid.setObjectName("Ghost")
        self.btn_grid.setCheckable(True)
        self.btn_grid.setChecked(True)
        self.btn_grid.setFixedWidth(30)
        self.btn_grid.setFixedHeight(30)
        self.btn_grid.setToolTip("网格视图 (Ctrl+1)")
        self.btn_grid.clicked.connect(lambda: self._switch_view(True))
        self.btn_list = QPushButton("☰")
        self.btn_list.setObjectName("Ghost")
        self.btn_list.setCheckable(True)
        self.btn_list.setFixedWidth(30)
        self.btn_list.setFixedHeight(30)
        self.btn_list.setToolTip("列表视图 (Ctrl+2)")
        self.btn_list.clicked.connect(lambda: self._switch_view(False))
        vl.addWidget(self.btn_grid)
        vl.addWidget(self.btn_list)
        tb_lay.addWidget(view_box)

        # 操作按钮
        self.btn_new = QPushButton("＋ 新建")
        self.btn_new.setFixedHeight(30)
        self.btn_new.clicked.connect(self.new_asset)
        self.btn_import = QPushButton("⬆ 导入")
        self.btn_import.setObjectName("Ghost")
        self.btn_import.setFixedHeight(30)
        self.btn_import.clicked.connect(self.open_import)
        tb_lay.addWidget(self.btn_new)
        tb_lay.addWidget(self.btn_import)

        root.addWidget(tb)

        # ── 主体三栏 ──
        splitter = QSplitter(Qt.Orientation.Horizontal)
        splitter.setHandleWidth(1)
        splitter.setStyleSheet("QSplitter::handle { background: #333845; }")

        self.sidebar = Sidebar()
        self.sidebar.filter_changed.connect(self._on_filter_changed)
        self.sidebar.new_series_requested.connect(self.manage_series)
        splitter.addWidget(self.sidebar)

        # 中间：网格 / 列表 堆叠
        mid = QWidget()
        mid_lay = QVBoxLayout(mid)
        mid_lay.setContentsMargins(0, 0, 0, 0)
        mid_lay.setSpacing(0)
        self.stack = QStackedWidget()
        self.grid = GridView(thumb_root=thumb_root)
        self.grid.asset_selected.connect(self._on_asset_selected)
        self.grid.asset_double_clicked.connect(self._on_asset_double)
        self.list = ListView(thumb_root=thumb_root)
        self.list.asset_selected.connect(self._on_asset_selected)
        self.list.asset_double_clicked.connect(self._on_asset_double)
        self.stack.addWidget(self.grid)
        self.stack.addWidget(self.list)
        mid_lay.addWidget(self.stack, 1)

        # 分页
        pager_wrap = QWidget()
        pw = QVBoxLayout(pager_wrap)
        pw.setContentsMargins(12, 6, 12, 6)
        self.pager = Pager()
        self.pager.prev_clicked.connect(lambda: self._page(-1))
        self.pager.next_clicked.connect(lambda: self._page(1))
        pw.addWidget(self.pager)
        mid_lay.addWidget(pager_wrap)

        splitter.addWidget(mid)

        self.detail = DetailPanel(thumb_root=thumb_root)
        self.detail.edit_requested.connect(self.edit_asset)
        self.detail.delete_requested.connect(self.delete_asset)
        self.detail.open_file.connect(self._open_file)
        self.detail.open_folder.connect(self._open_folder)
        splitter.addWidget(self.detail)

        splitter.setSizes([196, 850, 320])
        root.addWidget(splitter, 1)

        self.setCentralWidget(central)
        self._build_statusbar()

    def _build_statusbar(self):
        sb = QStatusBar()
        self.setStatusBar(sb)
        self.sb_left = QLabel("就绪")
        self.sb_left.setStyleSheet("color: #9AA0AB; font-size: 11px;")
        self.sb_right = QLabel(f"v{__version__}")
        self.sb_right.setStyleSheet("color: #6B7280; font-size: 11px;")
        sb.addWidget(self.sb_left)
        sb.addPermanentWidget(self.sb_right)

    # ==================================================================
    # 菜单
    # ==================================================================
    def _build_menu(self):
        mb = self.menuBar()
        mb.setStyleSheet("QMenuBar { background: #23262E; border-bottom: 1px solid #333845; }")

        m_file = mb.addMenu("文件(&F)")
        self._add(m_file, "导入资产…", self.open_import, "Ctrl+O")
        self._add(m_file, "新建资产…", self.new_asset, "Ctrl+N")
        m_file.addSeparator()
        self._add(m_file, "备份数据库…", self.backup_db, "Ctrl+Shift+B")
        self._add(m_file, "导出资产库 (JSON)…", self.export_json, "Ctrl+Shift+E")
        m_file.addSeparator()
        self._add(m_file, "退出", self.close, "Ctrl+Q")

        m_lib = mb.addMenu("资产库(&L)")
        self._add(m_lib, "系列与章节管理…", self.manage_series)
        self._add(m_lib, "刷新", self.refresh_all, "F5")

        m_view = mb.addMenu("视图(&V)")
        self._add(m_view, "网格视图", lambda: self._switch_view(True), "Ctrl+1")
        self._add(m_view, "列表视图", lambda: self._switch_view(False), "Ctrl+2")
        m_view.addSeparator()
        self._add(m_view, "全部资产", lambda: self._apply_filter(None), "Ctrl+0")

        m_help = mb.addMenu("帮助(&H)")
        self._add(m_help, "关于", self.about)

    def _add(self, menu, text, slot, shortcut=None):
        act = QAction(text, self)
        if shortcut:
            act.setShortcut(QKeySequence(shortcut))
        act.triggered.connect(slot)
        menu.addAction(act)
        return act

    # ==================================================================
    # 数据加载
    # ==================================================================
    def refresh_all(self):
        """全量刷新：侧边栏 + 当前页 + 统计。"""
        self.sidebar.set_series(self.db.list_series())
        self.sidebar.set_tags(self.db.list_tags())
        self.sidebar.update_stats(self.db.stats())
        self._load_page(keep_scroll=False)

    def _load_page(self, keep_scroll=True):
        """加载当前页资产并渲染。"""
        f = self._filter
        asset_type = f if isinstance(f, AssetType) else None
        series_id = f[1] if isinstance(f, tuple) and f[0] == "series" else None
        tag = f[1] if isinstance(f, tuple) and f[0] == "tag" else None

        self._total = self.db.count_assets(
            asset_type=asset_type, series_id=series_id, tag=tag, search=self._search or None)

        self._page_assets = self.db.list_assets(
            asset_type=asset_type, series_id=series_id, tag=tag,
            search=self._search or None,
            limit=self._limit, offset=self._offset,
            order_by=self._sort[1], reverse=self._sort[2],
        )

        series_map = {s.id: s.name for s in self.db.list_series()}
        chapter_map = {c.id: c.label for c in self.db.list_chapters()}

        if self.stack.currentWidget() is self.grid:
            self.grid.set_assets(self._page_assets)
        else:
            self.list.set_assets(self._page_assets, series_map, chapter_map)
        self.pager.update_info(self._offset, self._limit, self._total)

        # 状态
        self.sb_left.setText(f"共 {self._total} 项资产")
        if self._selected_id is not None:
            self._show_detail(self._selected_id)
        else:
            self.detail.show_asset(None)

    def _page(self, direction: int):
        step = direction * self._limit
        if self._offset + step < 0:
            self._offset = 0
        elif self._offset + step + self._limit > max(self._total, 0):
            pages = max(1, (self._total + self._limit - 1) // self._limit)
            self._offset = (pages - 1) * self._limit
        else:
            self._offset += step
        self._load_page()

    def _page_first(self):
        self._offset = 0
        self._load_page()

    # ==================================================================
    # 过滤 / 搜索 / 视图
    # ==================================================================
    def _on_filter_changed(self, payload):
        self._filter = payload
        self._offset = 0
        if payload is None:
            self.sidebar.btn_all.setChecked(True)
        self._load_page()

    def _apply_filter(self, payload):
        self.sidebar.filter_changed.emit(payload)

    def _on_search(self):
        self._search = self.search.text().strip()
        self.sidebar.clear_selection()
        self._offset = 0
        self._load_page()

    def _on_sort_changed(self, idx):
        self._sort = SORT_OPTIONS[idx]
        self._offset = 0
        self._load_page()

    def _switch_view(self, grid: bool):
        self.btn_grid.setChecked(grid)
        self.btn_list.setChecked(not grid)
        self.stack.setCurrentIndex(0 if grid else 1)
        if grid:
            self.grid.set_assets(self._page_assets)
            if self._selected_id:
                self.grid.highlight(self._selected_id)
        else:
            series_map = {s.id: s.name for s in self.db.list_series()}
            chapter_map = {c.id: c.label for c in self.db.list_chapters()}
            self.list.set_assets(self._page_assets, series_map, chapter_map)
            if self._selected_id:
                self.list.highlight(self._selected_id)

    # ==================================================================
    # 选中 / 详情
    # ==================================================================
    def _on_asset_selected(self, asset_id: int):
        self._selected_id = asset_id
        if self.grid is self.stack.currentWidget():
            self.grid.highlight(asset_id)
        else:
            self.list.highlight(asset_id)
        self._show_detail(asset_id)

    def _show_detail(self, asset_id: int | None):
        a = self.db.get_asset(asset_id) if asset_id else None
        s = self.db.get_series(a.series_id) if a and a.series_id else None
        c = None
        if a and a.chapter_id:
            c = next((x for x in self.db.list_chapters(a.series_id)
                      if x.id == a.chapter_id), None)
        self.detail.show_asset(
            a,
            series_name=s.name if s else "",
            chapter_label=c.label if c else "",
        )

    def _on_asset_double(self, asset_id: int):
        a = self.db.get_asset(asset_id)
        if not a:
            return
        if a.is_text:
            self._open_text_preview(a)
        elif a.type == AssetType.AUDIO:
            self.detail.tabs.setCurrentIndex(1)
        else:
            self._open_image_preview(asset_id)

    # ==================================================================
    # 操作：新建 / 编辑 / 删除
    # ==================================================================
    def new_asset(self):
        from .asset_dialog import AssetDialog
        default_sid = self._filter[1] if isinstance(self._filter, tuple) and self._filter[0] == "series" else None
        dlg = AssetDialog(self.db, self, default_series_id=default_sid)
        if dlg.exec() == dlg.Accepted:
            a = dlg.result_asset()
            a = self.db.create_asset(a)
            self._after_asset_change(a)

    def edit_asset(self, asset_id: int):
        a = self.db.get_asset(asset_id)
        if not a:
            return
        from .asset_dialog import AssetDialog
        dlg = AssetDialog(self.db, self, asset=a)
        if dlg.exec() == dlg.Accepted:
            a = dlg.result_asset()
            a.id = asset_id
            self.db.update_asset(a)
            self._after_asset_change(a)

    def delete_asset(self, asset_id: int):
        a = self.db.get_asset(asset_id)
        if not a:
            return
        ans = QMessageBox.question(
            self, "确认删除",
            f"删除资产「{a.title}」？\n\n文件不会被删除，仅移除资产记录。",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
        )
        if ans == QMessageBox.StandardButton.Yes:
            self.db.delete_asset(asset_id)
            self._selected_id = None
            self.detail.show_asset(None)
            self.refresh_all()
            self.sb_left.setText("已删除资产")

    def _after_asset_change(self, a):
        # 生成缩略图
        if a.id and not a.is_text and a.file_path:
            from .. import thumbnail as thumb_mod
            thumb_mod.make_thumb(a.file_path, a.id, Path(self.db.db_path).parent)
        self.refresh_all()

    def _open_image_preview(self, asset_id: int):
        a = self.db.get_asset(asset_id)
        if not a or not os.path.exists(a.file_path):
            return
        self.detail.tabs.setCurrentIndex(0)
        self.detail.image_view.show_image(a.file_path)

    def _open_text_preview(self, a):
        # 文本资产用详情面板的文本 Tab 展示
        self.detail.tabs.setCurrentIndex(2)
        self.detail.text_view.set_text(a.content or a.description)

    # ==================================================================
    # 文件 / 库操作
    # ==================================================================
    def open_import(self):
        from .import_dialog import ImportDialog
        dlg = ImportDialog(self.db, self)
        if dlg.exec():
            rep = dlg.report()
            if rep and rep.ok:
                self.refresh_all()
                self.sb_left.setText(f"已导入 {rep.imported} 项资产")
            elif rep and rep.failed:
                QMessageBox.warning(self, "导入部分失败", "\n".join(rep.failed[:10]))

    def manage_series(self):
        from .series_dialog import SeriesManagerDialog
        dlg = SeriesManagerDialog(self.db, self)
        dlg.exec()
        self.refresh_all()

    def backup_db(self):
        d = QFileDialog.getExistingDirectory(self, "选择备份目录",
                                             str(Path(self.db.db_path).parent))
        if d:
            p = self.db.backup(d)
            QMessageBox.information(self, "备份完成", f"已备份到：\n{p}")

    def export_json(self):
        p = QFileDialog.getSaveFileName(
            self, "导出资产库", "library_export.json", "JSON 文件 (*.json)")[0]
        if p:
            out = export_library(self.db, p)
            QMessageBox.information(self, "导出完成", f"已导出到：\n{out}")

    def about(self):
        QMessageBox.about(
            self, f"关于 {APP_DISPLAY_NAME}",
            f"<h2>🎬 {APP_DISPLAY_NAME}</h2>"
            f"<p><b>版本</b> {__version__}</p>"
            f"<p>本地漫画资产管理器 · 数据全部存储在本地 SQLite 数据库，无云端依赖。</p>"
            f"<p><b>数据库</b><br>{self.db.db_path}</p>"
            f"<p>管理漫画页、封面、角色/场景设定、剧本、分镜、音频等全类型漫剧资产。</p>"
        )

    # ==================================================================
    # 系统交互
    # ==================================================================
    def _open_file(self, path: str):
        if not path or not os.path.exists(path):
            QMessageBox.warning(self, "提示", "文件不存在（可能已被移动或删除）。")
            return
        self._reveal(path, in_dir=False)

    def _open_folder(self, path: str):
        if path and os.path.isdir(path):
            self._reveal(path, in_dir=True)

    @staticmethod
    def _reveal(path: str, in_dir: bool):
        p = Path(path)
        if sys.platform.startswith("darwin"):
            subprocess.Popen(["open", "-R", str(p)] if in_dir else ["open", str(p)])
        elif sys.platform.startswith("win"):
            import ctypes
            if in_dir:
                ctypes.windll.shell32.ShellExecuteW(None, "explore", str(p.parent), None, None, 1, None)
            else:
                os.startfile(p)  # type: ignore[attr-defined]
        else:
            xdg = "xdg-open"
            subprocess.Popen([xdg, str(p.parent if in_dir else p)])

    # ==================================================================
    # 拖拽导入
    # ==================================================================
    def dragEnterEvent(self, e: QDragEnterEvent):  # noqa: N802
        if e.mimeData().hasUrls():
            e.acceptProposedAction()

    def dropEvent(self, e: QDropEvent):  # noqa: N802
        urls = e.mimeData().urls()
        if not urls:
            return
        paths = [u.toLocalFile() for u in urls]
        dirs = [p for p in paths if os.path.isdir(p)]
        files = [p for p in paths if os.path.isfile(p)]
        if dirs:
            # 多目录 → 逐个导入为独立系列
            for d in dirs:
                from ..library import import_folder
                rep = import_folder(self.db, d, series_name=Path(d).name)
                self.sb_left.setText(f"拖拽导入：{rep.imported} 项")
            self.refresh_all()
        elif files:
            for f in files:
                from ..library import import_file
                import_file(self.db, f)
            self.refresh_all()
            self.sb_left.setText(f"拖拽导入 {len(files)} 个文件")

    def keyPressEvent(self, e):  # noqa: N802
        key = e.key()
        if key == Qt.Key.Key_Delete and self._selected_id:
            self.delete_asset(self._selected_id)
        elif key in (Qt.Key.Key_Left, Qt.Key.Key_Right) and self._selected_id:
            self._navigate(e)
        elif key == Qt.Key.Key_F and self._selected_id:
            self._open_image_preview(self._selected_id)
        elif key == Qt.Key.Key_F1:
            self.about()
        else:
            super().keyPressEvent(e)

    def _navigate(self, e):
        """在同一页内按页序上下翻页。"""
        ids = [a.id for a in self._page_assets]
        if not self._selected_id in ids:
            return
        i = ids.index(self._selected_id)
        delta = 1 if e.key() == Qt.Key.Key_Right else -1
        ni = i + delta
        if 0 <= ni < len(ids):
            self._on_asset_selected(ids[ni])

    def closeEvent(self, e):  # noqa: N802
        try:
            self.db.close()
        finally:
            e.accept()
