/**
 * SolidJS bindings for Yjs
 * Provides reactive primitives for collaborative editing
 */

import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js';
import { Text, Map as YMap, Array as YArray, Doc, YEvent } from 'yjs';

/**
 * Create a reactive binding to a Yjs Text instance
 * Automatically syncs changes between local state and Yjs document
 */
export function useYText(yText: Text) {
  const [text, setText] = createSignal(yText.toString());

  const observer = (event: any) => {
    setText(yText.toString());
  };

  yText.observe(observer);

  onCleanup(() => {
    yText.unobserve(observer);
  });

  const updateText = (newText: string) => {
    yText.delete(0, yText.length);
    yText.insert(0, newText);
  };

  return {
    text,
    updateText,
    yText,
  };
}

/**
 * Create a reactive binding to a Yjs Map
 */
export function useYMap<T extends Record<string, any>>(yMap: YMap<T>) {
  const [data, setData] = createSignal<T>(yMap.toJSON() as unknown as T);

  const observer = (_event: YEvent<YMap<T>>) => {
    setData(() => yMap.toJSON() as unknown as T);
  };

  yMap.observe(observer);

  onCleanup(() => {
    yMap.unobserve(observer);
  });

  const updateMap = (updates: Partial<T>) => {
    Object.entries(updates).forEach(([key, value]) => {
      yMap.set(key, value as T[string & keyof T]);
    });
  };

  const setValue = <K extends string & keyof T>(key: K, value: T[K]) => {
    yMap.set(key, value);
  };

  const getValue = <K extends string & keyof T>(key: K): T[K] | undefined => {
    return yMap.get(key) as T[K] | undefined;
  };

  return {
    data,
    updateMap,
    setValue,
    getValue,
    yMap,
  };
}

/**
 * Create a reactive binding to a Yjs Array
 */
export function useYArray<T>(yArray: YArray<T>) {
  const [items, setItems] = createSignal<T[]>(yArray.toArray());

  const observer = (_event: YEvent<YArray<T>>) => {
    setItems(yArray.toArray());
  };

  yArray.observe(observer);

  onCleanup(() => {
    yArray.unobserve(observer);
  });

  const updateArray = (updates: T[]) => {
    yArray.delete(0, yArray.length);
    yArray.insert(0, updates);
  };

  const pushItem = (item: T) => {
    yArray.push([item]);
  };

  const removeItem = (index: number) => {
    yArray.delete(index);
  };

  return {
    items,
    updateArray,
    pushItem,
    removeItem,
    yArray,
  };
}

/**
 * Create a collaborative editor binding
 * Binds a Yjs Text instance to a SolidJS component
 */
export function useCollaborativeText(yText: Text) {
  const { text, updateText } = useYText(yText);

  const handleChange = (newText: string) => {
    updateText(newText);
  };

  return {
    text,
    handleChange,
  };
}

/**
 * Create a reactive document binding
 * Provides access to all shared types in a Yjs Doc
 */
export function useCollaborativeDoc(doc: Doc) {
  const getText = createMemo(() => doc.getText('main'));
  const getMetadata = createMemo(() => doc.getMap('metadata'));
  const getCharacters = createMemo(() => doc.getMap('characters'));
  const getScenes = createMemo(() => doc.getMap('scenes'));

  const text = useYText(getText());
  const metadata = useYMap(getMetadata() as YMap<Record<string, any>>);
  const characters = useYMap(getCharacters() as YMap<Record<string, any>>);
  const scenes = useYMap(getScenes() as YMap<Record<string, any>>);

  return {
    doc,
    text,
    metadata,
    characters,
    scenes,
  };
}
