import type { CrudListQuery } from './contracts'

const positiveInt = (value: string | null, fallback: number): number => {
  const parsed = Number(value)
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
}

export interface CrudQueryCodecOptions {
  readonly allowedSortFields?: ReadonlySet<string>
  readonly allowedFilterFields?: ReadonlySet<string>
  readonly allowedFilterOperators?: ReadonlySet<string>
}

export function decodeCrudQuery(params: URLSearchParams, fallback: CrudListQuery, options: CrudQueryCodecOptions = {}): CrudListQuery {
  const sorts = params.getAll('sort').flatMap(value => {
    const parts = value.split(':')
    const field = parts[0]
    const direction = parts[1]
    if (field && (direction === 'asc' || direction === 'desc') && (!options.allowedSortFields || options.allowedSortFields.has(field))) {
      return [{ field, direction: direction as 'asc' | 'desc' }]
    }
    return []
  })
  const filters = params.getAll('filter').flatMap(value => {
    const first = value.indexOf(':')
    const second = value.indexOf(':', first + 1)
    if (first <= 0 || second <= first) return []
    const field = value.slice(0, first)
    const operator = value.slice(first + 1, second)
    if (options.allowedFilterFields && !options.allowedFilterFields.has(field)) return []
    if (options.allowedFilterOperators && !options.allowedFilterOperators.has(operator)) return []
    return [{ field, operator, value: value.slice(second + 1) }]
  })
  return {
    page: positiveInt(params.get('page'), fallback.page),
    pageSize: positiveInt(params.get('pageSize'), fallback.pageSize),
    search: params.get('search')?.trim() || undefined,
    sorts: sorts.length ? sorts : fallback.sorts,
    filters: filters.length ? filters : fallback.filters
  }
}

export function encodeCrudQuery(query: CrudListQuery): URLSearchParams {
  const params = new URLSearchParams()
  params.set('page', String(query.page))
  params.set('pageSize', String(query.pageSize))
  if (query.search) params.set('search', query.search)
  for (const sort of query.sorts) params.append('sort', `${sort.field}:${sort.direction}`)
  for (const filter of query.filters) params.append('filter', `${filter.field}:${filter.operator}:${String(filter.value ?? '')}`)
  return params
}
