import os
import zipfile
import logging
import importlib.metadata
from functools import partial
from datetime import datetime, timedelta
from typing import List, Dict, Tuple, Literal, Generator

import cv2
import gradio as gr

from kotonebot.backend.context import task_registry, ContextStackVars
from kotonebot.config.manager import load_config, save_config
from kotonebot.tasks.common import (
    BaseConfig, APShopItems, CapsuleToysConfig, ClubRewardConfig, PurchaseConfig, ActivityFundsConfig,
    PresentsConfig, AssignmentConfig, ContestConfig, ProduceConfig,
    MissionRewardConfig, PIdol, DailyMoneyShopItems, ProduceAction,
    RecommendCardDetectionMode, TraceConfig, StartGameConfig, UpgradeSupportCardConfig,
    upgrade_config
)
from kotonebot.config.base_config import UserConfig, BackendConfig
from kotonebot.backend.bot import KotoneBot

# 初始化日志
os.makedirs('logs', exist_ok=True)
log_formatter = logging.Formatter('[%(asctime)s][%(levelname)s][%(name)s] %(message)s')
log_filename = datetime.now().strftime('logs/%y-%m-%d-%H-%M-%S.log')

console_handler = logging.StreamHandler()
console_handler.setFormatter(log_formatter)
file_handler = logging.FileHandler(log_filename, encoding='utf-8')
file_handler.setFormatter(log_formatter)

root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
root_logger.addHandler(console_handler)
root_logger.addHandler(file_handler)

logging.getLogger("kotonebot").setLevel(logging.DEBUG)

# 升级配置
upgrade_msg = upgrade_config()

logger = logging.getLogger(__name__)

def _save_bug_report(
    path: str | None = None
) -> Generator[str, None, str]:
    """
    保存报告

    :param path: 保存的路径。若为 `None`，则保存到 `./reports/bug-{YY-MM-DD HH-MM-SS}.zip`。
    :return: 保存的路径
    """
    from kotonebot import device
    from kotonebot.backend.context import ContextStackVars
    
    # 确保目录存在
    os.makedirs('logs', exist_ok=True)
    os.makedirs('reports', exist_ok=True)

    error = ""
    if path is None:
        path = f'./reports/bug-{datetime.now().strftime("%y-%m-%d %H-%M-%S")}.zip'
    with zipfile.ZipFile(path, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zipf:
        # 打包截图
        yield "### 打包上次截图..."
        try:
            stack = ContextStackVars.current()
            screenshot = None
            if stack is not None:
                screenshot = stack._screenshot
                if screenshot is not None:
                    img = cv2.imencode('.png', screenshot)[1].tobytes()
                    zipf.writestr('last_screenshot.png', img)
            if screenshot is None:
                error += "无上次截图数据\n"
        except Exception as e:
            error += f"保存上次截图失败：{str(e)}\n"

        # 打包当前截图
        yield "### 打包当前截图..."
        try:
            screenshot = device.screenshot()
            img = cv2.imencode('.png', screenshot)[1].tobytes()
            zipf.writestr('current_screenshot.png', img)
        except Exception as e:
            error += f"保存当前截图失败：{str(e)}\n"

        # 打包配置文件
        yield "### 打包配置文件..."
        try:
            with open('config.json', 'r', encoding='utf-8') as f:
                zipf.writestr('config.json', f.read())
        except Exception as e:
            error += f"保存配置文件失败：{str(e)}\n"

        # 打包 logs 文件夹
        if os.path.exists('logs'):
            for root, dirs, files in os.walk('logs'):
                for file in files:
                    file_path = os.path.join(root, file)
                    arcname = os.path.join('logs', os.path.relpath(file_path, 'logs'))
                    zipf.write(file_path, arcname)
                    yield f"### 打包 log 文件：{arcname}"

        # 写出版本号
        zipf.writestr('version.txt', version)
    
    # 上传报告
    from .file_host.sensio import upload
    yield "### 上传报告..."
    url = ''
    try:
        url = upload(path)
    except Exception as e:
        yield f"### 上传报告失败：{str(e)}\n\n"
        return ''

    final_msg = f"### 报告导出成功：{url}\n\n"
    expire_time = datetime.now() + timedelta(days=7)
    if error:
        final_msg += f"### 但发生了以下错误\n\n"
        final_msg += '\n* '.join(error.strip().split('\n'))
    final_msg += '\n'
    final_msg += f"### 此链接将于 {expire_time.strftime('%Y-%m-%d %H:%M:%S')}（7 天后）过期\n\n"
    final_msg += '### 复制以上文本并反馈给开发者'
    yield final_msg
    return path

version = importlib.metadata.version('ksaa')
class KotoneBotUI:
    def __init__(self) -> None:
        self.is_running: bool = False
        self.kaa: KotoneBot = KotoneBot(module='kotonebot.tasks', config_type=BaseConfig)
        self._load_config()
        self._setup_kaa()
        
    def _setup_kaa(self) -> None:
        from kotonebot.backend.debug.vars import debug, clear_saved
        logger.info('Version: %s', version)

        self.kaa.initialize()
        if self.current_config.keep_screenshots:
            debug.auto_save_to_folder = 'dumps'
            debug.enabled = True
            clear_saved()
        else:
            debug.auto_save_to_folder = None
            debug.enabled = False

    def export_dumps(self) -> str:
        """导出 dumps 文件夹为 zip 文件"""
        if not os.path.exists('dumps'):
            return "dumps 文件夹不存在"
        
        timestamp = datetime.now().strftime('%y-%m-%d-%H-%M-%S')
        zip_filename = f'dumps-{timestamp}.zip'
        
        with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zipf:
            for root, dirs, files in os.walk('dumps'):
                for file in files:
                    file_path = os.path.join(root, file)
                    arcname = os.path.relpath(file_path, 'dumps')
                    zipf.write(file_path, arcname)
        
        return f"已导出到 {zip_filename}"

    def export_logs(self) -> str:
        """导出 logs 文件夹为 zip 文件"""
        if not os.path.exists('logs'):
            return "logs 文件夹不存在"
        
        timestamp = datetime.now().strftime('%y-%m-%d-%H-%M-%S')
        zip_filename = f'logs-{timestamp}.zip'
        
        with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zipf:
            for root, dirs, files in os.walk('logs'):
                for file in files:
                    file_path = os.path.join(root, file)
                    arcname = os.path.relpath(file_path, 'logs')
                    zipf.write(file_path, arcname)
        
        return f"已导出到 {zip_filename}"

    def get_button_status(self) -> str:
        if not hasattr(self, 'run_status'):
            return "启动"
        
        if not self.run_status.running:
            self.is_running = False
            return "启动"
        return "停止"

    def update_task_status(self) -> List[List[str]]:
        status_list: List[List[str]] = []
        if not hasattr(self, 'run_status'):
            for task_name, task in task_registry.items():
                status_list.append([task.name, "等待中"])
            return status_list
        
        for task_status in self.run_status.tasks:
            status_text = {
                'pending': '等待中',
                'running': '运行中',
                'finished': '已完成',
                'error': '出错',
                'cancelled': '已取消'
            }.get(task_status.status, '未知')
            status_list.append([task_status.task.name, status_text])
        return status_list

    def toggle_run(self) -> Tuple[str, List[List[str]]]:
        if not self.is_running:
            return self.start_run()
        return self.stop_run()
    
    def start_run(self) -> Tuple[str, List[List[str]]]:
        self.is_running = True
        self.run_status = self.kaa.start_all()
        return "停止", self.update_task_status()

    def stop_run(self) -> Tuple[str, List[List[str]]]:
        self.is_running = False
        if self.kaa:
            self.run_status.interrupt()
        return "启动", self.update_task_status()

    def save_settings(
        self,
        adb_ip: str,
        adb_port: int,
        screenshot_method: Literal['adb', 'adb_raw', 'uiautomator2', 'windows'],
        keep_screenshots: bool,
        check_emulator: bool,
        emulator_path: str,
        adb_emulator_name: str,
        trace_recommend_card_detection: bool,
        purchase_enabled: bool,
        money_enabled: bool,
        ap_enabled: bool,
        ap_items: List[str],
        money_items: List[DailyMoneyShopItems],
        activity_funds_enabled: bool,
        presents_enabled: bool,
        assignment_enabled: bool,
        mini_live_reassign: bool,
        mini_live_duration: Literal[4, 6, 12],
        online_live_reassign: bool,
        online_live_duration: Literal[4, 6, 12],
        contest_enabled: bool,
        select_which_contestant: Literal[1, 2, 3],
        produce_enabled: bool,
        produce_mode: Literal["regular"],
        produce_count: int,
        produce_idols: List[str],
        memory_sets: List[str],
        auto_set_memory: bool,
        auto_set_support: bool,
        use_pt_boost: bool,
        use_note_boost: bool,
        follow_producer: bool,
        self_study_lesson: Literal['dance', 'visual', 'vocal'],
        prefer_lesson_ap: bool,
        actions_order: List[str],
        recommend_card_detection_mode: str,
        mission_reward_enabled: bool,
        # club reward
        club_reward_enabled: bool,
        selected_note: DailyMoneyShopItems,
        # upgrade support card
        upgrade_support_card_enabled: bool,
        # capsule toys
        capsule_toys_enabled: bool,
		friend_capsule_toys_count: int,
		sense_capsule_toys_count: int,
		logic_capsule_toys_count: int,
		anomaly_capsule_toys_count: int,
        # start game
        start_game_enabled: bool,
        start_through_kuyo: bool,
        game_package_name: str,
        kuyo_package_name: str,
    ) -> str:
        ap_items_enum: List[Literal[0, 1, 2, 3]] = []
        ap_items_map: Dict[str, APShopItems] = {
            "支援强化点数提升": APShopItems.PRODUCE_PT_UP,
            "笔记数提升": APShopItems.PRODUCE_NOTE_UP,
            "重新挑战券": APShopItems.RECHALLENGE,
            "回忆再生成券": APShopItems.REGENERATE_MEMORY
        }
        for item in ap_items:
            if item in ap_items_map:
                ap_items_enum.append(ap_items_map[item].value)  # type: ignore
        
        self.current_config.backend.adb_ip = adb_ip
        self.current_config.backend.adb_port = adb_port
        self.current_config.backend.adb_emulator_name = adb_emulator_name
        self.current_config.backend.screenshot_impl = screenshot_method
        self.current_config.keep_screenshots = keep_screenshots
        self.current_config.backend.check_emulator = check_emulator
        self.current_config.backend.emulator_path = emulator_path
        
        options = BaseConfig(
            purchase=PurchaseConfig(
                enabled=purchase_enabled,
                money_enabled=money_enabled,
                money_items=money_items,
                ap_enabled=ap_enabled,
                ap_items=ap_items_enum
            ),
            activity_funds=ActivityFundsConfig(
                enabled=activity_funds_enabled
            ),
            presents=PresentsConfig(
                enabled=presents_enabled
            ),
            assignment=AssignmentConfig(
                enabled=assignment_enabled,
                mini_live_reassign_enabled=mini_live_reassign,
                mini_live_duration=mini_live_duration,
                online_live_reassign_enabled=online_live_reassign,
                online_live_duration=online_live_duration
            ),
            contest=ContestConfig(
                enabled=contest_enabled,
                select_which_contestant=select_which_contestant
            ),
            produce=ProduceConfig(
                enabled=produce_enabled,
                mode=produce_mode,
                produce_count=produce_count,
                idols=[PIdol[idol] for idol in produce_idols],
                memory_sets=[int(i) for i in memory_sets],
                auto_set_memory=auto_set_memory,
                auto_set_support_card=auto_set_support,
                use_pt_boost=use_pt_boost,
                use_note_boost=use_note_boost,
                follow_producer=follow_producer,
                self_study_lesson=self_study_lesson,
                prefer_lesson_ap=prefer_lesson_ap,
                actions_order=[ProduceAction(action) for action in actions_order],
                recommend_card_detection_mode=RecommendCardDetectionMode(recommend_card_detection_mode)
            ),
            mission_reward=MissionRewardConfig(
                enabled=mission_reward_enabled
            ),
            club_reward=ClubRewardConfig(
                enabled=club_reward_enabled,
                selected_note=selected_note
            ),
            upgrade_support_card=UpgradeSupportCardConfig(
                enabled=upgrade_support_card_enabled
            ),
            capsule_toys=CapsuleToysConfig(
                enabled=capsule_toys_enabled,
                friend_capsule_toys_count=friend_capsule_toys_count,
                sense_capsule_toys_count=sense_capsule_toys_count,
                logic_capsule_toys_count=logic_capsule_toys_count,
                anomaly_capsule_toys_count=anomaly_capsule_toys_count
            ),
            start_game=StartGameConfig(
                enabled=start_game_enabled,
                start_through_kuyo=start_through_kuyo,
                game_package_name=game_package_name,
                kuyo_package_name=kuyo_package_name
            ),
            trace=TraceConfig(
                recommend_card_detection=trace_recommend_card_detection
            )
        )
        
        self.current_config.options = options
        
        try:
            save_config(self.config, "config.json")
            gr.Success("设置已保存，请重启程序！")
            return ""
        except Exception as e:
            gr.Warning(f"保存设置失败：{str(e)}")
            return ""

    def _create_status_tab(self) -> None:
        with gr.Tab("状态"):
            gr.Markdown("## 状态")
            
            with gr.Row():
                run_btn = gr.Button("启动", scale=1)
            if upgrade_msg:
                gr.Markdown('### 配置升级报告')
                gr.Markdown(upgrade_msg)
            gr.Markdown('脚本报错或者卡住？点击"日志"选项卡中的"一键导出报告"可以快速反馈！')
            
            task_status = gr.Dataframe(
                headers=["任务", "状态"],
                value=self.update_task_status(),
                label="任务状态"
            )
            
            def on_run_click(evt: gr.EventData) -> Tuple[str, List[List[str]]]:
                return self.toggle_run()
            
            run_btn.click(
                fn=on_run_click,
                outputs=[run_btn, task_status]
            )

            # 添加定时器，分别更新按钮状态和任务状态
            gr.Timer(1.0).tick(
                fn=self.get_button_status,
                outputs=[run_btn]
            )
            gr.Timer(1.0).tick(
                fn=self.update_task_status,
                outputs=[task_status]
            )

    def _create_task_tab(self) -> None:
        with gr.Tab("任务"):
            gr.Markdown("## 执行任务")
            
            # 创建任务选择下拉框
            task_choices = [task.name for task in task_registry.values()]
            task_dropdown = gr.Dropdown(
                choices=task_choices,
                label="选择要执行的任务",
                info="选择一个要单独执行的任务",
                type="value",
                value=None
            )
            
            # 创建执行按钮
            execute_btn = gr.Button("执行任务")
            task_result = gr.Markdown("")
            
            # TODO: 实现任务执行逻辑
            def execute_single_task(task_name: str) -> str:
                if not task_name:
                    gr.Warning("请先选择一个任务")
                    return ""
                task = None
                for name, task in task_registry.items():
                    if name == task_name:
                        task = task
                        break
                if task is None:
                    gr.Warning(f"任务 {task_name} 未找到")
                    return ""
                gr.Info(f"任务 {task_name} 开始执行。执行结束前，请勿重复点击执行。")
                self.kaa.run([task])
                gr.Success(f"任务 {task_name} 执行完毕")
                return ""
            
            execute_btn.click(
                fn=execute_single_task,
                inputs=[task_dropdown],
                outputs=[task_result]
            )

    def _create_emulator_settings(self) -> Tuple[gr.Textbox, gr.Number, gr.Dropdown, gr.Checkbox, gr.Checkbox, gr.Textbox, gr.Textbox]:
        with gr.Column():
            gr.Markdown("### 模拟器设置")
            adb_ip = gr.Textbox(
                value=self.current_config.backend.adb_ip,
                label="ADB IP 地址",
                info=BackendConfig.model_fields['adb_ip'].description,
                interactive=True
            )
            adb_port = gr.Number(
                value=self.current_config.backend.adb_port,
                label="ADB 端口",
                info=BackendConfig.model_fields['adb_port'].description,
                minimum=1,
                maximum=65535,
                step=1,
                interactive=True
            )
            screenshot_impl = gr.Dropdown(
                choices=['adb', 'adb_raw', 'uiautomator2', 'windows'],
                value=self.current_config.backend.screenshot_impl,
                label="截图方法",
                info=BackendConfig.model_fields['screenshot_impl'].description,
                interactive=True
            )
            keep_screenshots = gr.Checkbox(
                label="保留截图数据",
                value=self.current_config.keep_screenshots,
                info=UserConfig.model_fields['keep_screenshots'].description,
                interactive=True
            )
            check_emulator = gr.Checkbox(
                label="检查并启动模拟器",
                value=self.current_config.backend.check_emulator,
                info=BackendConfig.model_fields['check_emulator'].description,
                interactive=True
            )
            with gr.Group(visible=self.current_config.backend.check_emulator) as check_emulator_group:
                emulator_path = gr.Textbox(
                    value=self.current_config.backend.emulator_path,
                    label="模拟器 exe 文件路径",
                    info=BackendConfig.model_fields['emulator_path'].description,
                    interactive=True
                )
                adb_emulator_name = gr.Textbox(
                    value=self.current_config.backend.adb_emulator_name,
                    label="ADB 模拟器名称",
                    info=BackendConfig.model_fields['adb_emulator_name'].description,
                    interactive=True
                )
            check_emulator.change(
                fn=lambda x: gr.Group(visible=x),
                inputs=[check_emulator],
                outputs=[check_emulator_group]
            )
        return adb_ip, adb_port, screenshot_impl, keep_screenshots, check_emulator, emulator_path, adb_emulator_name

    def _create_purchase_settings(self) -> Tuple[gr.Checkbox, gr.Checkbox, gr.Checkbox, gr.Dropdown, gr.Dropdown]:
        with gr.Column():
            gr.Markdown("### 商店购买设置")
            purchase_enabled = gr.Checkbox(
                label="启用商店购买",
                value=self.current_config.options.purchase.enabled,
                info=PurchaseConfig.model_fields['enabled'].description
            )
            with gr.Group(visible=self.current_config.options.purchase.enabled) as purchase_group:
                money_enabled = gr.Checkbox(
                    label="启用金币购买",
                    value=self.current_config.options.purchase.money_enabled,
                    info=PurchaseConfig.model_fields['money_enabled'].description
                )
                
                # 添加金币商店商品选择
                money_items = gr.Dropdown(
                    multiselect=True,
                    choices=list(DailyMoneyShopItems.all()),
                    value=self.current_config.options.purchase.money_items,
                    label="金币商店购买物品",
                    info=PurchaseConfig.model_fields['money_items'].description
                )
                
                ap_enabled = gr.Checkbox(
                    label="启用AP购买",
                    value=self.current_config.options.purchase.ap_enabled,
                    info=PurchaseConfig.model_fields['ap_enabled'].description
                )
                
                # 转换枚举值为显示文本
                selected_items: List[str] = []
                ap_items_map = {
                    APShopItems.PRODUCE_PT_UP: "支援强化点数提升",
                    APShopItems.PRODUCE_NOTE_UP: "笔记数提升",
                    APShopItems.RECHALLENGE: "重新挑战券",
                    APShopItems.REGENERATE_MEMORY: "回忆再生成券"
                }
                for item_value in self.current_config.options.purchase.ap_items:
                    item_enum = APShopItems(item_value)
                    if item_enum in ap_items_map:
                        selected_items.append(ap_items_map[item_enum])
                
                ap_items = gr.Dropdown(
                    multiselect=True,
                    choices=list(ap_items_map.values()),
                    value=selected_items,
                    label="AP商店购买物品",
                    info=PurchaseConfig.model_fields['ap_items'].description
                )
            
            purchase_enabled.change(
                fn=lambda x: gr.Group(visible=x),
                inputs=[purchase_enabled],
                outputs=[purchase_group]
            )
        return purchase_enabled, money_enabled, ap_enabled, ap_items, money_items

    def _create_work_settings(self) -> Tuple[gr.Checkbox, gr.Checkbox, gr.Dropdown, gr.Checkbox, gr.Dropdown]:
        with gr.Column():
            gr.Markdown("### 工作设置")
            assignment_enabled = gr.Checkbox(
                label="启用工作",
                value=self.current_config.options.assignment.enabled,
                info=AssignmentConfig.model_fields['enabled'].description
            )
            with gr.Group(visible=self.current_config.options.assignment.enabled) as work_group:
                with gr.Row():
                    with gr.Column():
                        mini_live_reassign = gr.Checkbox(
                            label="启用重新分配 MiniLive",
                            value=self.current_config.options.assignment.mini_live_reassign_enabled,
                            info=AssignmentConfig.model_fields['mini_live_reassign_enabled'].description
                        )
                        mini_live_duration = gr.Dropdown(
                            choices=[4, 6, 12],
                            value=self.current_config.options.assignment.mini_live_duration,
                            label="MiniLive 工作时长",
                            interactive=True,
                            info=AssignmentConfig.model_fields['mini_live_duration'].description
                        )
                    with gr.Column():
                        online_live_reassign = gr.Checkbox(
                            label="启用重新分配 OnlineLive",
                            value=self.current_config.options.assignment.online_live_reassign_enabled,
                            info=AssignmentConfig.model_fields['online_live_reassign_enabled'].description
                        )
                        online_live_duration = gr.Dropdown(
                            choices=[4, 6, 12],
                            value=self.current_config.options.assignment.online_live_duration,
                            label="OnlineLive 工作时长",
                            interactive=True,
                            info=AssignmentConfig.model_fields['online_live_duration'].description
                        )
            
            assignment_enabled.change(
                fn=lambda x: gr.Group(visible=x),
                inputs=[assignment_enabled],
                outputs=[work_group]
            )
        return assignment_enabled, mini_live_reassign, mini_live_duration, online_live_reassign, online_live_duration
    
    def _create_contest_settings(self) -> Tuple[gr.Checkbox, gr.Dropdown]:
        with gr.Column():
            gr.Markdown("### 竞赛设置")
            contest_enabled = gr.Checkbox(
                label="启用竞赛",
                value=self.current_config.options.contest.enabled,
                info=ContestConfig.model_fields['enabled'].description
            )
            with gr.Group(visible=self.current_config.options.contest.enabled) as contest_group:
                select_which_contestant = gr.Dropdown(
                    choices=[1, 2, 3],
                    value=self.current_config.options.contest.select_which_contestant,
                    label="选择第几位竞赛目标",
                    interactive=True,
                    info=ContestConfig.model_fields['select_which_contestant'].description
                )
            contest_enabled.change(
                fn=lambda x: gr.Group(visible=x),
                inputs=[contest_enabled],
                outputs=[contest_group]
            )
        return contest_enabled, select_which_contestant

    def _create_produce_settings(self):
        with gr.Column():
            gr.Markdown("### 培育设置")
            produce_enabled = gr.Checkbox(
                label="启用培育",
                value=self.current_config.options.produce.enabled,
                info=ProduceConfig.model_fields['enabled'].description
            )
            with gr.Group(visible=self.current_config.options.produce.enabled) as produce_group:
                produce_mode = gr.Dropdown(
                    choices=["regular", "pro"],
                    value=self.current_config.options.produce.mode,
                    label="培育模式",
                    info=ProduceConfig.model_fields['mode'].description
                )
                produce_count = gr.Number(
                    minimum=1,
                    value=self.current_config.options.produce.produce_count,
                    label="培育次数",
                    interactive=True,
                    info=ProduceConfig.model_fields['produce_count'].description
                )
                # 添加偶像选择
                idol_choices = [idol.name for idol in PIdol]
                selected_idols = [idol.name for idol in self.current_config.options.produce.idols]
                produce_idols = gr.Dropdown(
                    choices=idol_choices,
                    value=selected_idols,
                    label="选择要培育的偶像",
                    multiselect=True,
                    interactive=True,
                    info=ProduceConfig.model_fields['idols'].description
                )
                has_kotone = any("藤田ことね" in idol for idol in selected_idols)
                is_strict_mode = self.current_config.options.produce.recommend_card_detection_mode == RecommendCardDetectionMode.STRICT
                kotone_warning = gr.Markdown(
                    visible=has_kotone and not is_strict_mode,
                    value="使用「藤田ことね」进行培育时，确保将「推荐卡检测模式」设置为「严格模式」"
                )
                auto_set_memory = gr.Checkbox(
                    label="自动编成回忆",
                    value=self.current_config.options.produce.auto_set_memory,
                    info=ProduceConfig.model_fields['auto_set_memory'].description
                )
                with gr.Group(visible=not self.current_config.options.produce.auto_set_memory) as memory_sets_group:
                    memory_sets = gr.Dropdown(
                        choices=[str(i) for i in range(1, 11)],  # 假设最多10个编成位
                        value=[str(i) for i in self.current_config.options.produce.memory_sets],
                        label="回忆编成编号",
                        multiselect=True,
                        interactive=True,
                        info=ProduceConfig.model_fields['memory_sets'].description
                    )
                
                # 添加偶像选择变化时的回调
                def update_kotone_warning(selected_idols, recommend_card_detection_mode):
                    has_kotone = any("藤田ことね" in idol for idol in selected_idols)
                    is_strict_mode = recommend_card_detection_mode == RecommendCardDetectionMode.STRICT.value
                    return gr.Markdown(visible=has_kotone and not is_strict_mode)
                
                auto_set_support = gr.Checkbox(
                    label="自动编成支援卡",
                    value=self.current_config.options.produce.auto_set_support_card,
                    info=ProduceConfig.model_fields['auto_set_support_card'].description
                )
                use_pt_boost = gr.Checkbox(
                    label="使用支援强化 Pt 提升",
                    value=self.current_config.options.produce.use_pt_boost,
                    info=ProduceConfig.model_fields['use_pt_boost'].description
                )
                use_note_boost = gr.Checkbox(
                    label="使用笔记数提升",
                    value=self.current_config.options.produce.use_note_boost,
                    info=ProduceConfig.model_fields['use_note_boost'].description
                )
                follow_producer = gr.Checkbox(
                    label="关注租借了支援卡的制作人",
                    value=self.current_config.options.produce.follow_producer,
                    info=ProduceConfig.model_fields['follow_producer'].description
                )
                self_study_lesson = gr.Dropdown(
                    choices=['dance', 'visual', 'vocal'],
                    value=self.current_config.options.produce.self_study_lesson,
                    label='文化课自习时选项',
                    info='选择自习课类型'
                )
                prefer_lesson_ap = gr.Checkbox(
                    label="SP 课程优先",
                    value=self.current_config.options.produce.prefer_lesson_ap,
                    info=ProduceConfig.model_fields['prefer_lesson_ap'].description
                )
                actions_order = gr.Dropdown(
                    choices=[(action.display_name, action.value) for action in ProduceAction],
                    value=[action.value for action in self.current_config.options.produce.actions_order],
                    label="行动优先级",
                    info="设置每周行动的优先级顺序",
                    multiselect=True
                )

                # 添加推荐卡检测模式设置
                recommend_card_detection_mode = gr.Dropdown(
                    choices=[
                        (RecommendCardDetectionMode.NORMAL.display_name, RecommendCardDetectionMode.NORMAL.value),
                        (RecommendCardDetectionMode.STRICT.display_name, RecommendCardDetectionMode.STRICT.value)
                    ],
                    value=self.current_config.options.produce.recommend_card_detection_mode.value,
                    label="推荐卡检测模式",
                    info=ProduceConfig.model_fields['recommend_card_detection_mode'].description
                )
                recommend_card_detection_mode.change(
                    fn=update_kotone_warning,
                    inputs=[produce_idols, recommend_card_detection_mode],
                    outputs=kotone_warning
                )
                produce_idols.change(
                    fn=update_kotone_warning,
                    inputs=[produce_idols, recommend_card_detection_mode],
                    outputs=kotone_warning
                )

            produce_enabled.change(
                fn=lambda x: gr.Group(visible=x),
                inputs=[produce_enabled],
                outputs=[produce_group]
            )
            
            auto_set_memory.change(
                fn=lambda x: gr.Group(visible=not x),
                inputs=[auto_set_memory],
                outputs=[memory_sets_group]
            )
        return produce_enabled, produce_mode, produce_count, produce_idols, memory_sets, auto_set_memory, auto_set_support, use_pt_boost, use_note_boost, follow_producer, self_study_lesson, prefer_lesson_ap, actions_order, recommend_card_detection_mode
    
    def _create_club_reward_settings(self) -> Tuple[gr.Checkbox, gr.Dropdown]:
        with gr.Column():
            gr.Markdown("### 社团奖励设置")
            club_reward_enabled = gr.Checkbox(
                label="启用社团奖励",
                value=self.current_config.options.club_reward.enabled,
                info=ClubRewardConfig.model_fields['enabled'].description
            )
            with gr.Group(visible=self.current_config.options.club_reward.enabled) as club_reward_group:
                selected_note = gr.Dropdown(
                    choices=list(DailyMoneyShopItems.note_items()),
                    value=self.current_config.options.club_reward.selected_note,
                    label="想在社团奖励中获取到的笔记",
                    interactive=True,
                    info=ClubRewardConfig.model_fields['selected_note'].description
                )
            club_reward_enabled.change(
                fn=lambda x: gr.Group(visible=x),
                inputs=[club_reward_enabled],
                outputs=[club_reward_group]
            )
        return club_reward_enabled, selected_note

    def _create_capsule_toys_settings(self) -> Tuple[gr.Checkbox, gr.Number, gr.Number, gr.Number, gr.Number]:
        with gr.Column():
            gr.Markdown("### 扭蛋设置")
            capsule_toys_enabled = gr.Checkbox(
                label="是否启用自动扭蛋机",
                value=self.current_config.options.capsule_toys.enabled,
                info=CapsuleToysConfig.model_fields['enabled'].description
            )
            min_value = 0
            max_value = 10
            with gr.Group(visible=self.current_config.options.capsule_toys.enabled) as capsule_toys_group:
                friend_capsule_toys_count = gr.Number(
                    value=self.current_config.options.capsule_toys.friend_capsule_toys_count,
                    label="好友扭蛋机的扭蛋次数",
                    info=CapsuleToysConfig.model_fields['friend_capsule_toys_count'].description,
                    minimum=0,
                    maximum=5
                )
                sense_capsule_toys_count = gr.Number(
                    value=self.current_config.options.capsule_toys.sense_capsule_toys_count,
                    label="感性扭蛋机的扭蛋次数",
                    info=CapsuleToysConfig.model_fields['sense_capsule_toys_count'].description,
                    minimum=0,
                    maximum=5
                )
                logic_capsule_toys_count = gr.Number(
                    value=self.current_config.options.capsule_toys.logic_capsule_toys_count,
                    label="逻辑扭蛋机的扭蛋次数",
                    info=CapsuleToysConfig.model_fields['logic_capsule_toys_count'].description,
                    minimum=0,
                    maximum=5
                )
                anomaly_capsule_toys_count = gr.Number(
                    value=self.current_config.options.capsule_toys.anomaly_capsule_toys_count,
                    label="非凡扭蛋机的扭蛋次数",
                    info=CapsuleToysConfig.model_fields['anomaly_capsule_toys_count'].description,
                    minimum=0,
                    maximum=5
                )
            capsule_toys_enabled.change(
                fn=lambda x: gr.Group(visible=x),
                inputs=[capsule_toys_enabled],
                outputs=[capsule_toys_group]
            )
        return capsule_toys_enabled, friend_capsule_toys_count, sense_capsule_toys_count, logic_capsule_toys_count, anomaly_capsule_toys_count


    def _create_start_game_settings(self) -> Tuple[gr.Checkbox, gr.Checkbox, gr.Textbox, gr.Textbox]:
        with gr.Column():
            gr.Markdown("### 启动游戏设置")
            start_game_enabled = gr.Checkbox(
                label="是否启用 自动启动游戏",
                value=self.current_config.options.start_game.enabled,
                info=StartGameConfig.model_fields['enabled'].description
            )
            with gr.Group(visible=self.current_config.options.start_game.enabled) as start_game_group:
                start_through_kuyo = gr.Checkbox(
                    label="是否通过Kuyo启动游戏",
                    value=self.current_config.options.start_game.start_through_kuyo,
                    info=StartGameConfig.model_fields['start_through_kuyo'].description
                )
                game_package_name = gr.Textbox(
                    value=self.current_config.options.start_game.game_package_name,
                    label="游戏包名",
                    info=StartGameConfig.model_fields['game_package_name'].description
                )
                kuyo_package_name = gr.Textbox(
                    value=self.current_config.options.start_game.kuyo_package_name,
                    label="Kuyo包名",
                    info=StartGameConfig.model_fields['kuyo_package_name'].description
                )
            start_game_enabled.change(
                fn=lambda x: gr.Group(visible=x),
                inputs=[start_game_enabled],
                outputs=[start_game_group]
            )
        return start_game_enabled, start_through_kuyo, game_package_name, kuyo_package_name

    def _create_settings_tab(self) -> None:
        with gr.Tab("设置"):
            gr.Markdown("## 设置")
            
            # 模拟器设置
            emulator_settings = self._create_emulator_settings()
            
            # 商店购买设置
            purchase_settings = self._create_purchase_settings()
            
            # 活动费设置
            with gr.Column():
                gr.Markdown("### 活动费设置")
                activity_funds = gr.Checkbox(
                    label="启用收取活动费",
                    value=self.current_config.options.activity_funds.enabled,
                    info=ActivityFundsConfig.model_fields['enabled'].description
                )
            
            # 礼物设置
            with gr.Column():
                gr.Markdown("### 礼物设置")
                presents = gr.Checkbox(
                    label="启用收取礼物",
                    value=self.current_config.options.presents.enabled,
                    info=PresentsConfig.model_fields['enabled'].description
                )
            
            # 工作设置
            work_settings = self._create_work_settings()
            
            # 竞赛设置
            contest_settings = self._create_contest_settings()
            
            # 培育设置
            produce_settings = self._create_produce_settings()
            
            # 任务奖励设置
            with gr.Column():
                gr.Markdown("### 任务奖励设置")
                mission_reward = gr.Checkbox(
                    label="启用领取任务奖励",
                    value=self.current_config.options.mission_reward.enabled,
                    info=MissionRewardConfig.model_fields['enabled'].description
                )
            
            # 社团奖励设置
            club_reward_settings = self._create_club_reward_settings()

            # 升级支援卡设置
            with gr.Column():
                gr.Markdown("### 升级支援卡设置")
                upgrade_support_card_enabled = gr.Checkbox(
                    label="启用升级支援卡",
                    value=self.current_config.options.upgrade_support_card.enabled,
                    info=UpgradeSupportCardConfig.model_fields['enabled'].description
                )
            
            capsule_toys_settings = self._create_capsule_toys_settings()
            
            # 跟踪设置
            with gr.Column():
                gr.Markdown("### 跟踪设置")
                gr.Markdown("跟踪功能会记录指定模块的运行情况，并保存截图。")
                trace_recommend_card_detection = gr.Checkbox(
                    label="跟踪推荐卡检测",
                    value=self.current_config.options.trace.recommend_card_detection,
                    info=TraceConfig.model_fields['recommend_card_detection'].description,
                    interactive=True
                )

            # 启动游戏设置
            start_game_settings = self._create_start_game_settings()
            
            save_btn = gr.Button("保存设置")
            result = gr.Markdown()
            
            # 收集所有设置组件
            all_settings = [
                *emulator_settings,
                trace_recommend_card_detection,
                *purchase_settings,
                activity_funds,
                presents,
                *work_settings,
                *contest_settings,
                *produce_settings,
                mission_reward,
                *club_reward_settings,
                upgrade_support_card_enabled,
                *capsule_toys_settings,
                *start_game_settings
            ]
            
            save_btn.click(
                fn=self.save_settings,
                inputs=all_settings,
                outputs=result
            )

    def _create_log_tab(self) -> None:
        with gr.Tab("日志"):
            gr.Markdown("## 日志")
            
            with gr.Column():
                with gr.Row():
                    export_dumps_btn = gr.Button("导出 dump")
                    export_logs_btn = gr.Button("导出日志")
                with gr.Row():
                    save_report_btn = gr.Button("一键导出报告")
                result_text = gr.Markdown("等待操作\n\n\n")
            
            export_dumps_btn.click(
                fn=self.export_dumps,
                outputs=[result_text]
            )
            export_logs_btn.click(
                fn=self.export_logs,
                outputs=[result_text]
            )
            save_report_btn.click(
                fn=partial(_save_bug_report),
                outputs=[result_text]
            )

    def _create_whats_new_tab(self) -> None:
        """创建更新日志标签页，并显示最新版本更新内容"""
        with gr.Tab("更新日志"):
            from kotonebot.tasks.metadata import WHATS_NEW
            gr.Markdown(WHATS_NEW)

    def _create_screen_tab(self) -> None:
        with gr.Tab("画面"):
            gr.Markdown("## 当前设备画面")
            WIDTH = 720 // 3
            HEIGHT = 1280 // 3
            last_update_text = gr.Markdown("上次更新时间：无数据")
            screenshot_display = gr.Image(type="numpy", width=WIDTH, height=HEIGHT)

            def update_screenshot():
                ctx = ContextStackVars.current()
                if ctx is None:
                    return [None, last_update_text.value]
                screenshot = ctx._screenshot
                if screenshot is None:
                    return [None, last_update_text.value]
                screenshot = cv2.cvtColor(screenshot, cv2.COLOR_BGR2RGB)
                return screenshot, f"上次更新时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"

            gr.Timer(0.3).tick(
                fn=update_screenshot,
                outputs=[screenshot_display, last_update_text]
            )

    def _load_config(self) -> None:
        # 加载配置文件
        config_path = "config.json"
        self.config = load_config(config_path, type=BaseConfig, use_default_if_not_found=True)
        if not self.config.user_configs:
            # 如果没有用户配置，创建一个默认配置
            default_config = UserConfig[BaseConfig](
                name="默认配置",
                category="default",
                description="默认配置",
                backend=BackendConfig(),
                options=BaseConfig()
            )
            self.config.user_configs.append(default_config)
        self.current_config = self.config.user_configs[0]

    def create_ui(self) -> gr.Blocks:
        with gr.Blocks(title="琴音小助手", css="#container { max-width: 800px; margin: auto; padding: 20px; }") as app:
            with gr.Column(elem_id="container"):
                gr.Markdown(f"# 琴音小助手 v{version}")
                
                with gr.Tabs():
                    self._create_status_tab()
                    self._create_task_tab()
                    self._create_settings_tab()
                    self._create_log_tab()
                    self._create_whats_new_tab()
                    self._create_screen_tab()
            
        return app

def main() -> None:
    ui = KotoneBotUI()
    app = ui.create_ui()
    app.launch(inbrowser=True, show_error=True, server_name="0.0.0.0")

if __name__ == "__main__":
    main()
