"use client";

import * as React from "react";
import dynamic from "next/dynamic";
import type { Editor, TLStore } from "tldraw";

import { useCollaboration } from "@/lib/yjs-provider";
import { cn } from "@/lib/utils";

import { WhiteboardToolbar } from "./toolbar";
import { syncTldrawStoreWithYjs } from "./yjs-store";
import { customShapeUtils } from "./shapes";

import "tldraw/tldraw.css";

export type WhiteboardCanvasProps = {
  documentId?: string;
  className?: string;
  readOnly?: boolean;
};

function useHtmlDarkClass(): boolean {
  const [dark, setDark] = React.useState(false);

  React.useEffect(() => {
    const el = document.documentElement;
    const read = () => setDark(el.classList.contains("dark"));
    read();
    const observer = new MutationObserver(read);
    observer.observe(el, { attributes: true, attributeFilter: ["class"] });
    return () => observer.disconnect();
  }, []);

  return dark;
}

type TldrawModule = typeof import("tldraw");

function TldrawCollabBody({
  documentId,
  readOnly,
  className,
  tldraw,
}: {
  documentId: string;
  readOnly: boolean;
  className?: string;
  tldraw: TldrawModule;
}) {
  const { doc, isConnected, isSynced } = useCollaboration(`object:${documentId}`);
  const dark = useHtmlDarkClass();

  const store = React.useMemo<TLStore>(
    () => tldraw.createTLStore({ shapeUtils: customShapeUtils }),
    [documentId, tldraw],
  );

  React.useEffect(() => {
    if (!doc || !isSynced) return;
    return syncTldrawStoreWithYjs(store, doc);
  }, [doc, isSynced, store]);

  const editorRef = React.useRef<Editor | null>(null);

  React.useEffect(() => {
    editorRef.current?.updateInstanceState({ isReadonly: readOnly });
  }, [readOnly]);

  React.useEffect(() => {
    editorRef.current?.user.updateUserPreferences({
      colorScheme: dark ? "dark" : "light",
    });
  }, [dark]);

  const { Tldraw } = tldraw;

  const collab = React.useMemo(
    () => ({ isConnected, isSynced }),
    [isConnected, isSynced],
  );

  return (
    <Tldraw
      store={store}
      shapeUtils={customShapeUtils}
      className={cn("h-full min-h-0 w-full min-w-0", className)}
      inferDarkMode={false}
      onMount={(editor) => {
        editorRef.current = editor;
        editor.updateInstanceState({ isReadonly: readOnly });
        editor.user.updateUserPreferences({
          colorScheme: dark ? "dark" : "light",
        });
      }}
      components={{
        InFrontOfTheCanvas: () => (
          <WhiteboardToolbar collab={collab} readOnly={readOnly} />
        ),
      }}
    />
  );
}

function TldrawLocalBody({
  readOnly,
  className,
  tldraw,
}: {
  readOnly: boolean;
  className?: string;
  tldraw: TldrawModule;
}) {
  const dark = useHtmlDarkClass();
  const editorRef = React.useRef<Editor | null>(null);

  const store = React.useMemo<TLStore>(
    () => tldraw.createTLStore({ shapeUtils: customShapeUtils }),
    [tldraw],
  );

  React.useEffect(() => {
    editorRef.current?.updateInstanceState({ isReadonly: readOnly });
  }, [readOnly]);

  React.useEffect(() => {
    editorRef.current?.user.updateUserPreferences({
      colorScheme: dark ? "dark" : "light",
    });
  }, [dark]);

  const { Tldraw } = tldraw;

  return (
    <Tldraw
      store={store}
      shapeUtils={customShapeUtils}
      className={cn("h-full min-h-0 w-full min-w-0", className)}
      inferDarkMode={false}
      onMount={(editor) => {
        editorRef.current = editor;
        editor.updateInstanceState({ isReadonly: readOnly });
        editor.user.updateUserPreferences({
          colorScheme: dark ? "dark" : "light",
        });
      }}
      components={{
        InFrontOfTheCanvas: () => <WhiteboardToolbar readOnly={readOnly} />,
      }}
    />
  );
}

const TldynamicCollab = dynamic(
  () =>
    import("tldraw").then((tldraw) => {
      function CollabLoaded(
        props: Omit<WhiteboardCanvasProps, "documentId" | "readOnly"> & {
          documentId: string;
          readOnly: boolean;
        },
      ) {
        return <TldrawCollabBody {...props} tldraw={tldraw} />;
      }
      return CollabLoaded;
    }),
  { ssr: false, loading: () => <WhiteboardLoading /> },
);

const TldynamicLocal = dynamic(
  () =>
    import("tldraw").then((tldraw) => {
      function LocalLoaded(props: { readOnly: boolean; className?: string }) {
        return <TldrawLocalBody {...props} tldraw={tldraw} />;
      }
      return LocalLoaded;
    }),
  { ssr: false, loading: () => <WhiteboardLoading /> },
);

function WhiteboardLoading() {
  return (
    <div
      className="flex h-full min-h-[320px] w-full animate-pulse items-center justify-center rounded-lg border border-dashed border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.35)] text-xs text-muted-foreground"
      aria-hidden
    >
      Loading whiteboard…
    </div>
  );
}

export function WhiteboardCanvas({ documentId, className, readOnly = false }: WhiteboardCanvasProps) {
  const [client, setClient] = React.useState(false);
  React.useEffect(() => setClient(true), []);

  if (!client) {
    return (
      <div className={cn("relative flex min-h-0 flex-1 flex-col", className)}>
        <WhiteboardLoading />
      </div>
    );
  }

  return (
    <div
      className={cn(
        "relative flex min-h-0 flex-1 flex-col [&_.tl-container]:h-full [&_.tl-container]:min-h-0",
        className,
      )}
    >
      {documentId ? (
        <TldynamicCollab documentId={documentId} readOnly={!!readOnly} className={className} />
      ) : (
        <TldynamicLocal readOnly={!!readOnly} className={className} />
      )}
    </div>
  );
}
