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, PlusIcon, TrashIcon } from "@heroicons/react/24/outline";
import Editor from "@monaco-editor/react";
import { twMerge } from "tailwind-merge";
import {
  ChevronLeftIcon,
  ChevronRightIcon,
  PlayIcon,
  SparklesIcon,
} 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 { QuestionMessage } from "../QuestionMessage";
import { PromptInput } from "../PromptInput";
import { Popover, PopoverContent, PopoverTrigger } from "../../tremor/Popover";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { ChartPlaceholder } from "../ChartLoader";

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, widgets } = 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 [tab, setTab] = useState("chat");
  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 [explainingCode, setExplainingCode] = useState(false);
  const [explainedCode, setExplainedCode] = useState<string | null>(null);

  const deleteWidget = async (hideToast?: boolean) => {
    if (!widget || !backend) return;
    if (hideToast) {
      await backend.widgets.delete(widget.id);
      refreshWidgets(backend);
      setOpen(false);
      return;
    }
    toast.promise(
      async () => {
        await backend.widgets.delete(widget.id);
        refreshWidgets(backend);
        setOpen(false);
      },
      {
        loading: "Deleting widget...",
        success: "Widget deleted",
        error: (err) => `Failed to delete widget: ${err.message}`,
      }
    );
  };


  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
`;

    // @ts-ignore
    // if (widget?.config?._internal) {

    if (widget?.engine === "manual-v1") {
      setCode(newCode);
    }
  };

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

  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();
        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 wid = await backend.widget(widget.id).updatePrompts(msg as any[], (event) => {
        // console.log("PROGRESS: ", event);
      });
      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) {

        refreshWidgets(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 = async () => {
    if (!backend) return;
    if (code === "" && messages.length === 0) {
      await deleteWidget(true);
    }
    refreshWidgets(backend, widget?.id);
    setOpen(false);
  }

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

  useEffect(() => {
    const handleBeforeUnload = (event: any) => {
      if (code === "" && messages.length === 0 && open) {
        // Call your cleanup or save function here
        console.log("Tab is closing — running cleanup!");
        myCleanupFunction();

        // If you want to show a confirmation dialog:
        event.preventDefault();
        event.returnValue = ""; // Required for Chrome
      }
    };

    window.addEventListener("beforeunload", handleBeforeUnload);

    // Cleanup the event listener when component unmounts
    return () => {
      window.removeEventListener("beforeunload", handleBeforeUnload);
    };
  }, [open, code, messages]);

  const myCleanupFunction = async () => {
    if (!backend || !widget) return;
    await backend.widgets.delete(widget.id);
    refreshWidgets(backend);
  };

  const addToLibrary = async () => {
    if (!backend) return;
    let widgetCopy = { ...widget, use_in_library: true };

    toast.promise(
      async () => {
        let wid = await backend?.widgets.create(widgetCopy as any);
        let data = await backend.widget(wid.id).updateCache();
        return wid;
      },
      {
        loading: "Adding widget to library...",
        success: (widget) => {
          refreshWidgets(backend);
          return "Widget added to library";
        },
        error: (error) => "Failed to add widget to library: " + error.message,
      }
    );
  }

  const explainCode = async (str: string) => {
    if (!widget || !backend) return;
    setExplainingCode(true);
    setExplainedCode(null);
    try {
      let result = await backend.widget(widget.id).annotateCode();
      setExplainedCode(result.insights);
    } catch (e: any) {
      toast.error("Failed to explain code: " + e.message);
    }
    setExplainingCode(false);
  };

  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();
        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 =
    loading ? (
      <Card className="onvo-loading-card onvo-foreground-color onvo-relative onvo-flex onvo-w-full onvo-flex-col onvo-py-3  onvo-h-96">
        <ChartPlaceholder
          type={widget?.type || "bar"}
          title="Generating chart..."
          showProgress={true}
        />
      </Card>
    ) :
      (code === "" && messages.length === 0) ? (
        <Card className="onvo-loading-card onvo-foreground-color onvo-relative onvo-flex onvo-w-full onvo-flex-col onvo-py-3 onvo-h-96">
          <ChartPlaceholder
            type={widget?.type || "bar"}
            title="Waiting for your prompt..."
            showProgress={false}
          />
        </Card>
      ) : output ? (
        <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")
          }
        >
          <ChartBase
            json={{
              ...output,
              options: {
                ...output.options,
                animation: {
                  duration: 0
                }
              }
            }}
            title={title}
            key={title + hashCode(JSON.stringify(settings) + JSON.stringify(output))}
          />
        </Card>
      ) : <></>

  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 key={widget?.id || ""} className="onvo-relative onvo-flex onvo-flex-grow onvo-h-full onvo-w-full">

            <PanelGroup autoSaveId="edit-widget" direction="horizontal">
              <Panel minSize={25}>

                <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" />

                    {widget ? <Input
                      placeholder="Title"
                      className="onvo-text-xs onvo-max-w-96"
                      value={title} inputClassName="onvo-background-color onvo-border-black/10 dark:onvo-border-white/10"
                      onChange={(val) => setTitle(val.target.value)}
                    /> : <Label>
                      Create widget
                    </Label>}


                  </div>
                </div>
                <div className="onvo-h-[calc(100%-52px)] 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">

                  <PanelGroup autoSaveId="edit-widget-code" direction="vertical">
                    <Panel defaultSize={(data && data.length > 0) ? 80 : 100} minSize={20}>
                      <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">
                        {Preview}
                      </div>
                    </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>
              <PanelResizeHandle className="onvo-w-3 onvo-border-0 onvo-transition-all onvo-bg-black/10 dark:onvo-bg-white/10 hover:onvo-bg-black/20 dark:hover:onvo-bg-white/20 active:onvo-bg-black/40 dark:active:onvo-bg-white/40 onvo-flex onvo-justify-center onvo-items-center">
                <div className="onvo-w-1 onvo-h-12 onvo-rounded-full onvo-bg-black/30 dark:onvo-bg-white/30"></div>
              </PanelResizeHandle>
              <Panel defaultSize={50} minSize={25}>
                <Tabs defaultValue={widget?.engine === "manual-v1" ? "configure" : "chat"} onValueChange={(value) => setTab(value)} className="onvo-flex onvo-flex-col onvo-h-full">
                  <TabsList>
                    <div className="onvo-flex onvo-flex-row onvo-pt-5">
                      {widget?.engine !== "manual-v1" && (
                        <TabsTrigger value="chat">Chat</TabsTrigger>
                      )}
                      {widget?.engine === "manual-v1" && (
                        <TabsTrigger value="configure">Configure</TabsTrigger>
                      )}
                      {adminMode && (
                        <TabsTrigger value="code">Code</TabsTrigger>
                      )}
                      {adminMode && (
                        <TabsTrigger value="settings">Settings</TabsTrigger>
                      )}
                    </div>
                    <div className="onvo-flex-grow onvo-w-full onvo-h-2" />
                    {tab === "chat" && (

                      <div className="onvo-flex onvo-flex-row onvo-gap-2 onvo-flex-shrink-0 onvo-mr-2">

                        <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>
                    )}
                    {tab === "code" && (
                      <div className="onvo-flex onvo-flex-row onvo-gap-2 onvo-items-center onvo-flex-shrink-0 onvo-mr-2">
                        <Popover
                          open={explainingCode || explainedCode !== null}
                          onOpenChange={(open) => {
                            if (!open) setExplainedCode(null);
                          }}
                        >
                          <PopoverTrigger asChild>
                            <Button
                              variant="secondary"
                              className="onvo-flex-shrink-0"
                              onClick={() => explainCode(code)}
                            >
                              <SparklesIcon className="onvo-h-4 onvo-w-4 onvo-mr-2" />
                              Explain code
                            </Button>
                          </PopoverTrigger>
                          <PopoverContent className="onvo-w-96">
                            {explainingCode ? (
                              <div className="onvo-flex onvo-items-center onvo-space-x-2 onvo-p-4">
                                <div role="status">
                                  <svg
                                    aria-hidden="true"
                                    className="onvo-inline onvo-w-6 onvo-h-6 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>
                                <Text>Explaining code...</Text>
                              </div>
                            ) : (
                              <div className="onvo-prose onvo-prose-sm dark:onvo-prose-invert onvo-max-h-[50vh] onvo-overflow-y-auto onvo-p-2">
                                <ReactMarkdown remarkPlugins={[remarkGfm]}>
                                  {explainedCode || ""}
                                </ReactMarkdown>
                              </div>
                            )}
                          </PopoverContent>
                        </Popover>
                        <Button
                          variant="primary"
                          className="onvo-flex-shrink-0"
                          onClick={() => executeCode(code)}
                        >
                          <PlayIcon className="onvo-h-4 onvo-w-4 onvo-mr-2" />
                          Run code
                        </Button>
                      </div>
                    )}
                    {tab === "settings" && (
                      <Button
                        variant="primary"
                        className="onvo-flex-shrink-0 onvo-mr-2"
                        onClick={saveChanges}
                      >
                        Save Changes
                      </Button>
                    )}
                  </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-56px)] 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={(val) => {
                              setConfig(val);
                            }}
                            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-28px)]">
                        <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>

                  )}
                  {adminMode && (
                    <TabsContent value="settings" className="onvo-px-4 onvo-py-8">

                      <div className="onvo-flex onvo-flex-row onvo-items-center onvo-justify-between onvo-gap-2">
                        <Text className="onvo-text-sm onvo-w-32 onvo-shrink-0">Widget ID</Text>
                        <Input
                          placeholder="ID"
                          className="onvo-text-xs"
                          value={widget?.id} disabled
                          inputClassName="onvo-background-color onvo-border-black/10 dark:onvo-border-white/10"
                        />
                      </div>

                      <Divider />
                      <div className="onvo-flex onvo-flex-row onvo-items-center onvo-justify-between onvo-gap-2 onvo-mt-2">
                        <Text className="onvo-text-sm onvo-w-32 onvo-shrink-0">CSS ID</Text>
                        <Input
                          placeholder="id"
                          className="onvo-text-xs"
                          value={settings?.css_id}
                          onChange={(e) => setSettings({ ...settings, css_id: e.target.value })}
                          inputClassName="onvo-background-color onvo-border-black/10 dark:onvo-border-white/10"
                        />
                      </div>
                      <div className="onvo-flex onvo-flex-row onvo-items-center onvo-justify-between onvo-gap-2 onvo-mt-2">
                        <Text className="onvo-text-sm onvo-w-32 onvo-shrink-0">CSS classes</Text>
                        <Input
                          placeholder="class-name"
                          className="onvo-text-xs"
                          value={settings?.css_classnames}
                          onChange={(e) => setSettings({ ...settings, css_classnames: e.target.value })}
                          inputClassName="onvo-background-color onvo-border-black/10 dark:onvo-border-white/10"
                        />
                      </div>
                      <div className="onvo-flex onvo-flex-row onvo-items-center onvo-justify-between onvo-gap-2 onvo-mt-2">
                        <Text className="onvo-text-sm onvo-w-32 onvo-shrink-0">Link</Text>
                        <Input
                          placeholder="https://..."
                          className="onvo-text-xs"
                          value={settings?.link}
                          onChange={(e) => setSettings({ ...settings, link: e.target.value })}
                          inputClassName="onvo-background-color onvo-border-black/10 dark:onvo-border-white/10"
                        />
                      </div>

                      <Divider />
                      <div className="onvo-flex onvo-flex-row onvo-items-center onvo-justify-between onvo-gap-2">
                        <Text className="onvo-text-sm onvo-w-32 onvo-shrink-0">Drilldown widget</Text>
                        <Select
                          value={widget?.drilldown_widget || undefined}
                          onValueChange={(val) => {
                            if (!widget || !backend) return;
                            backend.widgets.update(widget.id, {
                              drilldown_widget: val,
                            });
                          }}
                        >
                          <SelectTrigger>
                            <SelectValue placeholder="None" />
                          </SelectTrigger>
                          <SelectContent>
                            {widgets
                              .filter((item) => item.id !== widget?.id && ["divider"].indexOf(widget?.type || "") === -1 && item.title.trim() !== "")
                              .map((item) => (
                                <SelectItem key={item.id} value={item.id}>
                                  {item.title}
                                </SelectItem>
                              ))}
                          </SelectContent>
                        </Select>
                      </div>

                      <Divider />
                      <div className="onvo-flex onvo-flex-row onvo-items-center onvo-justify-between onvo-gap-2">
                        <Button
                          variant="secondary"
                          onClick={() => addToLibrary()}
                          className="onvo-w-full onvo-mt-2"
                        >
                          <PlusIcon className="onvo-h-4 onvo-w-4 onvo-mr-2" />
                          Add to Library
                        </Button>
                        <Button
                          variant="destructive"
                          onClick={() => deleteWidget()}
                          className="onvo-w-full onvo-mt-2"
                        >
                          <TrashIcon className="onvo-h-4 onvo-w-4 onvo-mr-2" />
                          Delete widget
                        </Button>
                      </div>
                    </TabsContent>
                  )}
                </Tabs>
              </Panel>

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