import React, { useRef } from "react";
import { Icon } from "../../tremor/Icon";
import { Input } from "../../tremor/Input";
import { Button } from "../../tremor/Button";
import { useEffect, useState } from "react";
import { create } from "zustand";
import { useDashboard } from "../../layouts/Dashboard/useDashboard";
import { useBackend } from "../../layouts/Wrapper";
import { Text, Label, Title } from "../../tremor/Text";
import { ArrowUturnLeftIcon, ArrowUturnRightIcon, TrashIcon } from "@heroicons/react/24/outline";
import Editor from "@monaco-editor/react";
import { twMerge } from "tailwind-merge";
import {
  ChevronLeftIcon,
  ChevronRightIcon,
  PlayIcon,
} from "@heroicons/react/20/solid";
import ChartBase from "../Chart/ChartBase";
import { Card } from "../../tremor/Card";
import { toast } from "sonner";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import { WidgetWizard } from "../WidgetWizard";
import { LogType, Message, Widget, WidgetSettings } from "@onvo-ai/js";
import { useTheme } from "../../layouts/Dashboard/useTheme";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../tremor/Select";
import { MultiSelect } from "../../tremor/MultiSelect";
import { Divider } from "../../tremor/Divider";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../tremor/Tabs";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeaderCell,
  TableRoot,
  TableRow,
} from "../../tremor/Table";
import { waitForJob } from "../../lib/waitForJob";
import { QuestionMessage } from "../QuestionMessage";
import { PromptInput } from "../PromptInput";

const hashCode = function (s: string) {
  return s.split("").reduce(function (a, b) {
    a = ((a << 5) - a) + b.charCodeAt(0);
    return a & a;
  }, 0);
}

export const useEditWidgetModal = create<{
  open: boolean;
  widget?: Widget;
  setOpen: (
    open: boolean,
    widget?: Widget
  ) => void;
}>((set) => ({
  open: false,
  setOpen: (
    op: boolean,
    wid?: Widget
  ) => set({ open: op, widget: wid }),
}));



function useDebounce(cb: any, delay: number) {
  const [debounceValue, setDebounceValue] = useState(cb);
  useEffect(() => {
    const handler = setTimeout(() => {
      setDebounceValue(cb);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [cb, delay]);
  return debounceValue;
}

const getErrorDetails = (err: string) => {

  if (err.startsWith("FileNotFoundError")) {
    const regex =
      /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/g;
    const datasourceIds = err.match(regex);
    const deduplicatedIds = [...new Set(datasourceIds)] as string[];
    return {
      label: `The datasource with id ${deduplicatedIds[0]} has been removed from the dashboard or deleted.`,
      prompt: `Replace the datasource with id ${deduplicatedIds[0]} with an existing datasource.`
    }
  }
  if (err.startsWith("NameError: name 'main' is not defined")) {
    return {
      label: `There is no code for this widget to execute.`,
      prompt: `Rewrite the function 'main' again.`
    }
  }
  if (err.startsWith("ValueError: Usecols do not match columns")) {
    const regex = /\['([^']+)'\]|\s*'([^']+)'/g;
    const matches = [...err.matchAll(regex)].map(m => m[1] || m[2]);
    return {
      label: `The following columns no longer exist: ${matches.join(", ")}.`,
      prompt: `The following columns no longer exist: ${matches.join(", ")}. Rewrite using existing columns only.`
    }
  }
  return {
    label: err,
    prompt: `Fix the error: \`${err}\``
  }
}


const uuidRegex = /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi;


export const EditWidgetModal: React.FC<{}> = ({ }) => {
  const { dashboard, datasources, refreshWidgets } = useDashboard();
  const { backend, adminMode } = useBackend();
  const theme = useTheme();
  const { open, setOpen, widget } = useEditWidgetModal();
  const container = useRef<HTMLDivElement>(null);

  const editorRef = useRef(null);
  const monacoRef = useRef(null);
  const decorationsRef = useRef([]);

  const handleEditorDidMount = (editor: any, monaco: any) => {

    editorRef.current = editor;
    monacoRef.current = monaco;


    // Create a context key service
    const contextKeyService = editor._contextKeyService;
    const isUUIDContext = contextKeyService.createKey('isUUIDContext', false);

    // Update the context key based on the position when context menu is opened
    editor.onContextMenu((e: any) => {
      const position = e.target.position;
      const model = editor.getModel();

      if (!position || !model) {
        isUUIDContext.set(false);
        return;
      }

      // Check if cursor is on or near a UUID
      const uuidRegex = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i;

      // Get the current line text
      const lineContent = model.getLineContent(position.lineNumber);

      // Find all UUIDs in the line
      const matches = [...lineContent.matchAll(new RegExp(uuidRegex, 'gi'))];

      // Check if the cursor is within any UUID
      const isOnUUID = matches.some(match => {
        const start = match.index;
        const end = start + match[0].length;
        return position.column > start && position.column <= end;
      });

      isUUIDContext.set(isOnUUID);
    });

    // Add the action with a precondition based on the context key
    editor.addAction({
      id: 'go-to-datasource',
      label: 'Go to datasource',
      contextMenuGroupId: 'navigation',
      contextMenuOrder: 0,
      precondition: 'isUUIDContext',
      run: (ed: any) => {
        const position = ed.getPosition();
        const model = ed.getModel();

        if (!position || !model) return;

        // Get the current line content
        const lineContent = model.getLineContent(position.lineNumber);

        // Find UUIDs in the line
        const uuidRegex = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i;
        const matches = [...lineContent.matchAll(new RegExp(uuidRegex, 'gi'))];

        // Find the UUID at or nearest to cursor position
        for (const match of matches) {
          const start = match.index;
          const end = start + match[0].length;

          if (position.column > start && position.column <= end) {
            const uuid = match[0];
            const url = `https://dashboard.onvo.ai/datasources/${uuid}`;
            window.open(url, '_blank');
            break;
          }
        }
      },
    });


    highlightUUIDs();
  };


  const highlightUUIDs = () => {
    const editor = editorRef.current as any;
    const monaco = monacoRef.current as any;
    if (!editor || !monaco) return;

    const model = editor.getModel();
    if (!model) return;

    const value = model.getValue();
    const matches = [...value.matchAll(uuidRegex)];

    const decorations = matches.map((match) => {
      const start = model.getPositionAt(match.index);
      const end = model.getPositionAt(match.index + match[0].length);

      return {
        range: new monaco.Range(
          start.lineNumber,
          start.column,
          end.lineNumber,
          end.column
        ),
        options: {
          inlineClassName: "uuid-highlight",
        },
      };
    });

    decorationsRef.current = editor.deltaDecorations(
      decorationsRef.current,
      decorations
    );
  };


  // REVERT CHANGES STATES
  const [history, setHistory] = useState<
    {
      code: string, cache: any, messages: any[]
    }[]
  >([]);
  const [historyIndex, setHistoryIndex] = useState(-1);


  const [output, setOutput] = useState<any>(null);
  const [title, setTitle] = useState("");
  const [settings, setSettings] = useState<WidgetSettings>({});
  const [code, setCode] = useState("");
  const [messages, setMessages] = useState<(Message)[]>([]);
  const [config, setConfig] = useState<any>({});
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<null | string>(null);


  const [datasource, setDatasource] = useState<{ label: string, value: string }>({ label: "", value: "" });
  const [columns, setColumns] = useState<string[]>([]);
  const [groupBy, setGroupBy] = useState<string | null>(null);
  const [data, setData] = useState<any[]>([]);


  useEffect(() => {
    highlightUUIDs();
  }, [code]);

  const constructCode = () => {
    let dfColumns = columns.map(a => `'${a}'`).join(", ");
    let groupByCode = "";

    if (groupBy) {
      const otherColumns = columns.filter(a => a !== groupBy);
      groupByCode = `
  # Group by ${groupBy}
  df = df.groupby('${groupBy}').agg({${otherColumns.map(a => `'${a}': 'sum'`).join(',')}})
  df = df.reset_index()
        `
    }

    let newCode = `
def main():
  # Load necessary columns from the first CSV file
  df = pd.read_csv('/tmp/${datasource.value}.csv', usecols=[${dfColumns}])
    ${groupByCode}
  return df
`;

    setCode(newCode);
  };

  useEffect(() => {
    constructCode();
  }, [
    datasource,
    groupBy,
    columns
  ]);

  const getCache = async () => {
    if (!widget || !backend) return;
    let { data: cache } = await backend.widget(widget.id).cache();
    if (cache) {
      setOutput(cache);
    }
    setHistory([{
      cache: cache,
      code: widget.code,
      messages: widget.messages,
    }]);
    setHistoryIndex(0);

    let { data: rawCache } = await backend?.widget(widget.id).cache({ raw: true });
    if (rawCache) {
      setData(rawCache);
    }
  };

  const updateStates = (widget: Widget | undefined) => {
    if (widget) {
      setTitle(widget.title || "");
      setCode(widget.code);
      setSettings(
        widget.settings || {}
      );

      setError(widget.error || null);
      setMessages((widget.messages || []) as any[]);

      let conf = widget.config as any;
      let meta = conf._internal;
      delete conf._internal;
      setConfig(conf);
      if (meta) {
        setDatasource({ label: meta.datasource, value: meta.datasource });
        setColumns(meta.columns);
        setGroupBy(meta.groupBy);
      }
    } else {
      setTitle("");
      setCode("");
      setOutput(null);
      setMessages([]);
      setError(null);
      setConfig({});
      setColumns([]);
      setGroupBy(null);
    }
  };


  useEffect(() => {
    if (widget) {
      updateStates(widget);
    } else {
      updateStates(undefined);
    }
  }, [widget]);

  useEffect(() => {
    if (!widget) return;
    if (!backend) return;
    getCache();
  }, [widget, backend]);

  useEffect(() => {
    if (!data || data.length === 0) return;
    if (!config || Object.keys(config).length == 0 || !config.options || !config.options.plugins) {
      return;
    }

    let columns = Object.keys(data[0]);
    let out = JSON.stringify(config);
    columns.forEach(col => {
      out = out.replace(`"{{data['${col}']}}"`, JSON.stringify(data.map((a: any) => a[col])))
    });
    setOutput(JSON.parse(out));
  }, [data, config]);

  const saveChanges = () => {
    if (!widget || !backend) return;
    toast.promise(
      async () => {
        await backend.widgets.update(widget.id, {
          code: code,
          settings: settings,
          messages: [],
          config: {
            ...config,
            _internal: {
              datasource: datasource.value,
              columns: columns,
              groupBy: groupBy
            }
          },
          error: null,
        });

        let data = await backend?.widget(widget.id).updateCache();

        await waitForJob(data._meta.url);
        return true;
      },
      {
        loading: "Saving changes...",
        success: () => {
          refreshWidgets(backend);
          return "Changes saved!";
        },
        error: (error) => "Failed to save changes: " + error.message,
      }
    );
  };

  const requestEditWidget = async (msg: Message[]) => {
    if (!widget || !backend) return;
    setCode("");
    setLoading(true);

    try {

      let data = await backend.widget(widget.id).updatePrompts(msg as any[]);
      updateStates(data);
      await waitForJob(data._meta.url, "Updating widget");
      let wid = await backend?.widgets.get(widget.id);
      updateStates(wid);

      let { data: cacheData } = await backend?.widget(widget.id).cache();

      setHistory(hist => [...hist, {
        cache: cacheData,
        code: wid?.code || "",
        messages: msg,
      }]);

      setHistoryIndex(hist => hist + 1);
      toast.success("Your widget has been updated!");
      setOutput(cacheData);
      setLoading(false);
      if (backend) {
        backend.logs.create({
          type: LogType.EditWidget,
          dashboard: widget.dashboard,
          widget: widget.id,
        })
      }
    } catch (e: any) {
      toast.error("Failed to create widget: " + e.message);
      setLoading(false);
    }
  };

  const goBack = () => {
    if (!backend) return;
    refreshWidgets(backend, widget?.id);
    setOpen(false);
  }

  const debounceValue = useDebounce(title, 200);
  useEffect(() => {
    if (widget) {
      backend?.widgets.update(widget?.id, {
        title: debounceValue,
      });
    }
  }, [debounceValue]);

  const executeCode = async (str: string) => {
    if (!widget || !backend) return;
    if (str.search("pd.read_csv") === -1) return;

    setLoading(true);

    toast.promise(
      async () => {
        await backend.widgets.update(widget.id, { code: str });
        let data = await backend.widget(widget.id).updateCache();
        await waitForJob(data._meta.url);
        let { data: newData } = await backend.widget(widget.id).cache();
        setOutput(newData);

        setHistory(hist => [...hist, {
          cache: newData,
          code: code,
          messages: widget.messages,
        }]);
        setHistoryIndex(hist => hist + 1);

        let { data: rawCache } = await backend?.widget(widget.id).cache({ raw: true });
        if (rawCache) {
          setData(rawCache);
        }

        if (backend) {
          backend.logs.create({
            type: LogType.EditWidget,
            dashboard: widget.dashboard,
            widget: widget.id,
          })
        }
      },
      {
        loading: "Executing code...",
        success: () => {
          setLoading(false);
          return "Successfully executed code";
        },
        error: (e: any) => {
          setLoading(false);
          return "Failed to execute code: " + e.message;
        },
      }
    );
  };

  if (!dashboard?.settings?.can_create_widgets && !adminMode) return <></>;


  const Preview = (<div className="onvo-relative onvo-h-full onvo-w-full onvo-p-4 onvo-background-color onvo-bg-[radial-gradient(#e2e8f0_1px,transparent_1px)] dark:onvo-bg-[radial-gradient(#0f172a_1px,transparent_1px)] [background-size:16px_16px] onvo-flex onvo-justify-center onvo-items-center">
    {loading && (
      <div className="onvo-absolute onvo-top-0 onvo-left-0 onvo-bottom-0 onvo-right-0 onvo-z-10 onvo-backdrop-blur-md onvo-bg-white/50 dark:onvo-bg-black-900/50 onvo-flex onvo-justify-center onvo-items-center">
        <Card className="onvo-loading-card onvo-foreground-color onvo-flex onvo-flex-row onvo-gap-6 onvo-items-center !onvo-w-72 onvo-border-white/10 dark:onvo-border-black-900/10">
          <div role="status">
            <svg
              aria-hidden="true"
              className="onvo-inline onvo-w-10 onvo-h-10 onvo-text-gray-200 onvo-animate-spin dark:onvo-text-gray-600 onvo-fill-blue-600"
              viewBox="0 0 100 101"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
                fill="currentColor"
              />
              <path
                d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
                fill="currentFill"
              />
            </svg>
          </div>
          <Title>Regenerating chart</Title>
        </Card>
      </div>
    )}

    <Card
      className={
        "onvo-foreground-color onvo-relative onvo-flex onvo-w-full onvo-flex-col onvo-py-3 " +
        (output?.type === "metric"
          ? "onvo-h-32"
          : "onvo-h-72 @xl/widgetmodal:onvo-h-96")
      }
    >
      {output && (
        <ChartBase
          json={{
            ...output,
            options: {
              ...output.options,
              animation: {
                duration: 0
              }
            }
          }}
          title={title}
          key={title + hashCode(JSON.stringify(settings) + JSON.stringify(output))}
        />
      )}
    </Card></div>)

  return (
    <>
      <dialog open={open}>
        <div ref={container}
          className={twMerge(
            "onvo-@container/widgetmodal onvo-h-full onvo-animate-dialogOpen onvo-w-full onvo-z-50 onvo-fixed onvo-left-0 onvo-top-0 onvo-foreground-color"
          )}
        >
          <div
            className={
              "onvo-foreground-color onvo-w-full onvo-left-0 onvo-top-0 onvo-z-10 onvo-flex onvo-flex-row onvo-justify-start onvo-items-center onvo-gap-4 onvo-border-solid onvo-border-b onvo-border-black/10 onvo-p-2 dark:onvo-border-white/10"
            }
          >
            <Icon
              icon={ChevronLeftIcon}
              variant="shadow"
              className="onvo-ml-2 onvo-background-color onvo-border-solid onvo-border onvo-border-black/10 dark:onvo-border-white/10"
              onClick={goBack}
            />

            <div className="onvo-flex onvo-flex-row onvo-w-full onvo-gap-1 onvo-flex-grow onvo-justify-start onvo-items-center">
              <Text className="onvo-hidden @xl/widgetmodal:onvo-block">
                {dashboard?.title}
              </Text>
              <ChevronRightIcon className="onvo-hidden @xl/widgetmodal:onvo-block onvo-h-4 onvo-w-4 dark:onvo-fill-slate-500" />
              <Label>
                {widget ? (widget.title || "Edit Widget") : "Create widget"}
              </Label>
            </div>
            <div className="onvo-flex onvo-flex-row onvo-gap-2 onvo-flex-shrink-0">

              <Icon variant="shadow" icon={ArrowUturnLeftIcon} className={
                historyIndex > 0 ? "onvo-opacity-100" : "onvo-opacity-30"
              } onClick={() => {
                if (historyIndex === 0) { return; }
                let newIndex = Math.max(0, historyIndex - 1);
                setHistoryIndex(newIndex);
                let cachedWidget = history[newIndex];
                setCode(cachedWidget.code);
                setMessages(cachedWidget.messages || []);
                setOutput(
                  cachedWidget && cachedWidget.cache
                    ? cachedWidget.cache
                    : {}
                );
              }} />

              <Icon variant="shadow" className={
                historyIndex < (history.length - 1) ? "onvo-opacity-100" : "onvo-opacity-30"
              } icon={ArrowUturnRightIcon} onClick={() => {
                if (historyIndex === history.length - 1) { return; }
                let newIndex = Math.min(history.length - 1, historyIndex + 1);
                setHistoryIndex(newIndex);
                let cachedWidget = history[newIndex];
                setCode(cachedWidget.code);
                setMessages(cachedWidget.messages || []);
                setOutput(
                  cachedWidget && cachedWidget.cache
                    ? cachedWidget.cache
                    : {}
                );
              }} />

            </div>
          </div>
          <div key={widget?.id || ""} className="onvo-relative onvo-flex onvo-flex-grow onvo-h-[calc(100%-52px)] onvo-w-full">

            <PanelGroup autoSaveId="edit-widget" direction="horizontal">
              <Panel defaultSize={50} minSize={25}>
                <Tabs defaultValue={widget?.engine === "manual-v1" ? "configure" : "chat"} className="onvo-h-full onvo-pt-3">
                  <TabsList>
                    {widget?.engine !== "manual-v1" && (
                      <TabsTrigger value="chat">Chat</TabsTrigger>
                    )}
                    {widget?.engine === "manual-v1" && (
                      <TabsTrigger value="configure">Configure</TabsTrigger>
                    )}
                    {adminMode && (
                      <TabsTrigger value="code">Code</TabsTrigger>
                    )}
                  </TabsList>
                  {widget?.engine !== "manual-v1" && (
                    <TabsContent value="chat" className="onvo-h-[calc(100%-28px)] onvo-relative onvo-flex-grow onvo-w-full onvo-max-w-3xl onvo-mx-auto">
                      <div className="onvo-relative onvo-flex-grow onvo-h-[calc(100%-16px)] onvo-w-full onvo-flex onvo-flex-col">
                        <div className="onvo-overflow-y-auto onvo-scrollbar-thin onvo-flex-grow onvo-pt-2 onvo-px-4 onvo-w-full">
                          <div className="onvo-relative onvo-mx-auto onvo-w-full onvo-pt-2">
                            {messages.map((message, index) => (
                              <QuestionMessage
                                key={"message-" + index}
                                message={message}
                                isLast={index === messages.length - 1}
                                onAdd={() => { }}
                                onReply={(msg) => {
                                  let newMessages = [...messages, {
                                    role: "user" as const,
                                    content: msg,
                                  }];
                                  requestEditWidget(newMessages as any[]);
                                  setMessages(newMessages as any[]);
                                }}
                                onEdit={(msg) => {
                                  let newMessages = messages.map((m, i) => {
                                    if (i === index) {
                                      return {
                                        ...m,
                                        content: msg,
                                      };
                                    }
                                    return m;
                                  });
                                  requestEditWidget(newMessages);
                                  setMessages(newMessages);
                                }}
                              />
                            ))}
                            {(error && !loading) ? (
                              <div className="onvo-flex onvo-justify-start onvo-max-w-[80%]">
                                <div className="onvo-py-3 onvo-px-4 onvo-rounded-2xl onvo-border onvo-border-black/10 dark:onvo-border-white/10 onvo-flex-shrink">
                                  <Text>The widget failed to update during the last cache refresh. Would you like me to attempt to fix the issue?</Text>
                                  <div className="onvo-my-2 onvo-rounded-lg onvo-border onvo-border-red-500 onvo-bg-red-500/10 onvo-overflow-hidden">
                                    <div className="onvo-w-full onvo-px-3 onvo-py-2 onvo-bg-red-500">
                                      <Text className="!onvo-text-white onvo-text-xs !onvo-tracking-widest">STACKTRACE</Text>
                                    </div>
                                    <Text className="onvo-font-mono !onvo-text-sm onvo-px-3 onvo-py-2">
                                      {getErrorDetails(error).label}
                                    </Text>
                                  </div>
                                  <div className="onvo-w-full onvo-flex onvo-flex-row onvo-justify-end">
                                    <Button onClick={() => {
                                      let newMessages = [...messages, {
                                        role: "user" as const,
                                        content: getErrorDetails(error).prompt,
                                      }];
                                      requestEditWidget(newMessages as any);
                                      setMessages(newMessages as any);
                                    }}>Attempt fix</Button>
                                  </div>
                                </div>
                              </div>) : <></>}
                          </div>
                        </div>
                        <div className="onvo-px-4">
                          <PromptInput
                            url={dashboard?.settings?.help_url}
                            onSubmit={(msg) => {
                              setMessages([...messages, {
                                role: "user",
                                content: msg
                              }] as any[])
                              requestEditWidget([...messages, {
                                role: "user",
                                content: msg
                              }] as any[])
                            }} />
                        </div>
                      </div>
                    </TabsContent>)}
                  {widget?.engine === "manual-v1" && (
                    <TabsContent value="configure" >
                      <div className="onvo-h-[calc(100vh-94px)] onvo-relative onvo-overflow-y-auto onvo-scrollbar-thin onvo-flex-grow onvo-w-full onvo-p-4 onvo-flex onvo-flex-col onvo-gap-2">
                        <div className="onvo-flex onvo-flex-row onvo-items-center onvo-gap-2">
                          <Label className="onvo-w-24 onvo-flex-shrink-0">Datasource</Label>
                          <Select value={datasource.value} onValueChange={val => {
                            const selected = datasources.find(a => a.id === val);
                            if (!selected) return;
                            setDatasource({ label: selected.title, value: selected.id });
                          }}>
                            <SelectTrigger>
                              <SelectValue placeholder="Select" />
                            </SelectTrigger>
                            <SelectContent>
                              {datasources.map((item) => (
                                <SelectItem key={item.id} value={item.id}>
                                  {item.title}
                                </SelectItem>
                              ))}
                            </SelectContent>
                          </Select>

                        </div>

                        {(datasource && datasource.value) && (<>
                          <div className="onvo-flex onvo-flex-row onvo-items-center onvo-gap-2">
                            <Label className="onvo-w-24 onvo-flex-shrink-0">Columns</Label>
                            <MultiSelect value={columns} onValueChange={setColumns}
                              items={datasources.find(a => a.id === datasource.value)?.columns.map((item) => ({
                                label: item.title,
                                value: item.title
                              })) || []} />
                          </div>
                          {columns.length > 0 && (
                            <div className="onvo-flex onvo-flex-row onvo-items-center onvo-gap-2">

                              <Label className="onvo-w-24 onvo-flex-shrink-0">Group by</Label>
                              <Select value={groupBy || undefined} onValueChange={val => {
                                setGroupBy(val === "" ? null : val)
                              }}>
                                <SelectTrigger>
                                  <SelectValue placeholder="None" />
                                </SelectTrigger>
                                <SelectContent>
                                  {columns.map((item) => (
                                    <SelectItem key={item} value={item}>
                                      {item}
                                    </SelectItem>
                                  ))}
                                </SelectContent>
                              </Select>

                              <Icon icon={TrashIcon} variant="shadow" className=" !onvo-text-red-500 onvo-cursor-pointer" onClick={() => {
                                setGroupBy(null);
                              }} />

                            </div>)}
                        </>)}
                        <div className="onvo-flex onvo-flex-row onvo-justify-end onvo-w-full">
                          <Button onClick={() => executeCode(code)}>Fetch data</Button>
                        </div>

                        {(columns.length > 0) && (<>
                          <Divider />
                          <WidgetWizard
                            columns={columns}
                            onChange={setConfig}
                            config={config}
                          />
                          <div className="onvo-flex onvo-flex-row onvo-justify-end onvo-w-full">
                            <Button onClick={() => saveChanges()}>Save changes</Button>
                          </div></>
                        )}

                      </div>
                    </TabsContent>)}

                  {adminMode && (
                    <TabsContent value="code">
                      <div className="onvo-flex onvo-flex-col onvo-h-[calc(100vh-94px)]">

                        <div className="onvo-flex onvo-px-3 onvo-py-2 onvo-flex-row onvo-justify-between onvo-items-center onvo-border-b onvo-border-black/10 dark:onvo-border-white/10">
                          <Text className="onvo-font-semibold">Code editor</Text>
                          <Button
                            variant="secondary"
                            onClick={() => executeCode(code)}
                          > <PlayIcon className="onvo-h-4 onvo-w-4 onvo-mr-2" />
                            Run code
                          </Button>
                        </div>
                        <div className="onvo-flex-grow onvo-h-full onvo-relative" >
                          <Editor
                            defaultLanguage="python"
                            value={code}
                            height="100%"
                            options={{
                              padding: {
                                top: 10,
                              },
                            }}
                            onMount={handleEditorDidMount}
                            className="onvo-code-editor onvo-w-full"
                            theme={theme === "dark" ? "vs-dark" : "light"}
                            onChange={(val) => setCode(val || "")}
                          />
                          <style>{`
        .uuid-highlight {
          background: #3b82f6 !important;
          color: #ffffff !important;
          font-weight: bold;
          border-radius: 4px;
          padding: 1px 2px;
        }
      `}</style>
                        </div>
                      </div>
                    </TabsContent>
                  )}
                </Tabs>
              </Panel>
              <PanelResizeHandle className="onvo-w-[1px] onvo-border-0 onvo-bg-black/10 dark:onvo-bg-white/10 hover:onvo-bg-black/50 dark:hover:onvo-bg-white/50 active:onvo-bg-black/50 dark:active:onvo-bg-white/50" />
              <Panel minSize={25}>

                <div className="onvo-h-full onvo-flex onvo-flex-shrink-0 @xl/widgetmodal:onvo-flex-shrink onvo-flex-col onvo-justify-center onvo-w-full onvo-flex-grow onvo-relative onvo-overflow-y-auto">


                  <div className="onvo-border-b onvo-border-black/10 dark:onvo-border-white/10 onvo-p-3 onvo-w-full onvo-flex onvo-flex-row onvo-items-center onvo-justify-between onvo-gap-2">
                    <Text className="onvo-text-sm">Title</Text>
                    <Input
                      placeholder="Title"
                      className="onvo-text-xs"
                      value={title} inputClassName="onvo-background-color onvo-border-black/10 dark:onvo-border-white/10"
                      onChange={(val) => setTitle(val.target.value)}
                    />
                  </div>
                  <PanelGroup autoSaveId="edit-widget-code" direction="vertical">
                    <Panel defaultSize={(data && data.length > 0) ? 80 : 100} minSize={20}>
                      {Preview}
                    </Panel>
                    {(data && data.length > 0) && (<>
                      <PanelResizeHandle className="onvo-h-[1px] onvo-bg-black/10 dark:onvo-bg-white/10 hover:onvo-bg-black/50 dark:hover:onvo-bg-white/50 active:onvo-bg-black/50 dark:active:onvo-bg-white/50" />
                      <Panel minSize={10}>
                        <div className="onvo-flex onvo-flex-col onvo-h-full">


                          <div className="onvo-flex-grow onvo-h-full onvo-relative" >
                            <TableRoot>
                              <Table>
                                <TableHead>
                                  <TableRow>
                                    {columns.map(a => (
                                      <TableHeaderCell key={a}>{a}</TableHeaderCell>
                                    ))}
                                  </TableRow>
                                </TableHead>
                                <TableBody>
                                  {data.map((item, index) => (
                                    <TableRow key={index}>
                                      {columns.map(a => (
                                        <TableCell key={index + a}>{item[a]}</TableCell>
                                      ))}
                                    </TableRow>
                                  ))}
                                </TableBody>

                              </Table>
                            </TableRoot>

                          </div>
                        </div>
                      </Panel>
                    </>)}
                  </PanelGroup>


                </div>
              </Panel>
            </PanelGroup>
          </div>
        </div>
      </dialog>
    </>
  );
};
