/**
 * Gridify query-string builder.
 *
 * The backend list endpoints accept Gridify query params: `filter`, `orderBy`,
 * `page`, `pageSize`. This module turns typed UI filter state into a Gridify
 * `filter` string and assembles the query string.
 *
 * Gridify filter syntax (subset we use):
 *   field==value         equals
 *   field=*value*        contains  (append /i for case-insensitive)
 *   a==1,b==2            AND (comma)
 *   a==1|b==2            OR (pipe)
 * Special characters in values (comma, pipe, parens, =, <, >, !, asterisk,
 * backslash) must be escaped with a leading backslash.
 */

export interface GridifyQuery {
  /** Gridify filter expression, already composed. */
  filter?: string
  /** Gridify orderBy expression, e.g. "name" or "dateOfBirth desc". */
  orderBy?: string
  /** 1-based page number. */
  page?: number
  pageSize?: number
}

/** Escape a value for use inside a Gridify filter expression. */
export function escapeGridifyValue(value: string): string {
  return value.replace(/([,|()=<>!*\\])/g, '\\$1')
}

export type GridifyOp = '==' | '!=' | '>' | '<' | '>=' | '<=' | 'contains'

export interface FilterCondition {
  field: string
  op: GridifyOp
  value: string | number | boolean
  /** Case-insensitive match (appends /i). Only meaningful for string ops. */
  caseInsensitive?: boolean
}

/** Build a single Gridify condition, e.g. a name-contains or `status==Active`. */
export function condition(c: FilterCondition): string {
  const escaped = escapeGridifyValue(String(c.value))
  if (c.op === 'contains') {
    // Gridify's "contains" operator is `=*` — the value follows directly and is
    // matched as a substring. Do NOT wrap the value in asterisks (SQL-LIKE habit):
    // a trailing `*` is a literal character to Gridify, so `name=*a*` searches for
    // the literal "a*" and matches nothing. Correct form: `name=*a`.
    const suffix = c.caseInsensitive === false ? '' : '/i'
    return `${c.field}=*${escaped}${suffix}`
  }
  // Gridify's equals operator is a single '='; callers use '==' semantically.
  const op = c.op === '==' ? '=' : c.op
  const suffix = c.caseInsensitive ? '/i' : ''
  return `${c.field}${op}${escaped}${suffix}`
}

/** Join conditions with AND (comma). Falsy/empty conditions are dropped. */
export function andFilter(...conditions: Array<string | null | undefined | false>): string {
  return conditions.filter((c): c is string => Boolean(c)).join(',')
}

/** Assemble the URL query string ("?...") from a GridifyQuery. */
export function toQueryString(q: GridifyQuery): string {
  const params = new URLSearchParams()
  if (q.filter) params.set('filter', q.filter)
  if (q.orderBy) params.set('orderBy', q.orderBy)
  if (q.page != null) params.set('page', String(q.page))
  if (q.pageSize != null) params.set('pageSize', String(q.pageSize))
  const s = params.toString()
  return s ? `?${s}` : ''
}
