/**
 * Pure, client-side filtering for the shipping-rate settings grids
 * (shipping costs, overrides, commission tables, …).
 *
 * Rows are the pages' camelCase `rowData` objects. Every rule is tolerant of
 * missing fields: a row without the filtered field is treated as "no value" and
 * only matches when that filter is empty (except weight / validOn, which mirror
 * the billing semantics below).
 *
 * Weight: mirrors the billing rule — an open range (from = 0 AND to = 0) always
 * matches, otherwise `from <= w <= to`.
 * validOn: a row matches when it has a complete date range containing the day,
 * or no dates at all. One-sided ranges never match a validOn query.
 */

export type TriState = 'all' | 'yes' | 'no'

export interface RateFilterState {
  countryIds: number[]
  shipperId: number | ''
  organizationId: number | ''
  bpartnerId: number | ''
  serviceCode: string
  rateType: string
  weightKg: number | null
  active: TriState
  kleinpaket: TriState
  validOn: string
  text: string
}

export const emptyRateFilter = (): RateFilterState => ({
  countryIds: [],
  shipperId: '',
  organizationId: '',
  bpartnerId: '',
  serviceCode: '',
  rateType: '',
  weightKg: null,
  active: 'all',
  kleinpaket: 'all',
  validOn: '',
  text: ''
})

const num = (v: any): number => {
  const n = Number(v)
  return Number.isFinite(n) ? n : 0
}

const bool = (v: any): boolean => v === true || v === 'Y' || v === 'true' || v === 1

const idEq = (rowValue: any, wanted: number | '' | string): boolean => {
  if (wanted === '' || wanted === null || wanted === undefined) return true
  if (rowValue === '' || rowValue === null || rowValue === undefined) return false
  return String(rowValue) === String(wanted)
}

const strEq = (rowValue: any, wanted: string): boolean => {
  if (!wanted) return true
  return String(rowValue ?? '') === wanted
}

const triState = (rowValue: any, wanted: TriState): boolean => {
  if (wanted === 'all') return true
  return bool(rowValue) === (wanted === 'yes')
}

/** ISO 'yyyy-mm-dd' from either an ISO date, an ISO datetime or '' */
const isoDay = (v: any): string => {
  const m = String(v ?? '').match(/^(\d{4}-\d{2}-\d{2})/)
  return m ? m[1] : ''
}

export const weightMatches = (row: any, w: number | null): boolean => {
  if (w === null || w === undefined || !Number.isFinite(Number(w))) return true
  const from = num(row?.rangeWeightFrom ?? row?.rangeFrom)
  const to = num(row?.rangeWeightTo ?? row?.rangeTo)
  const weight = Number(w)
  return (from === 0 && to === 0) || (weight >= from && weight <= to)
}

export const validOnMatches = (row: any, day: string): boolean => {
  const d = isoDay(day)
  if (!d) return true
  const from = isoDay(row?.validFrom)
  const to = isoDay(row?.validTo)
  if (from && to) return d >= from && d <= to
  return !from && !to
}

export const textMatches = (row: any, text: string): boolean => {
  const q = String(text ?? '').trim().toLowerCase()
  if (!q) return true
  if (!row || typeof row !== 'object') return false
  for (const key of Object.keys(row)) {
    const v = row[key]
    if (v === null || v === undefined) continue
    const type = typeof v
    if (type !== 'string' && type !== 'number') continue
    if (String(v).toLowerCase().includes(q)) return true
  }
  return false
}

export const isRateFilterEmpty = (f: RateFilterState): boolean => {
  if (!f) return true
  return (
    !(f.countryIds && f.countryIds.length) &&
    f.shipperId === '' &&
    f.organizationId === '' &&
    f.bpartnerId === '' &&
    !f.serviceCode &&
    !f.rateType &&
    (f.weightKg === null || f.weightKg === undefined) &&
    (f.active ?? 'all') === 'all' &&
    (f.kleinpaket ?? 'all') === 'all' &&
    !f.validOn &&
    !String(f.text ?? '').trim()
  )
}

export const applyRateFilter = (rows: any[], f: RateFilterState): any[] => {
  const list = Array.isArray(rows) ? rows : []
  if (!f || isRateFilterEmpty(f)) return list.slice()

  const countrySet = new Set((f.countryIds || []).map(id => String(id)))

  return list.filter(row => {
    if (!row) return false
    if (countrySet.size) {
      const cid = row.countryId
      if (cid === '' || cid === null || cid === undefined || !countrySet.has(String(cid))) return false
    }
    if (!idEq(row.shipperId, f.shipperId)) return false
    if (!idEq(row.organizationId, f.organizationId)) return false
    if (!idEq(row.bpartnerId, f.bpartnerId)) return false
    if (!strEq(row.serviceCode, f.serviceCode)) return false
    if (!strEq(row.rateType, f.rateType)) return false
    if (!weightMatches(row, f.weightKg)) return false
    if (!triState(row.isActive, f.active ?? 'all')) return false
    if (!triState(row.isDHLKleinPaket, f.kleinpaket ?? 'all')) return false
    if (!validOnMatches(row, f.validOn)) return false
    if (!textMatches(row, f.text)) return false
    return true
  })
}
