import mitt from "mitt";
import { SingleHistory } from "../history";
import { installGlobalVar } from "./use-global-vars";
import { DrawData } from "../components";
import { Ref, ref, watch } from "vue";
import { copy } from "@/utils/shared";
import { useStoreRaw } from "../store/store";

type HistoryItem = { attachs: string; data: string };
class DrawHistory {
  history = new SingleHistory<HistoryItem>();
  hasUndo = this.history.hasUndo;
  hasRedo = this.history.hasRedo;
  get list() {
    return this.history.list()
  }
  get currentId() {
    return this.history.currentId
  }
  get current() {
    return this.list.find(item => item.id === this.currentId)?.data
  }

  preventFlag = false;
  onceFlag = false;
  onceHistory: string | null = null;
  initData: string | null = null;
  renderer: (data: HistoryItem) => void;
  bus = mitt<{
    attachs: Record<string, any>;
    renderer: void;
    push: void;
    pushed: void;
    redo: void;
    undo: void;
  }>();
  private pushAttachs: Record<string, any> = {};

  constructor(renderer: (data: string) => void) {
    this.renderer = ({ data, attachs }: HistoryItem) => {
      renderer(data);
      this.bus.emit("renderer");
      this.bus.emit("attachs", attachs ? JSON.parse(attachs) : {});
    };
  }
  
  clearHistoryAttach(id: string, name: string) {
    const history = this.history.history as any
    if (!history.$chunks[id]) return
    const data = JSON.parse(history.$chunks[id]).data as HistoryItem
    if (!data.attachs) return;
    const attachs = JSON.parse(data.attachs)
    if (attachs[name]) {
      delete attachs[name]
      data.attachs = JSON.stringify(attachs)
      history.$chunks[id] = JSON.stringify({ data })
    }
  }

  setPushAttach(name: string, data: any) {
    this.pushAttachs[name] = data;
  }

  setInit(data: string) {
    this.initData = data;
    this.history.reset();
    this.push(data);
  }

  preventTrack(fn: () => void) {
    this.preventFlag = true;
    fn();
    this.preventFlag = false;
  }

  private saveKeyPrev = '__history__'
  private saveKeyId: string | null = null
  get saveKey() {
    if (!this.saveKeyId) {
      throw '未设置本地保存key'
    }
    return this.saveKeyPrev + this.saveKeyId
  }
  setLocalId(id: string) {
    this.saveKeyId = id
  }

  getLocalId() {
    return this.saveKeyId
  }
  
  saveLocal() {
    localStorage.setItem(this.saveKey, JSON.stringify(this.list.map(item => item.data)))
  }

  clearLocal() {
    localStorage.removeItem(this.saveKey)
  }

  hasLocal() {
    for (let i = 0, len = localStorage.length; i < len; i++) {
      if (localStorage.key(i) === this.saveKey) {
        return true
      }
    }
    return false
  }

  loadLocalStorage() {
    const list = JSON.parse(localStorage.getItem(this.saveKey)!)
    if (!list.length) {
      this.clear()
      return;
    }

    this.history.reset()
    this.setInit(list[0].data)
    for (let i = 1; i < list.length; i++) {
      this.push(list[i].data)
    }
    this.renderer(list[list.length - 1])
  }

  push(data: string) {
    if (this.preventFlag) return;
    if (this.onceFlag) {
      this.onceHistory = data;
    } else if (data !== this.current?.data) {
      // console.log(data, this.current?.data)
      this.bus.emit("push");
      this.history.push({ attachs: JSON.stringify(this.pushAttachs), data });
      this.pushAttachs = {};
      this.bus.emit("pushed");
    }
  }

  redo() {
    const data = this.history.redo();
    this.bus.emit("redo");
    this.renderer(data);
    return data;
  }

  undo() {
    const data = this.history.undo();
    this.bus.emit("undo");
    this.renderer(data);
    return data;
  }

  onceTrack(fn: () => void) {
    this.onceFlag = true;
    fn();
    if (this.onceHistory) {
      this.push(this.onceHistory);
      this.onceHistory = null;
    }
    this.onceFlag = false;
  }

  clearCurrent(): void {
    this.renderer({ data: "", attachs: "" });
    this.push("");
  }

  clear(): void {
    this.history.reset()
    this.clearCurrent()
  }
  
  init() {
    if (this.initData) {
      this.renderer({ data: this.initData, attachs: "" });
      this.push(this.initData);
    }
  }
}

export const useHistory = installGlobalVar(() => {
  const store = useStoreRaw();
  const history = new DrawHistory((dataStr: string) => {
    const data: DrawData = dataStr ? JSON.parse(dataStr) : {};
    store.$patch((state) => {
      state.data = data;
    });
  });
  return history;
}, Symbol("history"));

export const useHistoryAttach = <T>(
  name: string,
  isRuning: Ref<boolean> = ref(true),
  getInit: () => T,
  cleanup = true
) => {
  const history = useHistory();
  const current = ref<T>(copy(getInit()!));
  const setIds = [] as string[]

  const addSetIds = () => setIds.push(history.currentId)
  const pushHandler = () => {
    history.setPushAttach(name, current.value);
  };
  const attachsHandler = (attachs: any) => {
    current.value = attachs && attachs[name] ? attachs[name] : getInit();
  };
  const cleanupAttach = () => {
    setIds.forEach(id => history.clearHistoryAttach(id, name))
    setIds.length = 0
  }

  watch(
    isRuning,
    (isRun, _, onCleanup) => {
      console.log('isRun', isRun)
      if (!isRun) return;
      history.bus.on("push", pushHandler);
      history.bus.on("attachs", attachsHandler);

      if (cleanup) {
        history.bus.on('pushed', addSetIds)
      }

      onCleanup(() => {
        console.log('clear')
        history.bus.off("push", pushHandler);
        history.bus.off("attachs", attachsHandler);
        current.value = void 0;
        
        if (cleanup) {
          history.bus.off('pushed', pushHandler);
          cleanupAttach()
        }
      });
    },
    { immediate: true, flush: "sync" }
  );

  return current;
};
