import { serialize, deserialize } from "../common/serializer";


export function useDatabase() {
  const worker = new SharedWorker(new URL(
    '../sharedworker/index.js', import.meta.url
  ), { type: 'module' });

  const pendingQueries: {
    queryID: number,
    resolve: (value: unknown) => void, reject: (reason?: any) => void,
  }[] = [];
  const pendingMutations: {
    mutationID: number,
    resolve: (value: unknown) => void, reject: (reason?: any) => void,
  }[] = [];

  worker.port.addEventListener('message', e => {
    const message = deserialize(e.data);

    if(message.code === 'query_result') {
      const idx = pendingQueries.findIndex(
        x => x.queryID === message.payload.queryID
      );

      if(idx >= 0) {
        const pendingQuery = pendingQueries.splice(idx, 1)[0];
        if(message.payload.code === 'success') {
          pendingQuery.resolve(message.payload.payload);
        } else {
          pendingQuery.reject({
            code: message.payload.code,
            payload: message.payload.payload,
          });
        }
      }
    }

    if(message.code === 'mutate_result') {
      const idx = pendingMutations.findIndex(
        x => x.mutationID === message.payload.mutationID
      );

      if(idx >= 0) {
        const pendingMutation = pendingMutations.splice(idx, 1)[0];
        if(message.payload.code === 'success') {
          pendingMutation.resolve(message.payload.payload);
        } else {
          pendingMutation.reject({
            code: message.payload.code,
            payload: message.payload.payload,
          });
        }
      }
    }
  });
  worker.port.start();

  let queryID = 0;
  function query(scope: string, param: unknown): Promise<unknown> {
    const promise = new Promise((resolve, reject) => {
      pendingQueries.push({ queryID, resolve, reject });
    });

    worker.port.postMessage({ code: 'query', payload: {
      queryID, scope, param: serialize(param),
    }});

    ++queryID;

    return promise;
  }

  let mutationID = 0;
  function mutate(scope: string, ops: unknown): Promise<unknown> {
    const promise = new Promise((resolve, reject) => {
      pendingMutations.push({ mutationID, resolve, reject });
    });

    worker.port.postMessage({ code: 'mutate', payload: {
      mutationID, scope, ops: serialize(ops),
    }});

    ++mutationID;

    return promise;
  }

  return { query, mutate };
}

export type Database = Awaited<ReturnType<typeof useDatabase>>;
