import { computed, ref } from "vue";

import {
  BOM_USAGE_LABELS,
  DISPOSAL_TYPE_LABELS,
  INVENTORY_TXN_TYPE_LABELS,
  ITEM_TYPE_LABELS,
  MAINTENANCE_TYPE_LABELS,
  MINIAPP_ROLE_LABELS,
  PURCHASE_CALC_MODE_LABELS,
  PURCHASE_SOURCE_TYPE_LABELS,
  QUALITY_STATUS_LABELS,
  SOURCE_DOC_TYPE_LABELS,
  STATUS_LABELS,
  STOCKTAKE_ROW_STATUS_LABELS,
  WAREHOUSE_TYPE_LABELS,
  normalizeDictionaryKey
} from "./dictionaries";

const SEARCH_LABEL_MAPS = [
  STATUS_LABELS,
  QUALITY_STATUS_LABELS,
  DISPOSAL_TYPE_LABELS,
  INVENTORY_TXN_TYPE_LABELS,
  SOURCE_DOC_TYPE_LABELS,
  BOM_USAGE_LABELS,
  PURCHASE_CALC_MODE_LABELS,
  WAREHOUSE_TYPE_LABELS,
  ITEM_TYPE_LABELS,
  PURCHASE_SOURCE_TYPE_LABELS,
  MAINTENANCE_TYPE_LABELS,
  MINIAPP_ROLE_LABELS,
  STOCKTAKE_ROW_STATUS_LABELS
];

function normalizeValue(value) {
  if (value === null || value === undefined) {
    return "";
  }
  return String(value).toLowerCase();
}

function getFieldValue(row, field) {
  if (typeof field === "function") {
    return field(row);
  }
  return row?.[field];
}

function getMappedLabels(value) {
  if (typeof value !== "string") {
    return [];
  }

  const normalizedKey = normalizeDictionaryKey(value);
  return SEARCH_LABEL_MAPS.map((map) => map[normalizedKey] || map[value]).filter(Boolean);
}

function collectSearchValues(value, seen = new WeakSet()) {
  if (value === null || value === undefined) {
    return [];
  }

  if (["string", "number", "boolean", "bigint"].includes(typeof value)) {
    return [value, ...getMappedLabels(value)];
  }

  if (value instanceof Date) {
    return [value.toISOString()];
  }

  if (typeof value !== "object") {
    return [value];
  }

  if (seen.has(value)) {
    return [];
  }

  seen.add(value);

  if (Array.isArray(value)) {
    return value.flatMap((item) => collectSearchValues(item, seen));
  }

  return Object.values(value).flatMap((item) => collectSearchValues(item, seen));
}

function buildSearchFields(searchFields, sortOptions) {
  const fields = [...searchFields, ...sortOptions.map((option) => option.field || option.key).filter(Boolean)];
  const primitiveKeys = new Set();

  return fields.filter((field) => {
    if (typeof field === "function") {
      return true;
    }

    const key = String(field || "");
    if (!key || primitiveKeys.has(key)) {
      return false;
    }

    primitiveKeys.add(key);
    return true;
  });
}

function buildSearchText(row, fields, searchAllRowFields) {
  const values = searchAllRowFields || !fields.length
    ? collectSearchValues(row)
    : fields.flatMap((field) => collectSearchValues(getFieldValue(row, field)));

  return values.map(normalizeValue).join(" ");
}

function compareValues(left, right, direction) {
  const multiplier = direction === "asc" ? 1 : -1;
  const leftNumber = Number(left);
  const rightNumber = Number(right);

  if (!Number.isNaN(leftNumber) && !Number.isNaN(rightNumber)) {
    return (leftNumber - rightNumber) * multiplier;
  }

  return normalizeValue(left).localeCompare(normalizeValue(right), "zh-CN", { numeric: true }) * multiplier;
}

export function useTableControls(
  rowsRef,
  { searchFields = [], sortOptions = [], defaultSortKey = "", defaultSortDirection = "desc", searchAllRowFields = false } = {}
) {
  const search = ref("");
  const sortKey = ref(defaultSortKey);
  const sortDirection = ref(defaultSortDirection);
  const fields = buildSearchFields(searchFields, sortOptions);

  const rows = computed(() => {
    const keywords = normalizeValue(search.value)
      .trim()
      .split(/\s+/)
      .filter(Boolean);
    const filteredRows = keywords.length
      ? rowsRef.value.filter((row) => {
          const searchText = buildSearchText(row, fields, searchAllRowFields);
          return keywords.every((keyword) => searchText.includes(keyword));
        })
      : [...rowsRef.value];

    if (!sortKey.value) {
      return filteredRows;
    }

    const option = sortOptions.find((item) => item.key === sortKey.value);
    if (!option) {
      return filteredRows;
    }

    return [...filteredRows].sort((left, right) =>
      compareValues(getFieldValue(left, option.field || option.key), getFieldValue(right, option.field || option.key), sortDirection.value)
    );
  });

  return {
    search,
    sortKey,
    sortDirection,
    sortOptions,
    rows
  };
}
