import { ref, computed } from "vue";

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

export const TEXT_LIKE_TYPES = ["text", "enum", "badge"];

export const OPERATOR_LABELS = {
  contains: "Bevat",
  starts_with: "Begint met",
  ends_with: "Eindigt met",
  equals: "Gelijk aan",
  not_equals: "Niet gelijk aan",
  lt: "Kleiner dan",
  lte: "Kleiner of gelijk aan",
  gt: "Groter dan",
  gte: "Groter of gelijk aan",
  date_equals: "Datum is",
  before: "Voor datum",
  after: "Na datum",
  between: "Tussen datums",
};

export const BASE_OPERATORS_BY_TYPE = {
  text: ["contains", "starts_with", "ends_with", "equals", "not_equals"],
  enum: ["contains", "starts_with", "ends_with", "equals", "not_equals"],
  badge: ["contains", "starts_with", "ends_with", "equals", "not_equals"],
  number: ["equals", "not_equals", "lt", "lte", "gt", "gte"],
  date: ["date_equals", "before", "after", "between"],
  select: ["equals", "not_equals"],
  boolean: ["equals", "not_equals"],
};

const DEFAULT_OPERATOR_BY_TYPE = {
  text: "contains",
  enum: "contains",
  badge: "contains",
  number: "equals",
  date: "date_equals",
  select: "equals",
  boolean: "equals",
};

// ---------------------------------------------------------------------------
// Pure helpers – exported for direct use by B2CDataTable & filter panel
// ---------------------------------------------------------------------------

export function getFilterScalar(v) {
  if (v == null) return "";
  if (typeof v === "object" && v !== null && "value" in v) return v.value;
  return v;
}

export function getFilterOp(v) {
  if (v == null || typeof v !== "object") return null;
  if (typeof v.op === "string" && v.op.trim() !== "") return v.op.trim();
  return null;
}

export function getFilterTypeForColumn(col) {
  if (!col) return "text";
  return col.filterType || col.type || "text";
}

export function getAllowedOperatorsForColumn(col) {
  if (!col) return BASE_OPERATORS_BY_TYPE.text;
  const type = getFilterTypeForColumn(col);
  const base = BASE_OPERATORS_BY_TYPE[type] || BASE_OPERATORS_BY_TYPE.text;
  if (Array.isArray(col.allowedOperators) && col.allowedOperators.length > 0) {
    const allowedSet = new Set(col.allowedOperators);
    const filtered = base.filter((op) => allowedSet.has(op));
    return filtered.length ? filtered : base;
  }
  return base;
}

export function getDefaultOperatorForColumn(col) {
  if (!col) return DEFAULT_OPERATOR_BY_TYPE.text;
  const type = getFilterTypeForColumn(col);
  return DEFAULT_OPERATOR_BY_TYPE[type] || DEFAULT_OPERATOR_BY_TYPE.text;
}

export function isBooleanColumn(col) {
  return !!col && getFilterTypeForColumn(col) === "boolean";
}

export function isSelectColumn(col) {
  return !!col && getFilterTypeForColumn(col) === "select";
}

export function isMultiSelectColumn(col) {
  return !!col && col.filterMultiple === true;
}

export function isDateColumn(col) {
  return !!col && getFilterTypeForColumn(col) === "date";
}

export function formatDateYmd(d) {
  if (!(d instanceof Date) || Number.isNaN(d.getTime())) return "";
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

export function parseYmdToDate(s) {
  const trimmed = String(s || "").trim();
  if (!/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) return null;
  const [y, m, d] = trimmed.split("-").map((x) => Number(x));
  const dt = new Date(y, m - 1, d);
  return Number.isNaN(dt.getTime()) ? null : dt;
}

export function getDateModelFromFilter(op, scalarValue) {
  if (op === "between") {
    const [startRaw, endRaw] = String(scalarValue || "").split("|");
    const start = parseYmdToDate((startRaw || "").trim());
    const end = parseYmdToDate((endRaw || "").trim());
    return [start, end];
  }
  return parseYmdToDate(String(scalarValue || "").trim());
}

export function getOperatorOptionsForColumn(col) {
  const ops = getAllowedOperatorsForColumn(col);
  return ops.map((op) => ({
    label: OPERATOR_LABELS[op] || op,
    value: op,
  }));
}

export function getBooleanValueOptionsForColumn(col) {
  if (col && col.valueMap && typeof col.valueMap === "object") {
    return Object.entries(col.valueMap).map(([raw, meta]) => ({
      value: raw,
      label: (meta && meta.label) || String(raw),
    }));
  }
  return [
    { value: "true", label: "Ja" },
    { value: "false", label: "Nee" },
  ];
}

export function getSelectValueOptionsForColumn(col) {
  if (!col) return [];
  if (Array.isArray(col.filterOptions) && col.filterOptions.length) {
    return col.filterOptions.map((opt) => ({
      value: String(opt.value),
      label: opt.label ?? String(opt.value),
    }));
  }
  if (col.valueMap && typeof col.valueMap === "object") {
    return Object.entries(col.valueMap).map(([raw, meta]) => ({
      value: raw,
      label: (meta && meta.label) || String(raw),
    }));
  }
  return [];
}

// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------

function generateId() {
  return typeof crypto !== "undefined" && crypto.randomUUID
    ? crypto.randomUUID()
    : `fr-${Date.now()}-${Math.random().toString(36).slice(2)}`;
}

// ---------------------------------------------------------------------------
// Composable
// ---------------------------------------------------------------------------

/**
 * Manages column-filter state and filter-row UI for B2CDataTable.
 *
 * @param {import('vue').ComputedRef<Array>} columnsRef        – all column definitions
 * @param {import('vue').ComputedRef<Array>} visibleOrderedRef – visible columns in user order
 */
export function useDataTableFilters(columnsRef, visibleOrderedRef) {
  const columnFilters = ref({});
  const filterRows = ref([]);

  const filterableColumns = computed(() =>
    visibleOrderedRef.value.filter((c) => c.filterable !== false),
  );

  function getColumnByKey(key) {
    return (columnsRef.value || []).find((c) => c.key === key) || null;
  }

  function resolveRowOperator(columnKey, filterValue) {
    const col = getColumnByKey(columnKey);
    const allowed = getAllowedOperatorsForColumn(col);
    const requested = getFilterOp(filterValue);
    if (requested && allowed.includes(requested)) return requested;
    const fallback = getDefaultOperatorForColumn(col);
    return allowed.includes(fallback) ? fallback : allowed[0];
  }

  function buildFilterRowsFromColumnFilters() {
    filterRows.value = Object.entries(columnFilters.value)
      .map(([columnKey, v]) => {
        const scalar = getFilterScalar(v);
        const op = resolveRowOperator(columnKey, v);
        const col = getColumnByKey(columnKey);
        if (col && isDateColumn(col)) {
          const valueString = String(scalar ?? "").trim();
          if (!valueString) return null;
          const dateModel = getDateModelFromFilter(op, valueString);
          return { columnKey, value: valueString, op, dateModel };
        }
        if (Array.isArray(scalar)) {
          const arr = scalar.map((s) => String(s ?? "").trim()).filter(Boolean);
          if (arr.length === 0) return null;
          return { columnKey, value: arr, op };
        }
        const value = String(scalar).trim();
        if (value === "") return null;
        return { columnKey, value, op };
      })
      .filter((entry) => {
        if (!entry) return false;
        if (Array.isArray(entry.value)) return entry.value.length > 0;
        return entry.value !== "";
      })
      .map(({ columnKey, value, op, dateModel }) => ({
        id: generateId(),
        columnKey,
        op,
        value,
        dateModel,
      }));
  }

  function updateColumnFiltersFromFilterRows() {
    const next = {};
    filterRows.value.forEach((row) => {
      if (!row.columnKey) return;
      const col = getColumnByKey(row.columnKey);
      if (col && isDateColumn(col)) {
        const model = row.dateModel;
        if (Array.isArray(model)) {
          const start = model[0] instanceof Date ? formatDateYmd(model[0]) : "";
          const end = model[1] instanceof Date ? formatDateYmd(model[1]) : "";
          if (start && end) {
            next[row.columnKey] = { value: `${start}|${end}`, op: "between" };
            return;
          }
          if (start) {
            next[row.columnKey] = { value: start, op: "date_equals" };
            return;
          }
          return;
        }
        if (model instanceof Date) {
          const v = formatDateYmd(model);
          if (!v) return;
          next[row.columnKey] = { value: v, op: "date_equals" };
          return;
        }
        const raw = String(row.value || "").trim();
        if (raw) {
          next[row.columnKey] = { value: raw, op: row.op || "date_equals" };
        }
        return;
      }
      if (Array.isArray(row.value)) {
        const arr = row.value
          .map((s) => String(s ?? "").trim())
          .filter(Boolean);
        if (arr.length === 0) return;
        next[row.columnKey] =
          row.op && typeof row.op === "string"
            ? { value: arr, op: row.op }
            : { value: arr };
        return;
      }

      const trimmed = String(row.value || "").trim();
      if (trimmed !== "") {
        next[row.columnKey] =
          row.op && typeof row.op === "string"
            ? { value: trimmed, op: row.op }
            : { value: trimmed };
      }
    });
    columnFilters.value = next;
  }

  function getAvailableFilterColumnsForRow(rowId) {
    const used = new Set(
      filterRows.value
        .filter((r) => r.id !== rowId && r.columnKey)
        .map((r) => r.columnKey),
    );
    return filterableColumns.value.filter((col) => !used.has(col.key));
  }

  function addFilterRow() {
    const available = getAvailableFilterColumnsForRow(null);
    const first = available[0] || null;
    if (!first) return;
    filterRows.value = [
      ...filterRows.value,
      {
        id: generateId(),
        columnKey: first ? first.key : null,
        op: first
          ? getDefaultOperatorForColumn(first)
          : getDefaultOperatorForColumn(null),
        value: "",
      },
    ];
  }

  function removeFilterRow(id) {
    filterRows.value = filterRows.value.filter((r) => r.id !== id);
    updateColumnFiltersFromFilterRows();
  }

  function setFilterRowColumnKey(rowId, columnKey) {
    const row = filterRows.value.find((r) => r.id === rowId);
    if (row) {
      if (columnKey) {
        const existsInOtherRow = filterRows.value.some(
          (r) => r.id !== rowId && r.columnKey === columnKey,
        );
        if (existsInOtherRow) return;
      }
      row.columnKey = columnKey;
      const col = getColumnByKey(columnKey);
      const allowed = getAllowedOperatorsForColumn(col);
      if (!allowed.includes(row.op)) {
        row.op = getDefaultOperatorForColumn(col);
      }
      updateColumnFiltersFromFilterRows();
    }
  }

  function setFilterRowValue(rowId, value) {
    const row = filterRows.value.find((r) => r.id === rowId);
    if (row) {
      row.value = value;
      updateColumnFiltersFromFilterRows();
    }
  }

  function setFilterRowDateModel(rowId, model) {
    const row = filterRows.value.find((r) => r.id === rowId);
    if (row) {
      row.dateModel = model;
      updateColumnFiltersFromFilterRows();
    }
  }

  function setFilterRowOperator(rowId, op) {
    const row = filterRows.value.find((r) => r.id === rowId);
    if (row) {
      row.op = op;
      updateColumnFiltersFromFilterRows();
    }
  }

  function clearFilters() {
    columnFilters.value = {};
    filterRows.value = [];
  }

  return {
    columnFilters,
    filterRows,
    filterableColumns,
    getColumnByKey,
    resolveRowOperator,
    buildFilterRowsFromColumnFilters,
    getAvailableFilterColumnsForRow,
    addFilterRow,
    removeFilterRow,
    setFilterRowColumnKey,
    setFilterRowValue,
    setFilterRowDateModel,
    setFilterRowOperator,
    clearFilters,
  };
}
