from __future__ import annotations

from textual.screen import ModalScreen
from textual.app import ComposeResult
from textual.containers import Horizontal, Vertical, Center, Container, ScrollableContainer
from textual.widgets import DataTable, Input, Button, Static, Markdown, Checkbox, TextArea
from textual import on
from textual.message import Message
import logging
import os
import yaml

from ketacli.sdk.ai.skills.registry import SkillsRegistry
from ketacli.sdk.ai.skills.loader import load_skill_by_name
from ketacli.sdk.ai.skills import DEFAULT_USER_SKILLS_DIR


class SkillEditorModal(ModalScreen):
    """技能编辑器：支持新增与编辑技能YAML。"""

    class SkillSaved(Message):
        def __init__(self, name: str, path: str):
            super().__init__()
            self.name = name
            self.path = path

    def __init__(self, mode: str = "create", preset_meta=None, preset_desc: str = "") -> None:
        super().__init__()
        self.mode = mode  # create | edit
        self.preset_meta = preset_meta
        self.preset_desc = preset_desc or ""
        self.logger = logging.getLogger("ketacli.textual")
        self._perm_options: list[str] = []
        self._tool_options: list[str] = []

    CSS = """
    SkillEditorModal { align: center middle; }
    #skill-editor-modal { width: 80%; max-height: 100%; background: $surface; border: round $primary; padding: 1; }
    #skill-editor { padding: 1; }
    .row { height: 3; margin: 0 0 1 0; }
    .row.textarea { height: auto; }
    .label { width: 20; content-align: right middle; margin-right: 1; }
    .input { width: 1fr; }
    .desc-area { height: 18; }
    .buttons { dock: bottom; height: 3; content-align: right middle; }
    .buttons Button { margin: 0 1; }
    """

    def compose(self) -> ComposeResult:
        title = "新增技能" if self.mode == "create" else "编辑技能"
        with Center():
            with Container(id="skill-editor-modal"):
                yield Static(title, classes="toolbar")
                yield Vertical(id="skill-editor", classes="editor")

    def on_mount(self) -> None:
        container = self.query_one("#skill-editor", Vertical)
        # 构建表单行
        def add_row(label_text: str, widget):
            row = Horizontal(classes="row")
            container.mount(row)
            row.mount(Static(label_text, classes="label", markup=False))
            widget.classes = "input"
            row.mount(widget)
            try:
                if "checkbox-group" in (widget.classes or ""):
                    row.add_class("textarea")
            except Exception:
                pass
            return widget
        name_inp = Input()
        summary_inp = Input()
        enabled_cb = Checkbox(label="启用", value=True, compact=True)
        triggers_inp = Input(placeholder="用逗号分隔，例如 a,b,c")
        # 动态权限选项（从注册表聚合），若为空则给出默认
        try:
            reg = SkillsRegistry()
            reg.build()
            opts = set()
            for m in reg.list_metas():
                for p in (m.permissions or []):
                    if isinstance(p, str) and p.strip():
                        opts.add(p.strip())
            self._perm_options = sorted(list(opts)) or ["system", "read_only"]
        except Exception:
            self._perm_options = ["system", "read_only"]
        perms_group = ScrollableContainer(classes="checkbox-group", id="perms-group")
        # 动态工具白名单选项（从函数注册表）
        try:
            from ketacli.sdk.ai.function_call import function_registry
            tools_defs = function_registry.get_openai_tools_format() or []
            names = []
            for tool in tools_defs:
                func_info = tool.get("function", {})
                name = func_info.get("name", "")
                if name:
                    names.append(name)
            self._tool_options = sorted(list(set(names)))
        except Exception:
            self._tool_options = []
        tools_group = ScrollableContainer(classes="tools-group", id="tools-group")
        desc_ta = TextArea()
        desc_ta.classes = "input desc-area"
        name_inp.id = "field-name"
        summary_inp.id = "field-summary"
        enabled_cb.id = "field-enabled"
        triggers_inp.id = "field-triggers"
        # 复选组不需要输入框ID，保留组容器以便布局
        desc_ta.id = "field-description"
        add_row("名称", name_inp)
        add_row("摘要", summary_inp)
        add_row("启用", enabled_cb)
        add_row("触发词", triggers_inp)
        add_row("权限", perms_group)
        add_row("工具白名单", tools_group)
        for opt in self._perm_options:
            cb = Checkbox(label=opt, value=False, compact=True)
            cb.id = f"checkbox-perm-{opt}"
            perms_group.mount(cb)
        for name in self._tool_options:
            cb = Checkbox(label=name, value=False, compact=True)
            cb.id = f"checkbox-tool-{name}"
            tools_group.mount(cb)
        # 描述单独一行
        row_desc = Horizontal(classes="row textarea")
        container.mount(row_desc)
        row_desc.mount(Static("描述", classes="label", markup=False))
        row_desc.mount(desc_ta)
        try:
            desc_ta.styles.min_height = 12
        except Exception:
            pass

        # 预填充
        if self.mode == "edit" and self.preset_meta:
            name_inp.value = self.preset_meta.name or ""
            summary_inp.value = self.preset_meta.summary or ""
            enabled_cb.value = bool(self.preset_meta.enabled)
            triggers_inp.value = ",".join(self.preset_meta.triggers or [])
            try:
                for opt in (self.preset_meta.permissions or []):
                    cid = f"#checkbox-perm-{opt}"
                    try:
                        cb = self.query_one(cid, Checkbox)
                        cb.value = True
                    except Exception:
                        pass
                for name in (self.preset_meta.tools_whitelist or []):
                    cid = f"#checkbox-tool-{name}"
                    try:
                        cb = self.query_one(cid, Checkbox)
                        cb.value = True
                    except Exception:
                        pass
            except Exception:
                pass
            try:
                desc_ta.text = self.preset_desc or ""
            except Exception:
                pass

        # 底部按钮
        btns = Horizontal(classes="buttons")
        container.mount(btns)
        btns.mount(Button("保存", id="save", variant="primary"))
        btns.mount(Button("取消", id="cancel"))

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "cancel":
            self.dismiss()
            return
        if event.button.id != "save":
            return
        # 读取表单值
        name = self.query_one("#field-name", Input).value.strip()
        self.logger.debug(f"[skills] 保存技能：name={name}")
        summary = self.query_one("#field-summary", Input).value.strip()
        enabled = self.query_one("#field-enabled", Checkbox).value
        triggers = [s.strip() for s in (self.query_one("#field-triggers", Input).value or "").split(",") if s.strip()]
        # 收集多选项
        permissions = []
        for opt in self._perm_options:
            cid = f"#checkbox-perm-{opt}"
            try:
                cb = self.query_one(cid, Checkbox)
                if cb.value:
                    permissions.append(opt)
            except Exception:
                pass
        tools = []
        for tool_name in self._tool_options:
            cid = f"#checkbox-tool-{tool_name}"
            try:
                cb = self.query_one(cid, Checkbox)
                if cb.value:
                    tools.append(tool_name)
            except Exception:
                pass
        try:
            desc_widget = self.query_one("#field-description", TextArea)
            description = getattr(desc_widget, "text", "")
        except Exception:
            description = ""
        self.logger.debug(f"[skills] 准备保存技能：name={name} enabled={enabled} triggers={triggers} perms={permissions} tools={tools} desc_len={len(description or '')}")
        if not name or not summary:
            self.app.notify("请填写名称与摘要", severity="error")
            return
        # 确定保存路径
        save_path = None
        if self.mode == "edit" and getattr(self.preset_meta, "source_path", None):
            save_path = self.preset_meta.source_path
        else:
            os.makedirs(os.path.abspath(os.path.expanduser(DEFAULT_USER_SKILLS_DIR)), exist_ok=True)
            save_path = os.path.join(os.path.abspath(os.path.expanduser(DEFAULT_USER_SKILLS_DIR)), f"{name}.yaml")
            if os.path.exists(save_path):
                self.app.notify("用户目录已存在同名技能文件，请更换名称或选择编辑", severity="error")
                return
        # 写入YAML
        data = {
            "name": name,
            "summary": summary,
            "enabled": bool(enabled),
            "triggers": triggers or None,
            "permissions": permissions or None,
            "tools_whitelist": tools or None,
            "description": description or "",
        }
        with open(save_path, "w", encoding="utf-8") as f:
            yaml.safe_dump(data, f, allow_unicode=True, sort_keys=False)
        self.logger.debug(f"[skills] 已写入技能YAML：path={save_path}")
        try:
            self.post_message(self.SkillSaved(name=name, path=save_path))
        except Exception:
            pass
        self.dismiss()


class SkillsBrowserModal(ModalScreen):
    """技能浏览器：左侧列表（元信息），右侧详情（懒加载 description）。"""

    CSS = """
    SkillsBrowserModal {
        align: center middle;
    }
    .root {
        width: 90%;
        height: 80%;
        border: round;
        padding: 1 2;
        background: $surface;
    }
    .left {
        width: 50%;
        height: 1fr;
    }
    .right {
        width: 50%;
        height: 1fr;
    }
    .toolbar {
        height: 3;
    }
    #skill-table {
        height: 1fr;
        min-height: 24;
        overflow: auto;
    }
    .desc {
        height: 1fr;
        overflow: auto;
    }
    """
    logger = logging.getLogger("ketacli.textual")

    def __init__(self) -> None:
        super().__init__()
        self.registry = SkillsRegistry()
        self._metas = []
        self._filter_text = ""
        self._selected_name = None
        self._selected_names: set[str] = set()

    class SkillChosen(Message):
        def __init__(self, name: str, skill):
            super().__init__()
            self.name = name
            self.skill = skill

    class SkillsChosenMulti(Message):
        def __init__(self, names: list[str], skills: list):
            super().__init__()
            self.names = names
            self.skills = skills

    def compose(self) -> ComposeResult:
        yield Horizontal(
            Vertical(
                Static("技能列表", classes="toolbar"),
                Input(placeholder="搜索 name/summary/triggers...", id="skill-search"),
                DataTable(id="skill-table"),
                Horizontal(
                    Button("新增技能", id="new"),
                    Button("编辑技能", id="edit"),
                    Button("添加到选择", id="add"),
                    Button("选择并开始", id="choose"),
                    Button("重建索引", id="reload"),
                ),
                classes="left",
            ),
            Vertical(
                Static("技能详情", classes="toolbar"),
                Markdown(id="skill-desc", classes="desc"),
                Static(id="skill-meta"),
                Static("已选择：<无>", id="selected-list"),
                Button("开始任务", id="start"),
                Button("关闭", id="close"),
                classes="right",
            ),
            classes="root",
        )

    def on_mount(self) -> None:
        self._build_and_fill()

    def _build_and_fill(self) -> None:
        self.registry.build()
        self._metas = self.registry.list_metas()
        table = self.query_one("#skill-table", DataTable)
        table.clear()
        table.cursor_type = "row"
        if not table.columns:
            table.add_columns("name", "summary", "enabled", "permissions", "source_path")
        for m in self._iter_filtered_metas():
            perms = ", ".join(m.permissions or []) if isinstance(m.permissions, list) else m.permissions or ""
            table.add_row(m.name, m.summary or "", str(bool(m.enabled)), perms, m.source_path or "")
        # 冲突/错误摘要提示
        summary = self.registry.summary()
        if summary:
            self.app.notify(summary, severity="warning")

    def _update_selected_list(self) -> None:
        """刷新右侧已选择技能的展示文本"""
        try:
            txt = "已选择：" + (", ".join(sorted(self._selected_names)) if self._selected_names else "<无>")
            self.query_one("#selected-list", Static).update(txt)
        except Exception:
            pass

    def _iter_filtered_metas(self):
        text = (self._filter_text or "").strip().lower()
        if not text:
            for m in self._metas:
                yield m
            return
        for m in self._metas:
            hay = " ".join([
                m.name or "",
                m.summary or "",
                " ".join(m.triggers or []),
            ]).lower()
            if text in hay:
                yield m

    @on(Input.Changed)
    def on_search_changed(self, event: Input.Changed) -> None:
        if event.input.id != "skill-search":
            return
        self._filter_text = event.value or ""
        self._build_and_fill()

    @on(Button.Pressed)
    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "reload":
            self.registry.reload()
            self._build_and_fill()
            self.app.notify("技能索引已重建", severity="success")
        elif event.button.id == "new":
            self.logger.debug(f"[skills] 打开新增技能编辑器，默认目录={DEFAULT_USER_SKILLS_DIR}")
            try:
                editor = SkillEditorModal(mode="create")
                self.app.push_screen(editor)
            except Exception as e:
                self.app.notify(f"打开新增技能编辑器失败: {e}", severity="error")
        elif event.button.id == "edit":
            name = self._selected_name
            if not name:
                self.app.notify("请先在列表中选择一个技能", severity="warning")
                return
            try:
                skill = load_skill_by_name(name)
            except Exception as e:
                self.app.notify(f"加载技能失败: {e}", severity="error")
                return
            if not skill:
                self.app.notify(f"未找到技能: {name}", severity="warning")
                return
            meta = skill.meta
            desc = skill.description or ""
            self.logger.debug(f"[skills] 打开编辑技能：name={meta.name} path={meta.source_path}")
            try:
                editor = SkillEditorModal(mode="edit", preset_meta=meta, preset_desc=desc)
                self.app.push_screen(editor)
            except Exception as e:
                self.app.notify(f"打开编辑器失败: {e}", severity="error")
        elif event.button.id == "add":
            name = self._selected_name
            if not name:
                self.app.notify("请先在列表中选择一个技能", severity="warning")
                return
            self._selected_names.add(name)
            self._update_selected_list()
            self.app.notify(f"已添加到选择：{name}", severity="success")
        elif event.button.id == "choose":
            name = self._selected_name
            if not name:
                self.app.notify("请先在列表中选择一个技能", severity="warning")
                return
            try:
                skill = load_skill_by_name(name)
            except Exception as e:
                self.app.notify(f"加载技能失败: {e}", severity="error")
                return
            if not skill:
                self.app.notify(f"未找到技能: {name}", severity="warning")
                return
            self.post_message(SkillsBrowserModal.SkillChosen(name, skill))
            self.dismiss()
        elif event.button.id == "start":
            names = list(sorted(self._selected_names))
            if not names and self._selected_name:
                names = [self._selected_name]
            if not names:
                self.app.notify("请先选择至少一个技能", severity="warning")
                return
            skills = []
            try:
                for n in names:
                    s = load_skill_by_name(n)
                    if s:
                        skills.append(s)
                if not skills:
                    self.app.notify("未能加载任何技能", severity="error")
                    return
                self.post_message(SkillsBrowserModal.SkillsChosenMulti(names, skills))
                self.dismiss()
            except Exception as e:
                self.app.notify(f"加载技能失败: {e}", severity="error")
        elif event.button.id == "close":
            self.dismiss()

    @on(DataTable.RowSelected)
    def on_row_selected(self, event: DataTable.RowSelected) -> None:
        table = self.query_one("#skill-table", DataTable)
        row = table.get_row(event.row_key)
        if not row:
            return
        name = row[0]
        self._selected_name = name
        # 懒加载描述
        try:
            skill = load_skill_by_name(name)
        except Exception as e:
            self.app.notify(f"加载技能失败: {e}", severity="error")
            return
        if not skill:
            self.app.notify(f"未找到技能: {name}", severity="warning")
            return
        desc = skill.description or "(无描述)"
        self.query_one("#skill-desc", Markdown).update(desc)
        # 显示附加元信息
        meta_lines = []
        meta = skill.meta
        meta_lines.append(f"permissions: {', '.join(meta.permissions or []) if meta.permissions else '(未设置)'}")
        meta_lines.append(f"tools_whitelist: {', '.join(meta.tools_whitelist or []) if meta.tools_whitelist else '(未设置)'}")
        self.query_one("#skill-meta", Static).update("\n".join(meta_lines))
        # 更新已选列表显示（不自动添加，仅刷新展示）
        self._update_selected_list()

    @on(SkillEditorModal.SkillSaved)
    def on_skill_saved(self, message: SkillEditorModal.SkillSaved) -> None:
        try:
            nm = getattr(message, "name", "")
            path = getattr(message, "path", "")
            self.logger.debug(f"[skills] 保存技能完成：name={nm} path={path}")
        except Exception:
            pass
        self.registry.reload()
        self._build_and_fill()
        try:
            self.app.notify("技能已保存并刷新列表", severity="success")
        except Exception:
            pass
