import {
  ControlledSelector,
  DataSpaceSelector,
  MasterDetailTable,
  PanelContainer,
  useDevtoolsState
} from "./chunk-34BKYKWL.mjs";

// src/panels/edge/WorkflowPanel/WorkflowPanel.tsx
import * as SchemaAST2 from "effect/SchemaAST";
import React2, { useMemo as useMemo2, useState as useState2 } from "react";
import { ComputeGraph, ComputeGraphModel, WorkflowLoader } from "@dxos/conductor";
import { Format } from "@dxos/echo/internal";
import { Filter } from "@dxos/echo-db";
import { DXN as DXN2 } from "@dxos/keys";
import { useQuery } from "@dxos/react-client/echo";
import { Toolbar as Toolbar2 } from "@dxos/react-ui";
import { mx as mx2 } from "@dxos/ui-theme";

// src/panels/edge/WorkflowPanel/WorkflowDebugPanel.tsx
import * as Effect from "effect/Effect";
import * as SchemaAST from "effect/SchemaAST";
import React, { forwardRef, useEffect, useMemo, useRef, useState } from "react";
import { ValueBag } from "@dxos/conductor";
import { Database } from "@dxos/echo";
import { EdgeHttpClient } from "@dxos/edge-client";
import { runAndForwardErrors } from "@dxos/effect";
import { createEventLogger } from "@dxos/functions";
import { QueueService } from "@dxos/functions";
import { ServiceContainer } from "@dxos/functions-runtime";
import { RemoteFunctionExecutionService } from "@dxos/functions-runtime";
import { invariant } from "@dxos/invariant";
import { DXN } from "@dxos/keys";
import { LogLevel, log } from "@dxos/log";
import { useConfig } from "@dxos/react-client";
import { Avatar, Input, Toolbar, useAsyncEffect } from "@dxos/react-ui";
import { SyntaxHighlighter } from "@dxos/react-ui-syntax-highlighter";
import { errorText, mx } from "@dxos/ui-theme";
var __dxlog_file = "/__w/dxos/dxos/packages/devtools/devtools/src/panels/edge/WorkflowPanel/WorkflowDebugPanel.tsx";
var WorkflowDebugPanelMode = /* @__PURE__ */ (function(WorkflowDebugPanelMode2) {
  WorkflowDebugPanelMode2["LOCAL"] = "local";
  WorkflowDebugPanelMode2["REMOTE"] = "remote";
  return WorkflowDebugPanelMode2;
})({});
var WorkflowDebugPanel = (props) => {
  const config = useConfig();
  const inputRef = useRef(null);
  const { space } = useDevtoolsState();
  const [input, setInput] = useState("");
  const [isExecuting, setIsExecuting] = useState(false);
  const [history, setHistory] = useState([]);
  const [inputTemplate, setInputTemplate] = useState("");
  const edgeClient = useMemo(() => {
    const edgeUrl = config.values.runtime?.services?.edge?.url;
    invariant(edgeUrl, "Edge url not configured", {
      F: __dxlog_file,
      L: 53,
      S: void 0,
      A: [
        "edgeUrl",
        "'Edge url not configured'"
      ]
    });
    return new EdgeHttpClient(edgeUrl);
  }, [
    config
  ]);
  useAsyncEffect(async () => {
    setInputTemplate("");
    await props.loader.load(DXN.fromLocalObjectId(props.graph.id)).then((workflow) => {
      const workflowMeta = workflow.resolveMeta();
      if (workflowMeta.inputs.length) {
        const inputTemplate2 = inputTemplateFromAst(workflowMeta.inputs[0].schema.ast);
        setInputTemplate(inputTemplate2);
        if (!input.length) {
          setInput(inputTemplate2);
        }
      }
    }).catch(log.catch);
  }, [
    props.loader,
    props.graph.id
  ]);
  const scrollerRef = useRef(null);
  const handleScroll = () => {
    if (scrollerRef.current) {
      scrollerRef.current.scrollTop = scrollerRef.current.scrollHeight;
    }
  };
  const controller = useRef(null);
  useEffect(() => handleStop(), []);
  const handleStop = () => {
    controller.current?.abort("stop");
    controller.current = null;
  };
  const handleClear = () => {
    handleStop();
    setInput("");
    setHistory([]);
    inputRef.current?.focus();
  };
  const handleResponse = ({ text, data, error } = {}) => {
    controller.current = null;
    setHistory((history2) => [
      ...history2,
      {
        type: "response",
        text,
        data,
        error
      }
    ]);
    setIsExecuting(false);
    handleScroll();
  };
  const handleRequest = async (input2) => {
    invariant(space, void 0, {
      F: __dxlog_file,
      L: 112,
      S: void 0,
      A: [
        "space",
        ""
      ]
    });
    try {
      invariant(input2.charAt(0) === "{", "Not a JSON object.", {
        F: __dxlog_file,
        L: 114,
        S: void 0,
        A: [
          "input.charAt(0) === '{'",
          "'Not a JSON object.'"
        ]
      });
      const validJsonString = input2.replace(/'/g, '"').replace(/([{,]\s*)([a-zA-Z0-9_]+)(\s*:)/g, '$1"$2"$3');
      const requestBody = JSON.parse(validJsonString);
      setIsExecuting(true);
      setInput(inputTemplate);
      setHistory((history2) => [
        ...history2,
        {
          type: "request",
          text: JSON.stringify(requestBody, null, 2)
        }
      ]);
      setTimeout(() => handleScroll());
      controller.current = new AbortController();
      let response;
      if (props.mode === "remote") {
        response = await edgeClient.executeWorkflow(space.id, props.graph.id, requestBody);
      } else {
        const compiled = await props.loader.load(DXN.fromLocalObjectId(props.graph.id));
        response = await runAndForwardErrors(compiled.run(ValueBag.make(requestBody)).pipe(Effect.withSpan("runWorkflow"), Effect.flatMap(ValueBag.unwrap), Effect.provide(createLocalExecutionContext(space)), Effect.scoped));
      }
      handleResponse({
        data: response
      });
    } catch (err) {
      if (err !== "stop") {
        const error = err instanceof Error ? err : new Error(err);
        handleResponse({
          error
        });
        log.catch(err, void 0, {
          F: __dxlog_file,
          L: 148,
          S: void 0,
          C: (f, a) => f(...a)
        });
      } else {
        handleResponse();
      }
    } finally {
      setIsExecuting(false);
    }
  };
  return /* @__PURE__ */ React.createElement("div", {
    className: mx("flex flex-col is-full bs-full overflow-hidden", props.classNames)
  }, /* @__PURE__ */ React.createElement(MessageThread, {
    ref: scrollerRef,
    history
  }), /* @__PURE__ */ React.createElement(Toolbar.Root, null, /* @__PURE__ */ React.createElement(Input.Root, null, /* @__PURE__ */ React.createElement(Input.TextInput, {
    ref: inputRef,
    autoFocus: true,
    placeholder: "Input JSON",
    value: input,
    onChange: (ev) => setInput(ev.target.value),
    onKeyDown: (ev) => ev.key === "Enter" && handleRequest(input)
  })), /* @__PURE__ */ React.createElement(Toolbar.IconButton, {
    icon: "ph--play--regular",
    size: 4,
    label: "Execute",
    iconOnly: true,
    onClick: () => handleRequest(input)
  }), /* @__PURE__ */ React.createElement(Toolbar.IconButton, {
    icon: isExecuting ? "ph--stop--regular" : "ph--trash--regular",
    size: 4,
    label: isExecuting ? "Stop" : "Clear",
    iconOnly: true,
    onClick: () => isExecuting ? handleStop() : handleClear()
  })));
};
var MessageThread = /* @__PURE__ */ forwardRef(({ history }, forwardedRef) => {
  if (!history.length) {
    return null;
  }
  return /* @__PURE__ */ React.createElement("div", {
    ref: forwardedRef,
    className: "flex flex-col gap-6 bs-full p-2 overflow-x-hidden overflow-y-auto"
  }, history.map((message, i) => /* @__PURE__ */ React.createElement("div", {
    key: i,
    className: "grid grid-cols-[2rem_1fr_2rem]"
  }, /* @__PURE__ */ React.createElement("div", {
    className: "p-1"
  }, message.type === "response" && /* @__PURE__ */ React.createElement(RobotAvatar, null)), /* @__PURE__ */ React.createElement("div", {
    className: "overflow-auto"
  }, /* @__PURE__ */ React.createElement(MessageItem, {
    message
  })))));
});
var MessageItem = ({ classNames, message }) => {
  const { type, text, data, error } = message;
  const wrapper = "p-1 pli-2 rounded-md bg-hoverSurface overflow-auto";
  return /* @__PURE__ */ React.createElement("div", {
    className: mx("flex", type === "request" ? "ml-[1rem] justify-end" : "mr-[1rem]", classNames)
  }, error && /* @__PURE__ */ React.createElement("div", {
    className: mx(wrapper, "whitespace-pre", errorText)
  }, String(error)), text !== void 0 && /* @__PURE__ */ React.createElement("div", {
    className: mx(wrapper, type === "request" && "bg-primary-400 dark:bg-primary-600")
  }, text || "\xD8"), data && /* @__PURE__ */ React.createElement(SyntaxHighlighter, {
    language: "json",
    className: mx(wrapper, "text-xs")
  }, JSON.stringify(data, null, 2)));
};
var RobotAvatar = () => /* @__PURE__ */ React.createElement(Avatar.Root, null, /* @__PURE__ */ React.createElement(Avatar.Content, {
  size: 6,
  variant: "circle",
  icon: "ph--robot--regular"
}));
var createLocalExecutionContext = (space) => {
  return new ServiceContainer().setServices({
    eventLogger: createEventLogger(LogLevel.INFO),
    database: Database.makeService(space.db),
    queues: QueueService.make(space.queues, void 0),
    functionCallService: RemoteFunctionExecutionService.mock()
  }).createLayer();
};
var inputTemplateFromAst = (ast) => {
  return `{ ${SchemaAST.getPropertySignatures(ast).map((property) => `"${property.name.toString()}": ""`).join(", ")} }`;
};

// src/panels/edge/WorkflowPanel/WorkflowPanel.tsx
var WorkflowPanel = (props) => {
  const state = useDevtoolsState();
  const space = props.space ?? state.space;
  const [displayMode, setDisplayMode] = useState2("compiled");
  const [executionMode, setExecutionMode] = useState2(WorkflowDebugPanelMode.LOCAL);
  const graphs = useQuery(space?.db, Filter.type(ComputeGraph));
  const [selectedId, setSelectedId] = useState2();
  const selected = useMemo2(() => graphs.find((graph) => graph.id === selectedId), [
    graphs,
    selectedId
  ]);
  const loader = useMemo2(() => createLoader(graphs), [
    graphs
  ]);
  const properties = useMemo2(() => [
    {
      name: "id",
      format: Format.TypeFormat.String
    },
    {
      name: "nodes",
      format: Format.TypeFormat.Number,
      size: 100
    },
    {
      name: "edges",
      format: Format.TypeFormat.Number,
      size: 100
    }
  ], []);
  const tableData = useMemo2(() => {
    return graphs.map((graph) => ({
      id: graph.id,
      nodes: graph.graph.nodes.length,
      edges: graph.graph.edges.length,
      _original: graph
    }));
  }, [
    graphs
  ]);
  const detailsTransform = useMemo2(() => {
    return async (data) => {
      const graph = data._original;
      if (!graph) {
        return null;
      }
      try {
        if (displayMode === "compiled") {
          return await toWorkflow(loader, graph);
        } else if (displayMode === "graph") {
          return toCompactGraph(graph);
        } else {
          return graph;
        }
      } catch (err) {
        return {
          error: err.message,
          stack: err.stack
        };
      }
    };
  }, [
    loader,
    displayMode
  ]);
  return /* @__PURE__ */ React2.createElement(PanelContainer, {
    toolbar: /* @__PURE__ */ React2.createElement(Toolbar2.Root, null, !props.space && /* @__PURE__ */ React2.createElement(DataSpaceSelector, null), /* @__PURE__ */ React2.createElement(ControlledSelector, {
      values: Object.values(DisplayMode),
      value: displayMode,
      setValue: setDisplayMode
    }), /* @__PURE__ */ React2.createElement(ControlledSelector, {
      values: Object.values(WorkflowDebugPanelMode),
      value: executionMode,
      setValue: setExecutionMode
    }))
  }, /* @__PURE__ */ React2.createElement("div", {
    className: "bs-full grid grid-rows-[4fr_3fr]"
  }, /* @__PURE__ */ React2.createElement(MasterDetailTable, {
    properties,
    data: tableData,
    detailsTransform,
    onSelectionChanged: setSelectedId
  }), /* @__PURE__ */ React2.createElement("div", {
    className: mx2("bs-full")
  }, selected && /* @__PURE__ */ React2.createElement(WorkflowDebugPanel, {
    loader,
    graph: selected,
    mode: executionMode
  }))));
};
var toWorkflow = async (loader, graph) => {
  try {
    const loaded = await loader.load(DXN2.fromLocalObjectId(graph.id));
    const mapProps = (ast) => Object.fromEntries(SchemaAST2.getPropertySignatures(ast).map((prop) => [
      prop.name,
      prop.type
    ]));
    const workflowMeta = loaded.resolveMeta();
    return {
      compiled: true,
      inputs: workflowMeta.inputs.map((input) => [
        input.nodeId,
        mapProps(input.schema.ast)
      ]),
      outputs: workflowMeta.outputs.map((output) => [
        output.nodeId,
        mapProps(output.schema.ast)
      ])
    };
  } catch (err) {
    return {
      message: err.message,
      stack: err.stack
    };
  }
};
var toCompactGraph = (graph) => {
  const model = new ComputeGraphModel(graph);
  const result = {};
  for (const node of model.nodes) {
    const incoming = [];
    const outgoing = [];
    for (const edge of model.edges) {
      if (node.id === edge.source) {
        outgoing.push(`${edge.output} -> ${edge.target}.${edge.input}`);
      } else if (node.id === edge.target) {
        incoming.push(`${edge.source}.${edge.output} -> ${edge.input}`);
      }
    }
    result[node.id] = {
      in: incoming,
      out: outgoing
    };
  }
  return result;
};
var createLoader = (graphs) => new WorkflowLoader({
  graphLoader: async (graphDxn) => {
    const graph = graphs.find((g) => DXN2.equals(graphDxn, DXN2.fromLocalObjectId(g.id)));
    if (!graph) {
      throw new Error(`Graph not found: ${graphDxn}.`);
    }
    return graph;
  },
  nodeResolver: async (node) => {
    throw new Error(`Unknown node type: ${node.type}.`);
  }
});
var DisplayMode = /* @__PURE__ */ (function(DisplayMode2) {
  DisplayMode2["COMPILED"] = "compiled";
  DisplayMode2["GRAPH"] = "graph";
  DisplayMode2["RAW"] = "raw";
  return DisplayMode2;
})(DisplayMode || {});

// src/panels/edge/WorkflowPanel/index.ts
var WorkflowPanel_default = WorkflowPanel;
export {
  WorkflowPanel_default as default
};
//# sourceMappingURL=WorkflowPanel-DP2B3DQZ.mjs.map
