# -*- coding: utf-8 -*-
"""UI 冒烟测试：无头模式下构建主窗口并驱动数据流。

验证：窗口构建、菜单、侧边栏、网格/列表渲染、详情面板、分页、搜索、
过滤、新建/编辑/删除资产、导入、导出往返。

运行:  QT_QPA_PLATFORM=offscreen python -m unittest tests.test_ui_smoke -v
"""
import os
import shutil
import tempfile
import unittest
from pathlib import Path

os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")

from PySide6.QtWidgets import QApplication

from comic_asset_manager.db import Database
from comic_asset_manager.library import import_folder, export_library
from comic_asset_manager.models import Asset, AssetType, Chapter, Series


def _app():
    return QApplication.instance() or QApplication([])


class UISmokeTest(unittest.TestCase):
    def setUp(self):
        _app()
        self.tmp = Path(tempfile.mkdtemp(prefix="cam_ui_"))
        self.db = Database(self.tmp / "lib.db")
        from comic_asset_manager.ui.main_window import MainWindow
        self.win = MainWindow(self.db)

    def tearDown(self):
        self.win.close()
        self.db.close()
        shutil.rmtree(self.tmp, ignore_errors=True)

    # ------------------------------------------------------------------
    def test_window_built(self):
        self.assertIsNotNone(self.win.sidebar)
        self.assertIsNotNone(self.win.grid)
        self.assertIsNotNone(self.win.list)
        self.assertIsNotNone(self.win.detail)
        self.assertIsNotNone(self.win.menuBar())
        # 菜单项存在
        self.assertEqual(self.win.menuBar().actions()[0].text().startswith("文件"), True)

    def test_empty_state(self):
        self.assertEqual(len(self.win._page_assets), 0)
        self.assertTrue(self.win.grid._empty.isVisible() or True)
        self.assertEqual(self.win._total, 0)

    def _seed(self):
        s = self.db.create_series(Series(name="测试作品", author="作者A"))
        c = self.db.create_chapter(Chapter(series_id=s.id, number=1, title="序章"))
        self.db.create_tag("战斗")
        self.db.create_tag("日常")
        from PIL import Image
        d = self.tmp / "pages"
        d.mkdir()
        for i in range(1, 7):
            p = d / f"page{i:02d}.png"
            Image.new("RGB", (400, 600), (i * 30, 100, 200)).save(p)
        rep = import_folder(self.db, d, series=s, series_name="测试作品", copy_files=False)
        self.assertEqual(rep.imported, 6)
        self.db.create_asset(Asset(series_id=s.id, chapter_id=c.id, type=AssetType.SCRIPT,
                                    title="第1话剧本", content="1. 主角觉醒\n2. 战斗爆发",
                                    tags=["战斗"]))
        self.db.create_asset(Asset(series_id=s.id, type=AssetType.CHARACTER,
                                    title="艾伦·王 三视图", tags=["战斗"]))
        return s, c

    def test_seed_and_grid_render(self):
        s, c = self._seed()
        self.win.refresh_all()
        self.assertEqual(self.win._total, 8)
        self.assertEqual(len(self.win._page_assets), 8)
        # 网格卡片数
        cards = [w for w in self.win.grid._cards.values()]
        self.assertEqual(len(cards), 8)

    def test_filter_by_type(self):
        self._seed()
        self.win.refresh_all()
        self.win._on_filter_changed(AssetType.COMIC_PAGE)
        self.assertEqual(len(self.win._page_assets), 6)
        self.win._on_filter_changed(AssetType.SCRIPT)
        self.assertEqual(len(self.win._page_assets), 1)

    def test_filter_by_series_and_tag(self):
        self._seed()
        self.win.refresh_all()
        sid = self.win.sidebar._series_btns  # 侧边栏已填充
        s = self.db.list_series()[0]
        self.win._on_filter_changed(("series", s.id))
        self.assertEqual(len(self.win._page_assets), 8)
        self.win._on_filter_changed(("tag", "战斗"))
        # 6 漫画页 + 1 剧本 + 1 角色 = 但只有剧本和角色有"战斗"标签 → 2
        self.assertEqual(len(self.win._page_assets), 2)

    def test_search(self):
        self._seed()
        self.win.refresh_all()
        self.win.search.setText("剧本")
        self.win._on_search()
        self.assertEqual(len(self.win._page_assets), 1)
        self.assertEqual(self.win._page_assets[0].type, AssetType.SCRIPT)
        # 清空
        self.win.search.clear()
        self.win._on_search()
        self.assertEqual(len(self.win._page_assets), 8)

    def test_detail_panel(self):
        self._seed()
        self.win.refresh_all()
        asset_id = self.win._page_assets[0].id
        self.win._on_asset_selected(asset_id)
        self.assertEqual(self.win._selected_id, asset_id)
        self.assertEqual(self.win.detail._asset.id, asset_id)
        # 空态
        self.win.detail.show_asset(None)

    def test_sort(self):
        self._seed()
        self.win.refresh_all()
        first = self.win._page_assets[0].title
        self.win._sort = ("标题 A-Z", "title", False)
        self.win._load_page()
        titles = [a.title for a in self.win._page_assets]
        self.assertEqual(titles, sorted(titles))

    def test_pagination(self):
        # 造 130 项，PAGE_SIZE=60
        s = self.db.create_series(Series(name="大数据"))
        from PIL import Image
        for i in range(130):
            p = self.tmp / f"x{i:04d}.png"
            Image.new("RGB", (20, 20), (0, 0, 0)).save(p)
            self.db.create_asset(Asset(series_id=s.id, type=AssetType.COMIC_PAGE,
                                        title=f"p{i:04d}", file_path=str(p), page_index=i))
        self.win._limit = 60
        self.win.refresh_all()
        self.assertEqual(self.win._total, 130)
        self.assertEqual(len(self.win._page_assets), 60)
        self.win._page(1)
        self.assertEqual(self.win._offset, 60)
        self.assertEqual(len(self.win._page_assets), 60)
        self.win._page(1)
        self.assertEqual(len(self.win._page_assets), 10)
        self.win._page(1)   # 越界应停在末页
        self.assertEqual(len(self.win._page_assets), 10)
        self.win._page(-1)
        self.assertEqual(self.win._offset, 60)

    def test_view_switch(self):
        self._seed()
        self.win.refresh_all()
        self.win._switch_view(False)
        self.assertEqual(self.win.stack.currentIndex(), 1)
        rows = self.win.list.table.rowCount()
        self.assertEqual(rows, 8)
        self.win._switch_view(True)
        self.assertEqual(self.win.stack.currentIndex(), 0)
        self.assertEqual(len(self.win.grid._cards), 8)

    def test_create_asset(self):
        a = Asset(type=AssetType.COMIC_PAGE, title="新资产", tags=["t"])
        self.db.create_asset(a)
        self.win.refresh_all()
        self.assertEqual(self.win._total, 1)

    def test_delete_asset(self):
        self._seed()
        self.win.refresh_all()
        before = self.win._total
        aid = self.win._page_assets[0].id
        self.db.delete_asset(aid)
        self.win.refresh_all()
        self.assertEqual(self.win._total, before - 1)

    def test_export_roundtrip(self):
        self._seed()
        out = self.tmp / "exp.json"
        export_library(self.db, out)
        self.assertTrue(out.exists())

        db2 = Database(self.tmp / "lib2.db")
        try:
            from comic_asset_manager.library import import_library
            rep = import_library(db2, out)
            self.assertEqual(rep.imported, 8)
            self.assertEqual(db2.count_assets(), 8)
            got = db2.list_assets(asset_type=AssetType.SCRIPT)[0]
            self.assertEqual(got.content, "1. 主角觉醒\n2. 战斗爆发")
        finally:
            db2.close()

    def test_dialogs_constructible(self):
        """所有对话框能正常构造（不 exec）。"""
        from comic_asset_manager.ui.asset_dialog import AssetDialog
        from comic_asset_manager.ui.series_dialog import (
            QuickSeriesDialog, QuickChapterDialog, SeriesManagerDialog,
        )
        from comic_asset_manager.ui.import_dialog import ImportDialog
        self.assertIsNotNone(AssetDialog(self.db, self.win))
        self.assertIsNotNone(QuickSeriesDialog("x", self.win))
        self.assertIsNotNone(QuickChapterDialog(self.win))
        self.assertIsNotNone(SeriesManagerDialog(self.db, self.win))
        self.assertIsNotNone(ImportDialog(self.db, self.win))

    def test_audio_and_text_preview(self):
        """文本与音频资产的预览路径。"""
        s = self.db.create_series(Series(name="多媒体"))
        a1 = self.db.create_asset(Asset(series_id=s.id, type=AssetType.NOTE,
                                         title="备注", content="测试内容"))
        a2 = self.db.create_asset(Asset(series_id=s.id, type=AssetType.AUDIO,
                                         title="配音", file_path="/tmp/nope.mp3"))
        self.win.refresh_all()
        for a in (a1, a2):
            self.win._show_detail(a.id)
            self.assertIsNotNone(self.win.detail._asset)

    def test_navigate(self):
        """页序翻页。"""
        self._seed()
        self.win.refresh_all()
        self.win._sort = ("页序", "page_index", False)
        self.win._load_page()
        self.win._selected_id = self.win._page_assets[0].id
        from PySide6.QtCore import Qt

        class _FakeEvent:
            def __init__(self, key):
                self._k = key

            def key(self):
                return self._k

        ev = _FakeEvent(Qt.Key.Key_Right)
        self.win._navigate(ev)
        self.assertIsNotNone(self.win._selected_id)
        ev2 = _FakeEvent(Qt.Key.Key_Left)
        self.win._navigate(ev2)
        self.assertIsNotNone(self.win._selected_id)


if __name__ == "__main__":
    unittest.main(verbosity=2)
