"""
Task Detail Dialog – displays task info, config, logs, notes, env vars.
"""
import os
import json
from nicegui import ui
from typing import Dict, Any

from pyruns._config import CONFIG_FILENAME
from pyruns.utils.task_io import load_task_info, save_task_info
from pyruns.utils.task_io import get_log_options, resolve_log_path
from pyruns.ui.theme import STATUS_ICONS
from pyruns.ui.widgets import status_badge, readonly_code_viewer
from pyruns.ui.components.env_editor import env_var_editor

# ── Drag JS for movable dialog ──
_DRAG_JS = """(e) => {
    if (e.target.closest('button') || e.target.closest('.q-tab') || e.target.closest('input')) return;
    const card = e.currentTarget.closest('.q-card');
    if (!card) return;
    e.preventDefault();
    const startX = e.clientX, startY = e.clientY;
    const ox = parseFloat(card.dataset.dragX || 0);
    const oy = parseFloat(card.dataset.dragY || 0);
    const onMove = (ev) => {
        const dx = ev.clientX - startX + ox;
        const dy = ev.clientY - startY + oy;
        card.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
        card.dataset.dragX = dx;
        card.dataset.dragY = dy;
    };
    const onUp = () => {
        document.removeEventListener('mousemove', onMove);
        document.removeEventListener('mouseup', onUp);
    };
    document.addEventListener('mousemove', onMove);
    document.addEventListener('mouseup', onUp);
}"""


def build_task_dialog(selected: dict, state: Dict[str, Any], task_manager):
    """
    Build the task detail dialog (once, at page init).
    Stores the dialog and open function into `selected["_dialog"]` and `selected["_open_fn"]`.
    """
    with ui.dialog().props("no-backdrop-dismiss") as task_dialog:
        with ui.card().classes(
            "task-detail-card w-[1100px] max-w-[92vw] "
            "p-0 flex flex-col overflow-hidden shadow-2xl"
        ).style("height: 80vh; max-height: 80vh;"):

            @ui.refreshable
            def dialog_body():
                t = selected.get("task")
                if not t:
                    ui.label("No task selected.").classes("p-8 text-slate-400 text-center")
                    return

                info_obj = load_task_info(t["dir"])
                status = t.get("status", "pending")
                icon_name = STATUS_ICONS.get(status, "help")

                # ── Dialog header (draggable) ──
                header = ui.row().classes(
                    "w-full bg-gradient-to-r from-slate-800 to-slate-900 "
                    "text-white px-6 py-3 items-center justify-between "
                    "cursor-move select-none flex-none"
                )
                header.on("mousedown", js_handler=_DRAG_JS)

                with header:
                    with ui.row().classes("items-center gap-3"):
                        ui.icon(icon_name, size="22px", color="white")
                        ui.label(t["name"]).classes("font-bold text-lg tracking-tight")
                        ui.icon("drag_indicator", size="16px").classes("text-white/30 ml-1")
                    with ui.row().classes("items-center gap-3"):
                        status_badge(status, size="md")
                        ui.button(icon="close", on_click=task_dialog.close).props(
                            "flat round dense color=white"
                        ).classes("opacity-70 hover:opacity-100")

                # ── Tabs ──
                tab_value = selected.get("tab", "task_info")
                with ui.tabs(value=tab_value).classes(
                    "w-full bg-slate-50 border-b border-slate-200 flex-none"
                ) as tabs:
                    ui.tab("task_info", label="Task Info", icon="info")
                    ui.tab("config", label="Config", icon="tune")
                    ui.tab("run.log", label="Run Log", icon="terminal")
                    ui.tab("notes", label="Notes", icon="edit_note")
                    ui.tab("env", label="Env Vars", icon="vpn_key")

                with ui.tab_panels(tabs, value=tab_value).classes(
                    "w-full flex-grow overflow-hidden"
                ).style("padding: 0; min-height: 0;"):

                    _build_tab_task_info(info_obj)
                    _build_tab_config(t)
                    _build_tab_run_log(t)
                    _build_tab_notes(t, info_obj)
                    _build_tab_env_vars(t, info_obj)

            dialog_body()

    def open_task_dialog(task, tab="task_info"):
        selected["task"] = task
        selected["tab"] = tab
        dialog_body.refresh()
        task_dialog.open()
        ui.run_javascript("""
            const card = document.querySelector('.task-detail-card');
            if (card) {
                card.style.transform = '';
                card.dataset.dragX = 0;
                card.dataset.dragY = 0;
            }
        """)

    selected["_dialog"] = task_dialog
    selected["_open_fn"] = open_task_dialog


# ═══════════════════════════════════════════════════════════════
#  Individual tab builders
# ═══════════════════════════════════════════════════════════════

def _build_tab_task_info(info_obj):
    """Task Info tab — readonly JSON viewer."""
    info_text = (
        json.dumps(info_obj, indent=2, ensure_ascii=False)
        if info_obj else "No task_info.json"
    )
    with ui.tab_panel("task_info"):
        readonly_code_viewer(info_text, mode="json")


def _build_tab_config(t):
    """Config tab — readonly YAML viewer (raw file content, preserves original formatting)."""
    cfg_path = os.path.join(t["dir"], CONFIG_FILENAME)
    try:
        with open(cfg_path, "r", encoding="utf-8") as f:
            cfg_text = f.read().strip() or "# Empty config"
    except Exception:
        cfg_text = "# No config.yaml"

    with ui.tab_panel("config"):
        readonly_code_viewer(cfg_text, mode="yaml")


def _build_tab_run_log(t):
    """Run Log tab — log viewer with dropdown for run1.log, run2.log, etc."""
    with ui.tab_panel("run.log"):
        log_options = get_log_options(t["dir"])
        log_names = list(log_options.keys())

        if not log_names:
            with ui.column().classes("w-full h-full items-center justify-center"):
                ui.icon("article", size="48px").classes("text-slate-200")
                ui.label("No log files found.").classes("text-slate-400 mt-2")
        else:
            # Resolve best log (by mtime)
            best_path = resolve_log_path(t["dir"])
            # Find the key (filename) for this path
            initial_name = next((n for n, p in log_options.items() if p == best_path), log_names[-1])
            initial_path = log_options[initial_name]

            # Forward reference: dropdown callback needs to update CodeMirror
            log_cm_ref = [None]

            def _switch_log(e):
                cm = log_cm_ref[0]
                if not cm:
                    return
                path = log_options.get(e.value, "")
                try:
                    with open(path, "r", encoding="utf-8", errors="replace") as lf:
                        cm.value = lf.read()
                except Exception:
                    cm.value = f"Cannot read {e.value}"

            # Toolbar
            with ui.row().classes(
                "w-full items-center gap-3 px-4 py-1.5 flex-none "
                "bg-gradient-to-r from-slate-800 to-slate-900 "
                "border-b border-slate-700"
            ):
                ui.icon("terminal", size="16px").classes("text-emerald-400")
                ui.label("Log Viewer").classes(
                    "text-xs font-bold text-slate-300 tracking-wide"
                )
                ui.space()
                if len(log_names) > 1:
                    ui.select(
                        log_names, value=initial_name,
                        on_change=_switch_log,
                    ).props("outlined dense dark").classes("w-48")
                else:
                    ui.label(initial_name).classes("text-xs text-slate-400 font-mono")

            # Read initial content
            try:
                with open(initial_path, "r", encoding="utf-8", errors="replace") as lf:
                    initial_content = lf.read()
            except Exception:
                initial_content = f"Cannot read {initial_name}"

            # CodeMirror — flex fills remaining space
            log_cm_ref[0] = ui.codemirror(
                value=initial_content, language=None, theme="vscodeDark",
                line_wrapping=True,
            ).classes("w-full readonly-cm").style(
                "flex:1 1 0; min-height:0; overflow:hidden;"
            )


def _build_tab_notes(t, info_obj):
    """Notes tab — editable textarea with save button."""
    # 1. 在 tab_panel 层级去除 padding (p-0) 和间距 (gap-0)
    # 使用 flex-col 让其垂直排列，h-full 撑满高度
    with ui.tab_panel("notes").classes("p-0 gap-0 flex flex-col h-full w-full"):
        
        notes_val = info_obj.get("notes", "") if isinstance(info_obj, dict) else ""
        # 使用字典来存储引用，以便回调函数能访问到最新的值
        notes_holder = {"text": notes_val} 

        # ── Toolbar ──
        # 保持原有逻辑，border-b 稍微保留一点分割线感觉，或者你可以去掉
        with ui.row().classes(
            "w-full items-center justify-between px-5 py-2 flex-none "
            "bg-gradient-to-r from-indigo-50 to-slate-50 "
            "border-b border-indigo-100"
        ):
            with ui.row().classes("items-center gap-2"):
                ui.icon("edit_note", size="18px").classes("text-indigo-500")
                ui.label("Task Notes & Description").classes(
                    "text-sm font-bold text-slate-700 tracking-wide"
                )

            def save_notes():
                info = load_task_info(t["dir"])
                info["notes"] = notes_holder["text"]
                save_task_info(t["dir"], info)
                ui.notify("Notes saved", type="positive", icon="check")

            ui.button("Save", icon="save", on_click=save_notes).props(
                "unelevated dense no-caps size=sm"
            ).classes("bg-indigo-600 text-white px-4")

        # ── Textarea ──
        # 2. 这里的容器去掉 padding (p-0)，背景设为白色
        with ui.column().classes("w-full flex-grow p-0 m-0 bg-white overflow-hidden"):
            notes_input = ui.textarea(
                value=notes_val,
                placeholder="Record experiment results, parameter notes, observations...",
            ).props(
                # 3. 关键修改：
                # borderless: 去掉输入框自带的边框
                # full-width: 宽度铺满
                # no-resize: 禁止拖拽右下角
                "borderless full-width no-resize"
            ).classes(
                # 给文字一些内边距 (px-4 py-3)，否则文字会贴着边缘
                "w-full h-full font-mono text-sm px-5 py-4 outline-none focus:outline-none"
            ).style(
                "line-height: 1.6; height: 100%;"
            )
            
            # 绑定数据更新
            notes_input.on_value_change(
                lambda e: notes_holder.update({"text": e.value})
            )


def _build_tab_env_vars(t, info_obj):
    """Env Vars tab — key/value editor."""
    with ui.tab_panel("env"):
        env = info_obj.get("env", {}) if isinstance(info_obj, dict) else {}
        rows = [{"key": k, "val": v} for k, v in env.items()]

        def on_save_env(new_env):
            info = load_task_info(t["dir"])
            info["env"] = new_env
            save_task_info(t["dir"], info)
            t["env"] = new_env
            ui.notify("Env vars saved", type="positive", icon="check")

        env_var_editor(rows, on_save=on_save_env)

