import {
  DEFAULT_BATCH_INTERVAL,
  DEFAULT_BATCH_SIZE,
  DEFAULT_BATCH_LENGTH,
} from "../config";

/**
 * 批处理队列
 * @param {*} args
 * @param {*} param1
 * @returns
 */
const BatchQueue = (
  { taskFn, ...args },
  {
    batchInterval = DEFAULT_BATCH_INTERVAL,
    batchSize = DEFAULT_BATCH_SIZE,
    batchLength = DEFAULT_BATCH_LENGTH,
  } = {}
) => {
  const queue = [];
  let isProcessing = false;
  let timer = null;

  const sendBatchRequest = async (payloads) => {
    const texts = payloads.map((item) => item.text);
    return taskFn({
      ...args,
      texts,
    });
  };

  const processQueue = async () => {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }

    if (queue.length === 0 || isProcessing) {
      return;
    }

    isProcessing = true;

    let tasksToProcess = [];
    let currentBatchLength = 0;
    let endIndex = 0;

    for (const task of queue) {
      const textLength = task.payload.text?.length || 0;
      if (
        endIndex >= batchSize ||
        (currentBatchLength + textLength > batchLength && endIndex > 0)
      ) {
        break;
      }
      currentBatchLength += textLength;
      endIndex++;
    }

    if (endIndex > 0) {
      tasksToProcess = queue.splice(0, endIndex);
    }

    if (tasksToProcess.length === 0) {
      isProcessing = false;
      return;
    }

    try {
      const payloads = tasksToProcess.map((item) => item.payload);
      const responses = await sendBatchRequest(payloads);

      tasksToProcess.forEach((taskItem, index) => {
        const response = responses[index];
        if (response) {
          taskItem.resolve(response);
        } else {
          taskItem.reject(new Error(`No response for item at index ${index}`));
        }
      });
    } catch (error) {
      tasksToProcess.forEach((taskItem) => taskItem.reject(error));
    } finally {
      isProcessing = false;
      if (queue.length > 0) {
        if (queue.length >= batchSize) {
          setTimeout(processQueue, 0);
        } else {
          scheduleProcessing();
        }
      }
    }
  };

  const scheduleProcessing = () => {
    if (!isProcessing && !timer && queue.length > 0) {
      timer = setTimeout(processQueue, batchInterval);
    }
  };

  const addTask = (data) => {
    return new Promise((resolve, reject) => {
      const payload = data;
      queue.push({ payload, resolve, reject });

      if (queue.length >= batchSize) {
        processQueue();
      } else {
        scheduleProcessing();
      }
    });
  };

  const destroy = () => {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    queue.forEach((task) =>
      task.reject(new Error("Queue instance was destroyed."))
    );
    queue.length = 0;
  };

  return { addTask, destroy };
};

// 实例字典
const queueMap = new Map();

/**
 * 获取批处理实例
 * @param {*} translator
 * @returns
 */
export const getBatchQueue = (args, opts) => {
  const { translator, from, to } = args;
  const key = `${translator}_${from}_${to}`;
  if (queueMap.has(key)) {
    return queueMap.get(key);
  }

  const queue = BatchQueue(args, opts);
  queueMap.set(key, queue);
  return queue;
};

/**
 * 清除所有任务
 */
export const clearAllBatchQueue = () => {
  for (const queue of queueMap.values()) {
    queue.destroy();
  }
};
