import React from "react";
import { fmtClock, fmtDate, fmtDateTimeLocalInput, localDateTimeInputToDisplayIso } from "./time";
import { LoadingTitle } from "./loading-indicator";
import { errorMessage, requestJson } from "./unidesk-error";
import { UniDeskErrorBanner } from "./unidesk-error-banner";

type AnyRecord = Record<string, any>;
type ReactNode = any;

const h = React.createElement;
const { useEffect } = React;
const useState: any = React.useState;

function StatusBadge({ status, children }: AnyRecord) {
  const normalized = String(status || "unknown").toLowerCase();
  return h("span", { className: `status-badge ${normalized}` }, children || status || "unknown");
}

function MetricCard({ label, value, hint, tone }: AnyRecord) {
  return h("article", { className: `metric-card ${tone || ""}` },
    h("div", { className: "metric-label" }, label),
    h("div", { className: "metric-value" }, value),
    h("div", { className: "metric-hint" }, hint),
  );
}

function Panel({ title, eyebrow, actions, children, className, loading }: AnyRecord) {
  return h("section", { className: `panel ${className || ""}` },
    h("div", { className: "panel-head" },
      h("div", null,
        eyebrow ? h("p", { className: "panel-eyebrow" }, eyebrow) : null,
        h(LoadingTitle, { title, loading }),
      ),
      actions ? h("div", { className: "panel-actions" }, actions) : null,
    ),
    h("div", { className: "panel-body" }, children),
  );
}

function RawButton({ title, data, onOpen, testId }: AnyRecord) {
  return h("button", {
    type: "button",
    className: "ghost-btn",
    "data-testid": testId,
    onClick: () => onOpen(title, data),
  }, "查看原始JSON");
}

function EmptyState({ title, text }: AnyRecord) {
  return h("div", { className: "empty-state" }, h("strong", null, title), h("span", null, text));
}

function microserviceRuntime(service: any): AnyRecord {
  return service?.runtime && typeof service.runtime === "object" && !Array.isArray(service.runtime) ? service.runtime : {};
}

function microserviceBackend(service: any): AnyRecord {
  return service?.backend && typeof service.backend === "object" && !Array.isArray(service.backend) ? service.backend : {};
}

function microserviceRepository(service: any): AnyRecord {
  return service?.repository && typeof service.repository === "object" && !Array.isArray(service.repository) ? service.repository : {};
}

function safeId(value: any): string {
  return String(value).replace(/[^a-zA-Z0-9_-]/g, "_");
}

function fmtPercent(value: number): string {
  if (!Number.isFinite(value)) return "--";
  return `${value.toFixed(1)}%`;
}

function todoApi(apiBaseUrl: string, path: string): string {
  return `${apiBaseUrl}/microservices/todo-note/proxy${path}`;
}

function todoCount(nodes: any[]): { total: number; completed: number; active: number } {
  return nodes.reduce((memo, node: any) => {
    const nested = todoCount(Array.isArray(node.children) ? node.children : []);
    const done = Boolean(node.completed);
    return {
      total: memo.total + 1 + nested.total,
      completed: memo.completed + (done ? 1 : 0) + nested.completed,
      active: memo.active + (done ? 0 : 1) + nested.active,
    };
  }, { total: 0, completed: 0, active: 0 });
}

function todoVisible(todo: any, filter: string): boolean {
  const selfVisible = filter === "all" || (filter === "completed" ? Boolean(todo.completed) : !todo.completed);
  const children = Array.isArray(todo.children) ? todo.children : [];
  return selfVisible || children.some((child: any) => todoVisible(child, filter));
}

function todoRegistryRows(registry: any): any[] {
  return Array.isArray(registry?.instances) ? registry.instances : [];
}

function childTodosForParent(nodes: any[], parentId: string): any[] {
  for (const node of nodes) {
    if (node?.id === parentId) return Array.isArray(node.children) ? node.children : [];
    const nested = childTodosForParent(Array.isArray(node?.children) ? node.children : [], parentId);
    if (nested.length > 0) return nested;
  }
  return [];
}

export function TodoNotePage({ microservices, onRaw, apiBaseUrl = "/api" }: AnyRecord) {
  const service = microservices.find((item: any) => item.id === "todo-note") || null;
  const [health, setHealth] = useState(null);
  const [registry, setRegistry] = useState(null);
  const [activeId, setActiveId] = useState("");
  const [instance, setInstance] = useState(null);
  const [filter, setFilter] = useState("all");
  const [fontSize, setFontSize] = useState(13);
  const [newListName, setNewListName] = useState("");
  const [newTodoTitle, setNewTodoTitle] = useState("");
  const [editingId, setEditingId] = useState("");
  const [editingTitle, setEditingTitle] = useState("");
  const [dragTodoId, setDragTodoId] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [refreshedAt, setRefreshedAt] = useState(null);

  const rows = todoRegistryRows(registry);
  const counts = todoCount(Array.isArray(instance?.todos) ? instance.todos : []);
  const runtime = service ? microserviceRuntime(service) : {};
  const repository = service ? microserviceRepository(service) : {};
  const backend = service ? microserviceBackend(service) : {};

  async function loadRegistry(preferId = activeId): Promise<string> {
    const [healthResult, registryResult] = await Promise.all([
      requestJson(`${apiBaseUrl}/microservices/todo-note/health`),
      requestJson(todoApi(apiBaseUrl, "/api/instances")),
    ]);
    setHealth(healthResult);
    setRegistry(registryResult);
    const nextRows = todoRegistryRows(registryResult);
    const nextId = nextRows.some((row: any) => row.id === preferId) ? preferId : nextRows[0]?.id || "";
    setActiveId(nextId);
    return nextId;
  }

  async function loadInstance(instanceId = activeId): Promise<void> {
    if (!instanceId) {
      setInstance(null);
      return;
    }
    const result = await requestJson(todoApi(apiBaseUrl, `/api/instances/${encodeURIComponent(instanceId)}`));
    setInstance(result);
  }

  async function refresh(preferId = activeId): Promise<void> {
    if (!service) return;
    setLoading(true);
    setError("");
    try {
      const nextId = await loadRegistry(preferId);
      await loadInstance(nextId);
      setRefreshedAt(new Date());
    } catch (err) {
      setError(errorMessage(err, "Todo Note 加载失败"));
    } finally {
      setLoading(false);
    }
  }

  async function applyTodoAction(action: AnyRecord): Promise<any | null> {
    if (!activeId) return null;
    setError("");
    try {
      const updated = await requestJson(todoApi(apiBaseUrl, `/api/instances/${encodeURIComponent(activeId)}/actions`), {
        method: "POST",
        body: JSON.stringify({ action }),
      });
      setInstance(updated);
      await loadRegistry(activeId);
      return updated;
    } catch (err) {
      setError(errorMessage(err, "Todo 操作失败"));
      return null;
    }
  }

  async function createList(event: any): Promise<void> {
    event.preventDefault();
    const name = newListName.trim();
    if (!name) return;
    setLoading(true);
    setError("");
    try {
      const created = await requestJson(todoApi(apiBaseUrl, "/api/instances"), { method: "POST", body: JSON.stringify({ name }) });
      setNewListName("");
      await refresh(created.id);
    } catch (err) {
      setError(errorMessage(err, "创建清单失败"));
    } finally {
      setLoading(false);
    }
  }

  async function deleteList(instanceId: string): Promise<void> {
    if (!window.confirm("确认删除这个 Todo Note 清单？")) return;
    setLoading(true);
    setError("");
    try {
      await requestJson(todoApi(apiBaseUrl, `/api/instances/${encodeURIComponent(instanceId)}`), { method: "DELETE" });
      await refresh(activeId === instanceId ? "" : activeId);
    } catch (err) {
      setError(errorMessage(err, "删除清单失败"));
    } finally {
      setLoading(false);
    }
  }

  async function addRootTodo(event: any): Promise<void> {
    event.preventDefault();
    const title = newTodoTitle.trim();
    if (!title) return;
    setNewTodoTitle("");
    await applyTodoAction({ type: "addTodo", title });
  }

  async function undoRedo(kind: "undo" | "redo"): Promise<void> {
    if (!activeId) return;
    setError("");
    try {
      const updated = await requestJson(todoApi(apiBaseUrl, `/api/instances/${encodeURIComponent(activeId)}/${kind}`), { method: "POST", body: JSON.stringify({}) });
      setInstance(updated);
      await loadRegistry(activeId);
    } catch (err) {
      setError(errorMessage(err, `${kind} 失败`));
    }
  }

  function beginEdit(todo: any): void {
    setEditingId(todo.id);
    setEditingTitle(String(todo.title || ""));
  }

  async function saveEdit(todoId: string): Promise<void> {
    const title = editingTitle.trim();
    setEditingId("");
    setEditingTitle("");
    if (title) await applyTodoAction({ type: "updateTodoTitle", todoId, title });
  }

  async function addChild(todoId: string): Promise<void> {
    const title = window.prompt("新增子任务标题");
    const trimmed = title?.trim();
    if (!trimmed) return;

    const beforeChildren = childTodosForParent(Array.isArray(instance?.todos) ? instance.todos : [], todoId);
    const beforeIds = new Set(beforeChildren.map((child: any) => child.id));
    const updated = await applyTodoAction({ type: "addTodo", title: trimmed, parentId: todoId, targetIndex: 0 });
    if (!updated) return;

    const afterChildren = childTodosForParent(Array.isArray(updated?.todos) ? updated.todos : [], todoId);
    const newChild = afterChildren.find((child: any) => !beforeIds.has(child.id));
    if (newChild && afterChildren[0]?.id !== newChild.id) {
      await applyTodoAction({ type: "moveTodo", todoId: newChild.id, targetParentId: todoId, targetIndex: 0 });
    }
  }

  async function dropTodo(targetParentId: string | null, targetIndex: number): Promise<void> {
    if (!dragTodoId) return;
    const payload: AnyRecord = { type: "moveTodo", todoId: dragTodoId, targetIndex };
    if (targetParentId) payload.targetParentId = targetParentId;
    setDragTodoId("");
    await applyTodoAction(payload);
  }

  useEffect(() => {
    refresh();
  }, [service?.id, service?.runtime?.providerStatus]);

  if (!service) return h(EmptyState, { title: "Todo Note 未登记", text: "请在 config.json 的 microservices 中登记用户服务 id=todo-note" });

  const activeSummary = rows.find((row: any) => row.id === activeId) || null;
  const rootTodos = Array.isArray(instance?.todos) ? instance.todos : [];
  const visibleRootTodos = rootTodos
    .map((todo: any, index: number) => ({ todo, index }))
    .filter((item: AnyRecord) => todoVisible(item.todo, filter));
  return h("div", { className: "todo-note-page", "data-testid": "todo-note-page" },
    h(Panel, {
      title: "Todo Note 工作台",
      eyebrow: "Main Server 用户服务",
      loading,
      actions: h("div", { className: "panel-actions" },
        h("button", { type: "button", className: "ghost-btn", disabled: loading, onClick: () => refresh(activeId), "data-testid": "todo-note-refresh-button" }, loading ? "刷新中" : "刷新"),
        h(RawButton, { title: "Todo Note 用户服务", data: service, onOpen: onRaw, testId: "raw-todo-note-service" }),
      ),
    },
      h("div", { className: "todo-note-hero" },
        h("div", null,
          h("div", { className: "node-version-line" },
            h(StatusBadge, { status: runtime.providerStatus === "online" ? "online" : "warn" }, runtime.providerStatus || "unknown"),
            h("span", null, service.providerId),
            h("span", null, backend.public ? "公网暴露" : "仅 UniDesk frontend 代理访问"),
            h("span", null, health?.ok ? "Health OK" : "Health --"),
          ),
          h("p", { className: "muted paragraph" }, service.description),
        ),
        h("div", { className: "microservice-ref-card" }, h("span", null, "Repo"), h("strong", null, repository.url || "--"), h("code", null, repository.commitId || "--")),
        h("div", { className: "microservice-ref-card" }, h("span", null, "Main Server Docker"), h("strong", null, `${backend.nodeBindHost || "--"}:${backend.nodePort || "--"}`), h("code", null, `${repository.composeService || "--"} / ${repository.containerName || "--"}`)),
      ),
      h(UniDeskErrorBanner, { error, wide: true }),
    ),
    h("div", { className: "todo-note-layout" },
      h(Panel, { title: "清单", eyebrow: `${rows.length} Instances`, className: "todo-list-panel", loading },
        h("form", { className: "todo-create-list", onSubmit: createList },
          h("input", { placeholder: "新清单名称", value: newListName, onChange: (event: any) => setNewListName(event.target.value), "aria-label": "新清单名称" }),
          h("button", { type: "submit", className: "ghost-btn", disabled: loading || !newListName.trim() }, "创建"),
        ),
        rows.length === 0 ? h(EmptyState, { title: "暂无清单", text: "迁移或创建清单后会出现在这里" }) :
          h("div", { className: "todo-instance-list" }, rows.map((row: any) => h("button", {
            key: row.id,
            type: "button",
            className: `todo-instance-row ${activeId === row.id ? "active" : ""}`,
            onClick: () => { setActiveId(row.id); loadInstance(row.id); },
            "data-testid": `todo-instance-${safeId(row.id)}`,
          },
            h("strong", null, row.name),
            h("span", null, `${row.completedCount ?? 0}/${row.todoCount ?? 0} 完成`),
            h("code", null, row.id),
          ))),
      ),
      h("div", { className: "todo-main-stack" },
        h(Panel, {
          title: activeSummary?.name || "待选择清单",
          eyebrow: refreshedAt ? `Updated ${fmtClock(refreshedAt)}` : "Todo Tree",
          loading,
          actions: instance ? h("div", { className: "panel-actions" },
            h("button", { type: "button", className: "ghost-btn", onClick: () => applyTodoAction({ type: "renameInstance", name: window.prompt("清单新名称", instance.name) || instance.name }) }, "重命名"),
            h("button", { type: "button", className: "ghost-btn danger", onClick: () => deleteList(activeId) }, "删除清单"),
            h(RawButton, { title: `Todo Instance ${activeId}`, data: instance, onOpen: onRaw, testId: "raw-todo-instance" }),
          ) : null,
        },
          !instance ? h(EmptyState, { title: "未选择清单", text: "左侧选择一个 Todo Note 清单" }) :
            h("div", { className: "todo-workbench", style: { "--todo-font-size": `${fontSize}px` } },
              h("div", { className: "todo-toolbar" },
                h("form", { className: "todo-add-form", onSubmit: addRootTodo },
                  h("input", { placeholder: "新增根任务", value: newTodoTitle, onChange: (event: any) => setNewTodoTitle(event.target.value), "aria-label": "新增根任务" }),
                  h("button", { type: "submit", className: "ghost-btn", disabled: !newTodoTitle.trim() }, "新增"),
                ),
                h("div", { className: "todo-filter-strip" }, ["all", "active", "completed"].map((item) => h("button", {
                  key: item,
                  type: "button",
                  className: `todo-filter ${filter === item ? "active" : ""}`,
                  onClick: () => setFilter(item),
                }, item === "all" ? "全部" : item === "active" ? "未完成" : "已完成"))),
                h("div", { className: "todo-toolbar-actions" },
                  h("button", { type: "button", className: "ghost-btn", onClick: () => applyTodoAction({ type: "setAllTodosExpanded", expanded: true }) }, "全部展开"),
                  h("button", { type: "button", className: "ghost-btn", onClick: () => applyTodoAction({ type: "setAllTodosExpanded", expanded: false }) }, "全部收起"),
                  h("button", { type: "button", className: "ghost-btn", onClick: () => undoRedo("undo") }, "撤销"),
                  h("button", { type: "button", className: "ghost-btn", onClick: () => undoRedo("redo") }, "重做"),
                  h("label", { className: "todo-font-control" }, "字号", h("input", { type: "range", min: 11, max: 18, value: fontSize, onChange: (event: any) => setFontSize(Number(event.target.value)) })),
                ),
              ),
              h("div", { className: "todo-stats-grid" },
                h(MetricCard, { label: "总任务", value: counts.total, hint: `${rows.length} lists` }),
                h(MetricCard, { label: "已完成", value: counts.completed, hint: `${fmtPercent(counts.total ? counts.completed / counts.total * 100 : 0)}`, tone: "ok" }),
                h(MetricCard, { label: "未完成", value: counts.active, hint: filter === "active" ? "当前筛选" : "active tasks", tone: counts.active > 0 ? "warn" : "ok" }),
                h(MetricCard, { label: "历史指针", value: instance.historyPointer ?? 0, hint: "undo / redo" }),
              ),
              h("div", {
                className: "todo-root-drop",
                onDragOver: (event: any) => event.preventDefault(),
                onDrop: (event: any) => { event.preventDefault(); dropTodo(null, rootTodos.length); },
              }, "拖到这里可移为根任务末尾"),
              h("div", { className: "todo-tree", "data-testid": "todo-note-tree" },
                visibleRootTodos.length === 0
                  ? h(EmptyState, { title: "没有匹配任务", text: "调整筛选或新增任务" })
                  : visibleRootTodos.map(({ todo, index }: AnyRecord) => h(TodoRow, {
                    key: todo.id,
                    todo,
                    depth: 0,
                    parentId: null,
                    index,
                    siblingCount: rootTodos.length,
                    filter,
                    editingId,
                    editingTitle,
                    setEditingTitle,
                    beginEdit,
                    saveEdit,
                    applyTodoAction,
                    addChild,
                    dragTodoId,
                    setDragTodoId,
                    dropTodo,
                  })),
              ),
            ),
        ),
      ),
    ),
  );
}

function TodoRow(props: AnyRecord): ReactNode {
  const { todo, depth, parentId, index, siblingCount, filter, editingId, editingTitle, setEditingTitle, beginEdit, saveEdit, applyTodoAction, addChild, dragTodoId, setDragTodoId, dropTodo } = props;
  const children = Array.isArray(todo.children) ? todo.children : [];
  const visibleChildren = children
    .map((child: any, childIndex: number) => ({ child, childIndex }))
    .filter((item: AnyRecord) => todoVisible(item.child, filter));
  const isEditing = editingId === todo.id;
  const targetParentId = parentId || null;
  return h("div", { className: "todo-row-wrap" },
    h("article", {
      className: `todo-row ${todo.completed ? "completed" : ""} ${dragTodoId === todo.id ? "dragging" : ""}`,
      style: { "--todo-depth": depth },
      draggable: true,
      onDragStart: (event: any) => { setDragTodoId(todo.id); event.dataTransfer.effectAllowed = "move"; },
      onDragOver: (event: any) => event.preventDefault(),
      onDrop: (event: any) => { event.preventDefault(); dropTodo(todo.id, children.length); },
      "data-testid": `todo-row-${safeId(todo.id)}`,
    },
      h("button", { type: "button", className: "todo-expand", disabled: children.length === 0, onClick: () => applyTodoAction({ type: "toggleTodoExpanded", todoId: todo.id }) }, children.length === 0 ? "·" : todo.expanded ? "▾" : "▸"),
      h("input", { type: "checkbox", checked: Boolean(todo.completed), onChange: () => applyTodoAction({ type: "toggleTodoCompleted", todoId: todo.id }), "aria-label": `完成 ${todo.title}` }),
      h("div", { className: "todo-title-cell", onDoubleClick: () => beginEdit(todo) },
        isEditing ? h("div", { className: "todo-edit-inline" },
          h("input", {
            value: editingTitle,
            autoFocus: true,
            onChange: (event: any) => setEditingTitle(event.target.value),
            onKeyDown: (event: any) => {
              if (event.key === "Enter") saveEdit(todo.id);
              if (event.key === "Escape") beginEdit({ id: "", title: "" });
            },
          }),
          h("button", { type: "button", className: "ghost-btn", onClick: () => saveEdit(todo.id) }, "保存"),
        ) : h("strong", null, todo.title || "Untitled"),
        h("div", { className: "todo-meta-line" },
          h("span", null, `子项 ${children.length}`),
          h("span", null, `更新 ${fmtDate(todo.updatedAt)}`),
          todo.reminderAt ? h("span", { className: "todo-reminder" }, `提醒 ${fmtDate(todo.reminderAt)}`) : h("span", null, "无提醒"),
        ),
      ),
      h("input", { className: "todo-reminder-input", type: "datetime-local", value: fmtDateTimeLocalInput(todo.reminderAt), onChange: (event: any) => applyTodoAction({ type: "setTodoReminder", todoId: todo.id, reminderAt: localDateTimeInputToDisplayIso(event.target.value) }) }),
      h("div", { className: "todo-row-actions" },
        h("button", { type: "button", className: "ghost-btn", onClick: () => beginEdit(todo) }, "编辑"),
        h("button", { type: "button", className: "ghost-btn", onClick: () => addChild(todo.id) }, "子项"),
        h("button", { type: "button", className: "ghost-btn", disabled: index <= 0, onClick: () => applyTodoAction({ type: "moveTodo", todoId: todo.id, ...(targetParentId ? { targetParentId } : {}), targetIndex: index - 1 }) }, "上移"),
        h("button", { type: "button", className: "ghost-btn", disabled: index <= 0, onClick: () => applyTodoAction({ type: "moveTodo", todoId: todo.id, ...(targetParentId ? { targetParentId } : {}), targetIndex: 0 }) }, "置顶"),
        h("button", { type: "button", className: "ghost-btn", disabled: index >= siblingCount - 1, onClick: () => applyTodoAction({ type: "moveTodo", todoId: todo.id, ...(targetParentId ? { targetParentId } : {}), targetIndex: index + 1 }) }, "下移"),
        h("button", { type: "button", className: "ghost-btn", disabled: !parentId, onClick: () => applyTodoAction({ type: "moveTodo", todoId: todo.id, targetIndex: 9999 }) }, "提升"),
        h("button", { type: "button", className: "ghost-btn danger", onClick: () => applyTodoAction({ type: "deleteTodo", todoId: todo.id }) }, "删除"),
      ),
    ),
    todo.expanded && visibleChildren.length > 0 ? h("div", { className: "todo-children" }, visibleChildren.map(({ child, childIndex }: AnyRecord) => h(TodoRow, {
      key: child.id,
      todo: child,
      depth: depth + 1,
      parentId: todo.id,
      index: childIndex,
      siblingCount: children.length,
      filter,
      editingId,
      editingTitle,
      setEditingTitle,
      beginEdit,
      saveEdit,
      applyTodoAction,
      addChild,
      dragTodoId,
      setDragTodoId,
      dropTodo,
    }))) : null,
  );
}
