#  Copyright (c) 2025  Jeffrey Hsu - JITToolBox
#  #
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#  #
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  #
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.

from functools import wraps
from typing import Literal, Callable

from PySide6.QtCore import Qt, Signal, QThread, QEvent
from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QFileDialog, QButtonGroup, QWidget, QApplication, QStackedWidget
from qfluentwidgets import GroupHeaderCardWidget, PushButton, IconWidget, BodyLabel, PrimaryPushButton, FluentIcon, \
    LineEdit, RadioButton, HyperlinkButton, FlyoutViewBase, TeachingTip, TeachingTipTailPosition, SegmentedWidget, \
    SimpleCardWidget, DisplayLabel

from module.worker import DTGWorker
from ui import MAIN_THEME_COLOR
from ui.components.infobar import ProgressInfoBar
from ui.components.widget import Widget, MyGroupHeaderCardWidget, NotImplementedWidget
from ui.pyui.sub.defense import ODModeExportSettings, ODModeSettings
from utils.function import open_template


class ChooseTemplateView(FlyoutViewBase):
    closed = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.vBoxLayout = QVBoxLayout(self)
        QApplication.instance().installEventFilter(self)

    def paintEvent(self, e):
        ...

    def eventFilter(self, watched, event):
        if event.type() == QEvent.MouseButtonPress:
            if not self.rect().contains(self.mapFromGlobal(event.globalPosition().toPoint())):
                self.closed.emit()
        return super().eventFilter(watched, event)

    def addTemplate(self, content: str, cb: Callable[[], None]):
        label = HyperlinkButton("", content)
        self.vBoxLayout.addWidget(label)
        label.clicked.connect(cb)
        label.clicked.connect(self.closed.emit)


class InitSettingCard(MyGroupHeaderCardWidget):
    chooseSignal = Signal(str, str)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.setTitle("输入选项")
        self.setBorderRadius(8)

        self.sBtnHBoxLayout = QHBoxLayout(self)
        self.qBtnHBoxLayout = QHBoxLayout(self)
        self.sTemplateButton = HyperlinkButton("", "模板下载")
        self.chooseStudentButton = PushButton("打开")
        self.qTemplateButton = HyperlinkButton("", "模板下载")
        self.chooseQuestionButton = PushButton("打开")

        self.chooseStudentButton.setFixedWidth(120)
        self.chooseQuestionButton.setFixedWidth(120)
        self.sBtnHBoxLayout.addWidget(self.sTemplateButton)
        self.sBtnHBoxLayout.addWidget(self.chooseStudentButton)
        self.qBtnHBoxLayout.addWidget(self.qTemplateButton)
        self.qBtnHBoxLayout.addWidget(self.chooseQuestionButton)

        self.stuGroup = self.addGroup(FluentIcon.DOCUMENT, "学生名单", "选择学生名单文件", self.sBtnHBoxLayout)
        self.QueGroup = self.addGroup(FluentIcon.DOCUMENT, "题库", "选择题库文件", self.qBtnHBoxLayout)

        self.chooseStudentButton.clicked.connect(
            lambda: self.choose_file(self.stuGroup.setContent, "已选择文件：", lambda x: self.chooseSignal.emit('s', x)))
        self.chooseQuestionButton.clicked.connect(
            lambda: self.choose_file(self.QueGroup.setContent, "已选择文件：", lambda x: self.chooseSignal.emit('q', x)))
        self.qTemplateButton.clicked.connect(lambda: open_template('template-defense-paper-questions.xlsm', self))
        self.sTemplateButton.clicked.connect(self.show_template_list_view)

    def choose_file(
            self,
            set_value: Callable[[str], None] = None,
            prefix: str = "",
            cb: Callable[[str], None] = None
    ) -> None:
        file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "Excel 文件 (*.xlsx *.xlsm);")
        if file_path and set_value:
            set_value(prefix + file_path)
            if cb:
                cb(file_path)

    def show_template_list_view(self):
        view = ChooseTemplateView(self)
        view.addTemplate("普通模板", lambda: open_template("template-defense-paper-student-1.xlsm", self))
        view.addTemplate("达成度模板", lambda: open_template("template-defense-paper-student-2.xlsm", self))
        w = TeachingTip.make(
            target=self.sTemplateButton,
            view=view,
            tailPosition=TeachingTipTailPosition.TOP,
            duration=-1,
            parent=self
        )
        view.closed.connect(w.close)


class ExportSettingsCard(GroupHeaderCardWidget):
    startSignal = Signal()
    updateSignal = Signal(str, str)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.setTitle("输出选项")
        self.setBorderRadius(8)

        self.chooseExportDirectoryButton = PushButton("选择")
        self.exportFileNameLineEdit = LineEdit()
        self.startButton = PrimaryPushButton(FluentIcon.PLAY_SOLID, "开始")
        self.pdfRadio = RadioButton("PDF")
        self.wordRadio = RadioButton("Word")
        self.radioWidget = QWidget(self)
        self.radioHbox = QHBoxLayout(self.radioWidget)
        self.radioGroup = QButtonGroup(self.radioWidget)

        self.radioGroup.addButton(self.pdfRadio)
        self.radioGroup.addButton(self.wordRadio)
        self.radioHbox.addWidget(self.pdfRadio)
        self.radioHbox.addWidget(self.wordRadio)
        self.wordRadio.setChecked(True)
        self.hintIcon = IconWidget(FluentIcon.INFO.icon(color=MAIN_THEME_COLOR))
        self.hintLabel = BodyLabel("点击开始按钮以开始生成 👉")
        self.chooseExportDirectoryButton.setFixedWidth(120)
        self.startButton.setFixedWidth(120)
        self.exportFileNameLineEdit.setFixedWidth(360)
        self.exportFileNameLineEdit.setPlaceholderText("输入导出文件名，例如：21工程管理（1）答辩题目")
        self.bottomLayout = QHBoxLayout()
        self.startButton.setEnabled(False)

        # 设置底部工具栏布局
        self.hintIcon.setFixedSize(16, 16)
        self.bottomLayout.setSpacing(10)
        self.bottomLayout.setContentsMargins(24, 15, 24, 20)
        self.bottomLayout.addWidget(self.hintIcon, 0, Qt.AlignLeft)
        self.bottomLayout.addWidget(self.hintLabel, 0, Qt.AlignLeft)
        self.bottomLayout.addStretch(1)
        self.bottomLayout.addWidget(self.startButton, 0, Qt.AlignRight)
        self.bottomLayout.setAlignment(Qt.AlignVCenter)

        self.dirGroup = self.addGroup(FluentIcon.FOLDER, "导出目录", "选择导出文件的目录",
                                      self.chooseExportDirectoryButton)
        self.fnGroup = self.addGroup(FluentIcon.DOCUMENT, "导出文件名", "输入导出文件的名称",
                                     self.exportFileNameLineEdit)
        self.exportFormatGroup = self.addGroup(FluentIcon.DOCUMENT, "导出文件格式", "选择导出文件的格式",
                                               self.radioWidget)
        self.exportFormatGroup.setSeparatorVisible(True)

        self.vBoxLayout.addLayout(self.bottomLayout)

        # ==============
        self.chooseExportDirectoryButton.clicked.connect(self.choose_dir)
        self.startButton.clicked.connect(self.startSignal.emit)
        self.exportFileNameLineEdit.textChanged.connect(lambda x: self.updateSignal.emit('n', x))

    def choose_dir(self) -> None:
        dir_path = QFileDialog.getExistingDirectory(self, "选择文件夹", "")
        if dir_path:
            self.dirGroup.setContent(f"当前保存的文件目录：{dir_path}")
            self.updateSignal.emit('d', dir_path)

    def update_export_setting_by_signal(self, path: str) -> None:
        f_dir = path[:path.rfind('/')]
        f_name = path[path.rfind('/') + 1:path.rfind('.')] + '-答辩题目'
        self.dirGroup.setContent(f"当前保存的文件目录：{f_dir}")
        self.updateSignal.emit('d', f_dir)
        self.exportFileNameLineEdit.setText(f_name)
        self.updateSignal.emit('n', f_name)


class DPMode(QWidget):
    errorSignal = Signal(str, str)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.initCard = InitSettingCard(self)
        self.exportCard = ExportSettingsCard(self)
        self.vbox = QVBoxLayout(self)
        self.vbox.setContentsMargins(0, 0, 0, 0)

        self.vbox.addWidget(self.initCard)
        self.vbox.addWidget(self.exportCard)
        self.vbox.addStretch(1)

        self.pib = ProgressInfoBar(parent=self, isClosable=False, duration=-1)
        self.pib.hide()

        self.thread = None
        self.worker = None

        self.successFlag = True

        # ===================================

        self.input_student_filepath = None
        self.input_question_filepath = None
        self.output_filepath = None
        self.output_filename = None

        # ====================================
        self.initCard.chooseSignal.connect(self.input_signal_receive)
        self.exportCard.updateSignal.connect(self.export_update_signal_receive)
        self.exportCard.startSignal.connect(self.start_generate)

    def show_info_bar(self):
        self.pib.show()
        self.pib.set_title('请稍后')

    def set_pb_value(self, value: int) -> None:
        if value == -1:
            self.successFlag = False
        self.pib.set_progress(value)

    def set_pb_msg(self, value: str) -> None:
        self.pib.set_title(value)

    def enable_start_check(func: Callable):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            result = func(self, *args, **kwargs)

            fields = [
                self.input_student_filepath,
                self.input_question_filepath,
                self.output_filepath,
                self.output_filename
            ]
            if all(fields):
                self.exportCard.startButton.setEnabled(True)
            else:
                self.exportCard.startButton.setEnabled(False)
            return result

        return wrapper

    @enable_start_check
    def set_value(self, key: str, value: str) -> None:
        if key == "input_student_filepath":
            self.input_student_filepath = value
        elif key == "input_question_filepath":
            self.input_question_filepath = value
        elif key == "output_filepath":
            self.output_filepath = value
        elif key == "output_filename":
            self.output_filename = value

    def input_signal_receive(self, s_type: Literal['s', 'q'], value: str):
        if s_type == "s":
            self.set_value("input_student_filepath", value)
            self.exportCard.update_export_setting_by_signal(value)
        elif s_type == "q":
            self.set_value("input_question_filepath", value)

    def export_update_signal_receive(self, s_type: Literal['d', 'n'], value: str):
        if s_type == "d":
            self.set_value("output_filepath", value)
        elif s_type == "n":
            self.set_value("output_filename", value)

    def start_generate(self):
        self.thread = QThread()
        self.worker = DTGWorker(
            self.input_student_filepath,
            self.input_question_filepath,
            self.output_filepath,
            self.output_filename,
            self.exportCard.radioGroup.checkedButton().text().lower()
        )
        self.worker.moveToThread(self.thread)

        self.show_info_bar()
        self.successFlag = True
        self.exportCard.startButton.setEnabled(False)

        # 线程启动与信号连接
        self.thread.started.connect(self.worker.run)
        self.worker.progress[int].connect(self.set_pb_value)
        self.worker.progress[str].connect(self.set_pb_msg)
        self.worker.error.connect(self.show_error)

        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.finished.connect(self.clear_thread_worker_refs)
        self.thread.finished.connect(self.after_generate)

        # 启动线程
        self.thread.start()

    def clear_thread_worker_refs(self):
        self.thread = None
        self.worker = None

    def after_generate(self):
        self.exportCard.startButton.setEnabled(True)
        if self.successFlag:
            self.pib.show_success(content="正在打开文件...")
        else:
            self.pib.show_error()

    def show_error(self, title: str, content: str):
        self.errorSignal.emit(title, content)


class DOMode(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.vbox = QVBoxLayout(self)
        self.vbox.setContentsMargins(0, 0, 0, 0)

        self.odSettings = ODModeSettings(self)
        self.odExportSettings = ODModeExportSettings(self)

        self.vbox.addWidget(self.odSettings)
        self.vbox.addWidget(self.odExportSettings)
        self.vbox.addStretch(1)


class DefenseWidget(Widget):
    errorSignal = Signal(str, str)

    def __init__(self, key: str, parent=None):
        super().__init__(key, parent)

        self.vbox = QVBoxLayout(self)
        self.stack = QStackedWidget(self)
        self.menu = SegmentedWidget(self)
        self.dpMode = DPMode(self)
        self.doMode = NotImplementedWidget(self)

        self.addSubInterface(self.dpMode, 'DPMode', '书面答辩')
        self.addSubInterface(self.doMode, 'DOMode', '口头答辩')

        self.menu.setCurrentItem('DPMode')
        self.vbox.addWidget(self.menu)
        self.vbox.addWidget(self.stack)
        self.vbox.addStretch(1)

    def addSubInterface(self, widget: QWidget, objectName: str, text: str):
        widget.setObjectName(objectName)
        self.stack.addWidget(widget)

        # 使用全局唯一的 objectName 作为路由键
        self.menu.addItem(
            routeKey=objectName,
            text=text,
            onClick=lambda: self.stack.setCurrentWidget(widget)
        )

    def onCurrentIndexChanged(self, index):
        widget = self.stack.widget(index)
        self.menu.setCurrentItem(widget.objectName())
