/* Copyright 2026 Marimo. All rights reserved. */

import { CommandList } from "cmdk";
import { BetweenHorizontalStartIcon, PlusIcon, XIcon } from "lucide-react";
import React from "react";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import {
  Command,
  CommandEmpty,
  CommandInput,
  CommandItem,
} from "@/components/ui/command";
import type { Snippet } from "@/core/network/types";
import { useAsyncData } from "@/hooks/useAsyncData";
import { renderHTML } from "@/plugins/core/RenderHTML";
import { ErrorBanner } from "@/plugins/impl/common/error-banner";
import { PanelEmptyState } from "./empty-state";

import "./snippets-panel.css";
import { EditorView } from "@codemirror/view";
import { Suspense } from "react";
import { Spinner } from "@/components/icons/spinner";
import { Button } from "@/components/ui/button";
import { Tooltip } from "@/components/ui/tooltip";
import { useCellActions } from "@/core/cells/cells";
import { useLastFocusedCellId } from "@/core/cells/focus";
import { useRequestClient } from "@/core/network/requests";
import { LazyAnyLanguageCodeMirror } from "@/plugins/impl/code/LazyAnyLanguageCodeMirror";
import { useTheme } from "@/theme/useTheme";
import { cn } from "@/utils/cn";
import { HideInKioskMode } from "../../kiosk-mode";
import { ContributeSnippetButton } from "../components/contribute-snippet-button";
import { usePanelOrientation, usePanelSection } from "./panel-context";

const extensions = [EditorView.lineWrapping];

const SnippetsPanel: React.FC = () => {
  const { readSnippets } = useRequestClient();
  const [selectedSnippet, setSelectedSnippet] = React.useState<Snippet>();
  const orientation = usePanelOrientation();
  const section = usePanelSection();
  const {
    data: snippets,
    error,
    isPending,
  } = useAsyncData(() => {
    return readSnippets();
  }, []);

  if (error) {
    return <ErrorBanner error={error} />;
  }

  if (isPending || !snippets) {
    return <Spinner size="medium" centered={true} />;
  }

  const isVertical = orientation === "vertical";

  return (
    <div className="flex-1 overflow-hidden h-full">
      <PanelGroup key={section} direction={orientation} className="h-full">
        {/* Snippet list panel */}
        <Panel defaultSize={40} minSize={20} maxSize={70}>
          <Command className="h-full rounded-none">
            <div className="flex items-center w-full border-b">
              <CommandInput
                placeholder="Search snippets..."
                className="h-6 m-1"
                rootClassName="flex-1 border-r"
              />
              <ContributeSnippetButton>
                <button className="float-right px-2 m-0 h-full hover:bg-accent hover:text-accent-foreground">
                  <PlusIcon className="h-4 w-4" />
                </button>
              </ContributeSnippetButton>
            </div>

            <CommandEmpty>No results</CommandEmpty>
            <SnippetList
              onSelect={(snippet) => setSelectedSnippet(snippet)}
              snippets={snippets.snippets}
            />
          </Command>
        </Panel>
        <PanelResizeHandle
          className={cn(
            "bg-border hover:bg-primary/50 transition-colors",
            isVertical ? "h-1" : "w-1",
          )}
        />
        {/* Snippet viewer panel */}
        <Panel defaultSize={60} minSize={20}>
          <Suspense>
            <div className="h-full snippet-viewer flex flex-col overflow-hidden">
              {selectedSnippet ? (
                <SnippetViewer
                  key={selectedSnippet.title}
                  snippet={selectedSnippet}
                  onClose={() => setSelectedSnippet(undefined)}
                />
              ) : (
                <PanelEmptyState
                  title=""
                  description="Click on a snippet to view its content."
                />
              )}
            </div>
          </Suspense>
        </Panel>
      </PanelGroup>
    </div>
  );
};

export default SnippetsPanel;

const SnippetViewer: React.FC<{ snippet: Snippet; onClose: () => void }> = ({
  snippet,
  onClose,
}) => {
  const { theme } = useTheme();
  const { createNewCell } = useCellActions();
  const lastFocusedCellId = useLastFocusedCellId();

  const handleInsertSnippet = () => {
    // Add below last focused cell in reverse order
    for (const section of snippet.sections.toReversed()) {
      if (section.code) {
        createNewCell({
          code: section.code,
          before: false,
          cellId: lastFocusedCellId ?? "__end__",
          // If the code already exists, skip creation
          skipIfCodeExists: true,
        });
      }
    }
  };

  const handleInsertCode = (code: string) => {
    createNewCell({
      code,
      before: false,
      cellId: lastFocusedCellId ?? "__end__",
    });
  };

  return (
    <>
      <div className="text-sm font-semibold bg-muted border-y px-2 py-1 flex justify-between items-center">
        <span>{snippet.title}</span>
        <Button
          size="sm"
          variant="ghost"
          onClick={onClose}
          className="h-6 w-6 p-0 hover:bg-muted-foreground/10"
        >
          <XIcon className="h-4 w-4" />
        </Button>
      </div>
      <div className="px-2 py-2 space-y-4 overflow-auto flex-1">
        <div className="flex justify-end">
          <HideInKioskMode>
            <Button
              className="float-right"
              size="xs"
              variant="outline"
              onClick={handleInsertSnippet}
            >
              Insert snippet
              <BetweenHorizontalStartIcon className="ml-2 h-4 w-4" />
            </Button>
          </HideInKioskMode>
        </div>
        {snippet.sections.map((section) => {
          const { code, html, id } = section;
          if (html) {
            return (
              <div key={`${snippet.title}-${id}`}>
                {renderHTML({ html: html })}
              </div>
            );
          }

          if (!code) {
            return null;
          }

          return (
            <div
              className="relative hover-actions-parent pr-2"
              key={`${snippet.title}-${id}`}
            >
              <HideInKioskMode>
                <Tooltip content="Insert snippet">
                  <Button
                    className="absolute -top-2 -right-1 z-10 hover-action px-2 bg-background"
                    size="sm"
                    variant="outline"
                    onClick={() => {
                      handleInsertCode(code);
                    }}
                  >
                    <BetweenHorizontalStartIcon className="h-5 w-5" />
                  </Button>
                </Tooltip>
              </HideInKioskMode>
              <Suspense>
                <LazyAnyLanguageCodeMirror
                  key={`${snippet.title}-${id}`}
                  theme={theme === "dark" ? "dark" : "light"}
                  language="python"
                  className="cm border rounded overflow-hidden"
                  extensions={extensions}
                  value={code}
                  readOnly={true}
                />
              </Suspense>
            </div>
          );
        })}
      </div>
    </>
  );
};

const SnippetList: React.FC<{
  onSelect: (snippet: Snippet) => void;
  snippets: Snippet[];
}> = ({ snippets, onSelect }) => {
  return (
    <CommandList className="flex flex-col overflow-auto">
      {snippets.map((snippet) => (
        <CommandItem
          className="rounded-none"
          key={snippet.title}
          onSelect={() => onSelect(snippet)}
        >
          <div className="flex flex-row gap-2 items-center">
            <span className="mt-1 text-accent-foreground">{snippet.title}</span>
          </div>
        </CommandItem>
      ))}
    </CommandList>
  );
};
