#  Copyright (c) 2025-2026  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/>.

import os
from functools import wraps
from typing import Callable, Literal

from PySide6.QtCore import Qt, Signal, QThread
from PySide6.QtWidgets import QVBoxLayout, QFileDialog, QHBoxLayout
from qfluentwidgets import GroupHeaderCardWidget, FluentIcon, PushButton, LineEdit, IconWidget, BodyLabel, \
    PrimaryPushButton, SwitchButton, HyperlinkButton, InfoBar, InfoBarPosition

from module import LOGLEVEL
from module.worker import ARGWorker
from ui import MAIN_THEME_COLOR
from ui.components.infobar import ProgressInfoBar
from ui.components.widget import Widget, MyGroupHeaderCardWidget
from utils.function import open_template


class InputSettingCard(MyGroupHeaderCardWidget):
    chooseSignal = Signal(str)

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

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

        self.openTemplateButton = HyperlinkButton("", "模板下载")
        self.chooseFileButton = PushButton("打开")

        self.chooseFileButton.setFixedWidth(120)
        self.btnHBoxLayout.addWidget(self.openTemplateButton)
        self.btnHBoxLayout.addWidget(self.chooseFileButton)

        self.inputGroup = self.addGroup(FluentIcon.DOCUMENT, "目标文件", "选择达成度计算表", self.btnHBoxLayout)

        # ============================
        self.chooseFileButton.clicked.connect(self.choose_file)
        self.openTemplateButton.clicked.connect(lambda: open_template('template-achievement-file.xlsm', self))

    def choose_file(self):
        file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "Excel 文件 (*.xlsm);")
        if file_path:
            self.inputGroup.setContent("已选择文件：" + file_path)
            self.chooseSignal.emit(file_path)


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

    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.autoOpenSwitch = SwitchButton()
        self.disableCompatibilityCheckSwitch = SwitchButton()

        self.autoOpenSwitch.setChecked(True)
        self.bottomLayout = QHBoxLayout()
        self.hintIcon = IconWidget(FluentIcon.INFO.icon(color=MAIN_THEME_COLOR))
        self.hintLabel = BodyLabel("点击开始按钮以开始生成 👉")
        self.startButton.setEnabled(False)

        # 设置底部工具栏布局
        self.hintIcon.setFixedSize(16, 16)
        self.hintIcon.autoFillBackground()
        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.chooseExportDirectoryButton.setFixedWidth(120)
        self.exportFileNameLineEdit.setPlaceholderText("请输入文件名")
        self.startButton.setFixedWidth(120)
        self.exportFileNameLineEdit.setFixedWidth(360)
        self.exportFileNameLineEdit.setPlaceholderText("输入导出文件名，例如：21工程管理（1）达成度报告")

        self.dirGroup = self.addGroup(FluentIcon.FOLDER, "导出目录", "选择导出文件的目录",
                                      self.chooseExportDirectoryButton)
        self.fnGroup = self.addGroup(FluentIcon.DOCUMENT, "导出文件名", "输入导出文件名", self.exportFileNameLineEdit)
        self.aoGroup = self.addGroup(FluentIcon.VIEW, "自动打开", "生成完成后自动打开文件", self.autoOpenSwitch)
        self.ccGroup = self.addGroup(FluentIcon.DEVELOPER_TOOLS, "关闭兼容性检查",
                                     "⚠ 注意：该功能为实验性内容，仅当你明确了解其影响并知道自己在做什么时，才建议关闭兼容性检查！",
                                     self.disableCompatibilityCheckSwitch)
        self.ccGroup.setSeparatorVisible(True)
        self.vBoxLayout.addLayout(self.bottomLayout)

        # =============================
        self.chooseExportDirectoryButton.clicked.connect(self.choose_dir)
        self.startButton.clicked.connect(self.startSignal)

    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_path_and_filename_by_input_path(self, path: str):
        dir_path = path[:path.rfind("/")]
        self.dirGroup.setContent(f"已选择目录：{dir_path}")
        self.updateSignal.emit('d', dir_path)

        file_name = path[path.rfind("/") + 1:path.rfind(".")] + "-达成度报告"
        self.exportFileNameLineEdit.setText(file_name)
        self.updateSignal.emit('f', file_name)


class AchievementWidget(Widget):
    error = Signal(str, str)

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

        self.inputGroup = InputSettingCard(self)
        self.outputGroup = OutputSettingCard(self)
        self.vbox = QVBoxLayout(self)

        self.vbox.addWidget(self.inputGroup)
        self.vbox.addWidget(self.outputGroup)
        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_file_path = ""
        self.output_file_path = ""
        self.output_file_name = ""

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

        self.inputGroup.chooseSignal.connect(self.input_signal_receive)
        self.outputGroup.updateSignal.connect(self.export_signal_receive)
        self.outputGroup.startSignal.connect(self.start_generate)

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

            fields = [
                self.input_file_path,
                self.output_file_path,
                self.output_file_name,
            ]
            if all(fields):
                self.outputGroup.startButton.setEnabled(True)
            else:
                self.outputGroup.startButton.setEnabled(False)
            return result

        return wrapper

    @enable_start_check
    def set_value(
            self,
            key: Literal['input_file_path', 'output_file_path', 'output_file_name'],
            value: str
    ) -> None:
        setattr(self, key, value)

    def input_signal_receive(self, file_path: str) -> None:
        self.set_value('input_file_path', file_path)
        self.outputGroup.update_path_and_filename_by_input_path(file_path)

    def export_signal_receive(self, s_type: Literal['d', 'f'], value: str) -> None:
        if s_type == 'd':
            self.set_value('output_file_path', value)
        elif s_type == 'f':
            self.set_value('output_file_name', value)

    def start_generate(self):
        self.thread = QThread()
        self.worker = ARGWorker(
            self.input_file_path,
            self.output_file_path,
            self.output_file_name,
            disable_cc=self.outputGroup.disableCompatibilityCheckSwitch.isChecked()
        )
        self.worker.moveToThread(self.thread)

        self.outputGroup.startButton.setEnabled(False)

        self.thread.started.connect(self.worker.run)

        self.show_info_bar()
        self.successFlag = True

        self.worker.info.connect(self.show_info)
        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):
        if self.outputGroup.autoOpenSwitch.isChecked() and self.successFlag:
            try:
                os.startfile(self.output_file_path + "/" + self.output_file_name + ".docx")
            except Exception as e:
                self.show_error("?? 不好出错了", str(e))
        if self.successFlag:
            self.pib.show_success(
                content="正在打开文件" if self.outputGroup.autoOpenSwitch.isChecked() else "文件已保存")
        else:
            self.pib.show_error()
        self.outputGroup.startButton.setEnabled(True)

    def show_error(self, title: str, content: str):
        self.successFlag = False
        self.error.emit(title, content)

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

    def show_info(self, content: str, level: str):
        if level == LOGLEVEL.INFO:
            self.pib.set_title(content)
        elif level == LOGLEVEL.WARNING:
            InfoBar.warning(
                title='提示',
                content=content,
                orient=Qt.Horizontal,
                isClosable=True,
                position=InfoBarPosition.TOP_RIGHT,
                duration=5000,
                parent=self
            )
        elif level == LOGLEVEL.ERROR:
            InfoBar.error(
                title='错误',
                content=content,
                orient=Qt.Horizontal,
                isClosable=True,
                position=InfoBarPosition.TOP_RIGHT,
                duration=-1,
                parent=self
            )
