"""
调取各类对话框，实现与影刀机器人的交互。如消息对话框、自定义对话框
"""
from tkinter import messagebox
import tkinter as tk
from tkinter import filedialog, ttk
import threading
import time
import datetime

__all__ = [
    "show_alert",
    "show_confirm",
    "show_custom_dialog",
    "show_message_box",
    "show_workbook_dialog",
    "show_notification"
]


class CustomDialog:
    def __init__(self, root, settings):
        self.root = root
        self.settings = settings
        self.result = {}
        self.dialog = None
        self.timeout = settings.get("timeout", -1)
        self.default_btn = settings.get("default_btn", None)
        self.timed_out = False

    def show(self):
        self.dialog = tk.Toplevel(self.root)
        self.dialog.title(self.settings.get("dialog_title", ""))
        self.dialog.grab_set()
        self.controls = []

        frame = tk.Frame(self.dialog)
        frame.pack(padx=10, pady=10)

        editors = self.settings.get("settings", {}).get("editors", [])
        for i, editor in enumerate(editors):
            control = self._create_control(frame, editor)
            self.controls.append((editor, control))

        btn = tk.Button(self.dialog, text=self.default_btn or "确定", command=self._on_confirm)
        btn.pack(pady=5)

        if self.timeout >= 0:
            threading.Thread(target=self._timeout_watchdog, daemon=True).start()

        self.root.wait_window(self.dialog)
        return self.result

    def _on_confirm(self):
        for editor, widget in self.controls:
            name = editor.get("label", f"field_{id(widget)}")
            etype = editor.get("type")
            if etype in ["TextBox", "Password", "TextArea"]:
                self.result[name] = widget.get("1.0", "end-1c") if etype == "TextArea" else widget.get()
            elif etype == "Number":
                try:
                    self.result[name] = float(widget.get())
                except ValueError:
                    self.result[name] = None
            elif etype == "CheckBox":
                self.result[name] = bool(widget.var.get())
            elif etype == "Select":
                self.result[name] = widget.get()
            elif etype == "Date":
                self.result[name] = widget.get()
            elif etype == "File":
                self.result[name] = widget.get()
            else:
                self.result[name] = None
        self.dialog.destroy()

    def _timeout_watchdog(self):
        time.sleep(self.timeout)
        if self.dialog.winfo_exists():
            self.timed_out = True
            self._on_confirm()

    def _create_control(self, parent, editor):
        etype = editor.get("type")
        label = editor.get("label", "")
        value = editor.get("value")
        null_text = editor.get("nullText", "")

        if label:
            tk.Label(parent, text=label).pack(anchor="w")

        if etype in ["TextBox", "Password"]:
            entry = tk.Entry(parent, show="*" if etype == "Password" else None)
            if value:
                entry.insert(0, str(value))
            elif null_text:
                entry.insert(0, null_text)
            entry.pack(fill="x")
            return entry
        elif etype == "TextArea":
            text = tk.Text(parent, height=5)
            if value:
                text.insert("1.0", value)
            elif null_text:
                text.insert("1.0", null_text)
            text.pack(fill="x")
            return text
        elif etype == "Number":
            entry = tk.Entry(parent)
            if value:
                entry.insert(0, str(value))
            entry.pack(fill="x")
            return entry
        elif etype == "CheckBox":
            var = tk.IntVar(value=1 if value else 0)
            cb = tk.Checkbutton(parent, text=label, variable=var)
            cb.var = var
            cb.pack(anchor="w")
            return cb
        elif etype == "Select":
            options = [opt["display"] for opt in editor.get("options", [])]
            values = {opt["display"]: opt["value"] for opt in editor.get("options", [])}
            combo = ttk.Combobox(parent, values=options)
            if value:
                for k, v in values.items():
                    if v == value:
                        combo.set(k)
            combo.pack(fill="x")
            return combo
        elif etype == "Date":
            entry = tk.Entry(parent)
            entry.insert(0, value or datetime.datetime.now().strftime("%Y-%m-%d"))
            entry.pack(fill="x")
            return entry
        elif etype == "File":
            def select_file():
                kind = editor.get("kind", "OpenFile")
                if kind == "OpenFile":
                    path = filedialog.askopenfilename()
                elif kind == "SaveFile":
                    path = filedialog.asksaveasfilename()
                elif kind == "SelectFolder":
                    path = filedialog.askdirectory()
                else:
                    path = ""
                entry.delete(0, tk.END)
                entry.insert(0, path)

            entry = tk.Entry(parent)
            if null_text:
                entry.insert(0, null_text)
            entry.pack(fill="x")
            btn = tk.Button(parent, text="浏览", command=select_file)
            btn.pack(anchor="e")
            return entry
        else:
            return tk.Label(parent, text=f"未知控件类型: {etype}")


#
# print(messagebox.askokcancel('请确认', 'hello world'))
def show_alert(message, title='提示'):
    """
    打开消息对话框
    :param message: 需要在消息对话框中展示的内容
    :param title: 消息对话框标题，若不填，默认为提示
    :return:
    """
    messagebox.showinfo(title, message)


def show_confirm(message, title='请确认'):
    """

    :param message:需要在确认对话框中展示的内容
    :param title:确认对话框标题，默认标题内容为“请确认”
    :return:bool：返回确认对话框选择结果，选择确认返回True，否则返回False
    """
    return messagebox.askokcancel(title, message)


def show_custom_dialog(settings):
    """
    :param settings: 自定义对话框配置json串, json主要包含对话框标题和自定义控件配置, json结构如下:
    {
            "dialog_title": "标题",
            "default_btn": "确定",
            "timeout":10,
            "settings":
            {
                "editors":[{"type":"CheckBox"},{"label":"复选框"},...]
            }
        }
    :return:
    """
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口
    dialog = CustomDialog(root, settings)
    return dialog.show()


def show_message_box(title, message, button='ok', timeout=-1, default_button=None):
    """
    打开消息对话框
    :param title:消息对话框标题
    :param message:消息对话框要展示的信息
    :param button:消息对话框中的按钮。共有'ok'（确定）、'okcancel（确定/取消）、'yesno'（是/否）、'yesnocancel'（是/否/取消）四类组合方式
    :param timeout:对话框等待点击超时时间，若超过该时间对话框仍没点击则按默认按钮进行自动点击。若不填该参数，则默认一直等待。当timeout为0时，则对话框不等待
    :param default_button:超时时默认点击的按钮, 仅在超时时间大于0时起效
    :return:str：返回消息对话框交互结果，如：ok 或 cancel 等
    """
    result = {"value": None}

    def on_click(value):
        result["value"] = value
        win.destroy()

    def on_timeout():
        if timeout > 0 and result["value"] is None and default_button:
            result["value"] = default_button
            win.destroy()

    # 创建对话框窗口
    win = tk.Tk()
    win.withdraw()
    win.title(title)
    win.geometry("400x160")
    win.minsize(300, 120)
    win.configure(bg="#f8f8f8")
    win.after(0, win.deiconify)  # 显示窗口
    win.attributes("-topmost", True)

    frame = tk.Frame(win, bg="#f8f8f8", padx=20, pady=20)
    frame.pack(expand=True, fill="both")

    label = tk.Label(frame, text=message, bg="#f8f8f8", anchor="w", justify="left", font=("Arial", 11))
    label.pack(fill="x", pady=(0, 10))

    btn_frame = tk.Frame(frame, bg="#f8f8f8")
    btn_frame.pack()

    def add_button(text, val):
        btn = tk.Button(btn_frame, text=text, width=10, command=lambda: on_click(val))
        btn.pack(side="left", padx=10)

    if button == "ok":
        add_button("确定", "ok")
    elif button == "okcancel":
        add_button("确定", "ok")
        add_button("取消", "cancel")
    elif button == "yesno":
        add_button("是", "yes")
        add_button("否", "no")
    elif button == "yesnocancel":
        add_button("是", "yes")
        add_button("否", "no")
        add_button("取消", "cancel")

    # 设置超时
    if timeout > 0 and default_button:
        win.after(timeout * 1000, on_timeout)
    elif timeout == 0:
        win.destroy()
        return default_button or "cancel"

    win.mainloop()
    return result["value"]


def show_workbook_dialog(title, message):
    """
    打开数据表格对话框
    :param title:数据表格对话框标题。数据表格对话框启动后，左上角的内容即为title所指定的值
    :param message:数据表格对话框提示信息
    :return:str：返回数据表格对话框处理结果，如：ok 或 cancel 等
    """


def show_notification(message, placement='rightbottom', level='info', timeout=3):
    """
    打开消息通知框
    :param message:消息通知框的通知内容
    :param placement:通知消息在屏幕上的显示位置，可以在屏幕顶部（top）、屏幕底部（bottom）和屏幕右下角（rightbottom）显示。若不填，则默认在屏幕右下角显示
    :param level:通知消息的消息级别，默认为信息级别（info），除 信息 外还可以设置 警告（warning）和 错误（error）
    :param timeout:通知消息框显示时长。若不填，则默认显示3秒
    :return:
    """

    def _show():
        win = tk.Tk()
        win.overrideredirect(True)  # 无边框
        win.attributes("-topmost", True)
        win.configure(bg="white")

        # 颜色配置
        colors = {
            'info': ("#0078D7", "white"),
            'warning': ("#FFAA00", "black"),
            'error': ("#D13438", "white")
        }
        bg_color, fg_color = colors.get(level, ("#0078D7", "white"))

        # 样式与内容
        frame = tk.Frame(win, bg=bg_color, padx=15, pady=10)
        frame.pack()
        label = tk.Label(frame, text=message, bg=bg_color, fg=fg_color, font=("Arial", 11))
        label.pack()

        # 尺寸
        win.update_idletasks()
        w = win.winfo_width()
        h = win.winfo_height()

        # 屏幕尺寸
        sw = win.winfo_screenwidth()
        sh = win.winfo_screenheight()

        # 放置位置
        if placement == 'top':
            x = (sw - w) // 2
            y = 40
        elif placement == 'bottom':
            x = (sw - w) // 2
            y = sh - h - 60
        elif placement == 'rightbottom':
            x = sw - w - 20
            y = sh - h - 40
        else:
            x = sw - w - 20
            y = sh - h - 40

        win.geometry(f"{w}x{h}+{x}+{y}")

        # 自动关闭
        win.after(timeout * 1000, win.destroy)
        win.mainloop()

    # 独立线程展示通知（不阻塞主线程）
    threading.Thread(target=_show, daemon=True).start()


if __name__ == '__main__':
    show_alert(1111)
    # settings = {"dialog_title": "hello",
    #             "settings": {
    #                 "editors": [
    #                     {
    #                         "type": "Password",
    #                         "label": "密码框",
    #                         "nullText": None,
    #                         "value": "123"
    #                     },
    #                     {
    #                         "type": "TextArea",
    #                         "label": "文本域",
    #                         "nullText": "hello world",
    #                         "value": None
    #                     },
    #                     {
    #                         "type": "Date",
    #                         "label": "时间",
    #                         "value": "2020/04/18",
    #                     },
    #                     {
    #                         "type": "File",
    #                         "label": "文件选择器",
    #                         "nullText": "请选择文件",
    #                         "kind": "OpenFile"
    #                     }
    #                 ]
    #             }
    #             }
    # print(show_custom_dialog(settings))
    # print(show_message_box('title', '说点什么', button='yesno', timeout=3, default_button='yes'))

    show_notification("操作成功", placement="top", level="info", timeout=3)
    # show_notifycation("请注意数据格式", placement="bottom", level="warning", timeout=4)
    # show_notifycation("发生错误，请重试", placement="rightbottom", level="error", timeout=5)
    time.sleep(5)
