import { readonly, shallowReactive } from 'vue';
import { type Time } from '@silane/datetime';

import {
  FileNotFoundObjectFileSystemError, type useObjectFileSystem,
} from '../objectfilesystem';
import { comparerByKey } from '../../common/utils';
import { getObjectID } from '../../common/zkan-utils';


export type TimeLabel = {
  id: string, label: string,
  startTime: Time, endTime: Time, disabled: boolean,
}

export interface AddTimeLabelStoreMutationOp {
  opcode: 'add';
  param: {
    readonly __id?: string, readonly label: string,
    readonly startTime: Time, readonly endTime: Time,
    readonly disabled: boolean,
  };
}
export interface UpdateTimeLabelStoreMutationOp {
  opcode: 'update';
  param: {
    readonly id: string,
    readonly label: string, readonly disabled: boolean,
  };
}
export interface RemoveTimeLabelStoreMutationOp {
  opcode: 'remove';
  param: { readonly id: string };
}
export interface ClearTimeLabelStoreMutationOp {
  opcode: 'clear';
  param: unknown;
}
export type TimeLabelStoreMutationOp = (
  AddTimeLabelStoreMutationOp | UpdateTimeLabelStoreMutationOp |
  RemoveTimeLabelStoreMutationOp | ClearTimeLabelStoreMutationOp
);

export async function useTimeLabelStore(
  objectFileSystem: ReturnType<typeof useObjectFileSystem>
) {
  const filePath = '/timelabel.json';

  const timeLabels= shallowReactive<TimeLabel[]>([]);

  function sortTimeLabels() {
    timeLabels.sort(comparerByKey(x => x.label));
  }

  async function loadFromFile() {
    let savedData;
    try {
      savedData = await objectFileSystem.get(filePath);
    } catch(e) {
      if(!(e instanceof FileNotFoundObjectFileSystemError)) {
        throw e;
      }
      savedData = { timeLabels: [] };
    }
    timeLabels.splice(0, timeLabels.length, ...savedData.timeLabels);
    sortTimeLabels();
  }
  await loadFromFile();

  async function mutate(ops: readonly TimeLabelStoreMutationOp[]) {
    const tmpIdMap = new Map<string, string>();

    try {
      for(const op of ops) {
        if(op.opcode === 'add') {
          const newId = getObjectID();
          if(op.param.__id != null) {
            tmpIdMap.set(op.param.__id, newId);
          }
          timeLabels.push({
            id: newId, label: op.param.label,
            startTime: op.param.startTime, endTime: op.param.endTime,
            disabled: op.param.disabled,
          });
        } else if(op.opcode === 'update') {
          const index = timeLabels.findIndex(x => x.id === op.param.id);
          if(index >= 0) {
            timeLabels[index] = {
              ...timeLabels[index],
              label: op.param.label, disabled: op.param.disabled,
            };
          }
        } else if(op.opcode === 'remove') {
          const index = timeLabels.findIndex(x => x.id === op.param.id);
          if(index >= 0) {
            timeLabels.splice(index, 1);
          }
        } else if(op.opcode === 'clear') {
          timeLabels.splice(0, timeLabels.length);
        }
      }
      sortTimeLabels();
      await objectFileSystem.put(filePath, { timeLabels });
      return {
        idMapping: [...tmpIdMap.entries()],
      };
    } catch(e) {
      await loadFromFile();
      throw e;
    }

  }

  async function query(param: {}) {
    return timeLabels;
  }

  async function export_() {
    return timeLabels;
  }

  async function import_(value: TimeLabel[]) {
    await objectFileSystem.put(filePath, { timeLabels: value });
    timeLabels.splice(0, timeLabels.length, ...value);
    sortTimeLabels();
  }

  return {
    mutate, query, export: export_, import: import_,
    timeLabels: readonly(timeLabels),
  };
}

export type TimeLabelStore = Awaited<ReturnType<typeof useTimeLabelStore>>;
