const DEFAULT_PAGE_SIZE = 20;
const MAX_PAGE_SIZE = 100;

const createBadRequestError = (message) => {
  const error = new Error(message);
  error.status = 400;
  return error;
};

const parsePositiveInteger = (value, fallback, maximum) => {
  const parsedValue = Number.parseInt(value, 10);

  if (!Number.isFinite(parsedValue) || parsedValue < 1) {
    return fallback;
  }

  return maximum ? Math.min(parsedValue, maximum) : parsedValue;
};

const parseFilterValue = (rawValue, filterConfig, filterKey) => {
  const type = filterConfig.type || "string";

  if (type === "number") {
    const parsedValue = Number(rawValue);
    if (!Number.isFinite(parsedValue)) {
      throw createBadRequestError(`مقدار فیلتر ${filterKey} معتبر نیست.`);
    }
    return parsedValue;
  }

  if (type === "boolean") {
    if (rawValue === "true") return true;
    if (rawValue === "false") return false;
    throw createBadRequestError(`مقدار فیلتر ${filterKey} معتبر نیست.`);
  }

  if (type === "date") {
    const parsedValue = new Date(rawValue);
    if (Number.isNaN(parsedValue.getTime())) {
      throw createBadRequestError(`تاریخ فیلتر ${filterKey} معتبر نیست.`);
    }

    if (filterConfig.endOfDay) {
      parsedValue.setUTCHours(23, 59, 59, 999);
    }

    return parsedValue;
  }

  return String(rawValue).trim();
};

const addFilterCondition = (query, field, operator, value) => {
  if (operator === "eq") {
    query[field] = value;
    return;
  }

  const mongoOperator = {
    gte: "$gte",
    lte: "$lte",
    gt: "$gt",
    lt: "$lt",
  }[operator];

  if (!mongoOperator) {
    throw createBadRequestError("عملگر فیلتر پشتیبانی نمی‌شود.");
  }

  const currentCondition =
    query[field] && typeof query[field] === "object" ? query[field] : {};

  query[field] = {
    ...currentCondition,
    [mongoOperator]: value,
  };
};

export const buildTableQuery = (
  rawQuery = {},
  {
    sortableFields = [],
    filters = {},
    defaultSort = { field: "createdAt", direction: "desc" },
    defaultPageSize = DEFAULT_PAGE_SIZE,
    maxPageSize = MAX_PAGE_SIZE,
  } = {},
) => {
  const currentPage = parsePositiveInteger(rawQuery.page, 1);
  const pageSize = parsePositiveInteger(
    rawQuery.pageSize,
    defaultPageSize,
    maxPageSize,
  );

  const requestedSortField = String(rawQuery.sortField || "").trim();
  const requestedSortDirection = String(
    rawQuery.sortDirection || "",
  ).toLowerCase();

  if (requestedSortField && !sortableFields.includes(requestedSortField)) {
    throw createBadRequestError("ستون مرتب‌سازی معتبر نیست.");
  }

  if (
    requestedSortDirection &&
    !["asc", "desc"].includes(requestedSortDirection)
  ) {
    throw createBadRequestError("جهت مرتب‌سازی معتبر نیست.");
  }

  const sortField = requestedSortField || defaultSort.field;
  const sortDirection =
    requestedSortDirection || defaultSort.direction || "desc";
  const sortValue = sortDirection === "asc" ? 1 : -1;
  const sort = {
    [sortField]: sortValue,
    ...(sortField === "_id" ? {} : { _id: sortValue }),
  };

  const filterQuery = {};

  Object.entries(filters).forEach(([filterKey, filterConfig]) => {
    const rawValue = rawQuery[`filter_${filterKey}`];

    if (
      rawValue === undefined ||
      rawValue === null ||
      String(rawValue).trim() === ""
    ) {
      return;
    }

    if (
      filterConfig.allowedValues &&
      !filterConfig.allowedValues.includes(String(rawValue))
    ) {
      throw createBadRequestError(`مقدار فیلتر ${filterKey} مجاز نیست.`);
    }

    let parsedValue = parseFilterValue(rawValue, filterConfig, filterKey);
    if (filterConfig.transform) {
      parsedValue = filterConfig.transform(parsedValue);
    }

    addFilterCondition(
      filterQuery,
      filterConfig.field || filterKey,
      filterConfig.operator || "eq",
      parsedValue,
    );
  });

  return {
    currentPage,
    pageSize,
    skip: (currentPage - 1) * pageSize,
    filterQuery,
    sort,
    sortField,
    sortDirection,
  };
};
