import { History } from "stateshot";
import { computed, reactive, ref } from "vue";

export type HistoryState = {
  hasUndo: boolean;
  hasRedo: boolean;
};

export type StateCallback = (state: HistoryState) => void;

export class SingleHistory<T = any> {
  history: History<{ data: T }>;
  hasUndo = ref(false);
  hasRedo = ref(false);

  constructor() {
    this.history = new History();
  }

  list() {
    const history: any = this.history;
    return history.$records.map((item: any) => {
      const id = item.hashes[0];
      return { id, data: JSON.parse(history.$chunks[item.hashes[0]]).data };
    }) as {id: string, data: T}[];
  }

  get currentId(): string {
    const history: any = this.history;
    return history.$records[history.$index].hashes[0];
  }

  private syncState() {
    this.hasRedo.value = this.history.hasRedo;
    this.hasUndo.value = this.history.hasUndo;
  }

  get data() {
    return this.history.get()?.data;
  }

  undo(): T {
    if (this.history.hasUndo) {
      this.history.undo();
      this.syncState();
    }
    return this.data;
  }

  redo(): T {
    if (this.history.hasRedo) {
      this.history.redo();
      this.syncState();
    }
    return this.data;
  }

  push(data: T) {
    this.history.pushSync({ data });
    this.syncState();
  }

  reset() {
    this.history.reset();
  }
}

export class MergeHistory<T> {
  historyStack: SingleHistory<T>[] = reactive([]);
  current = computed(() => this.historyStack[this.historyStack.length - 1]);
  hasUndo = computed(() => this.current.value.hasUndo);
  hasRedo = computed(() => this.current.value.hasRedo);

  branch() {
    const single = new SingleHistory();
    this.historyStack.push(single);
  }

  merge() {
    const lastStack = this.historyStack.pop()!;
    if (lastStack.hasUndo.value) {
      this.current.value.push(lastStack.data);
    }
    lastStack.reset();
  }

  undo() {
    return this.current.value.undo();
  }

  redo() {
    return this.current.value.redo();
  }

  push(data: T) {
    return this.current.value.push(data);
  }

  clear() {
    return this.current.value.reset();
  }

  get data() {
    return this.current.value.data;
  }

  destory() {
    this.historyStack.length = 0;
  }
}
