import moment, { type Moment } from 'moment'
import { type paramsSerializationOptions } from '../../composables/use-endless-scroll-api'
import {
  isCalendarHeaderInterface,
  isDefaultSelectHeaderInterface,
  isEnumSelectHeaderInterface,
  isMultipleDefaultSelectHeaderInterface,
  isMultipleEnumSelectHeaderInterface,
  isMultipleRelationSelectHeaderInterface, isPeriodCalendarHeaderInterface,
  isRangeNumberHeaderInterface,
  isRelationSelectHeaderInterface,
  isTextSearchHeaderInterface,
} from '../tables/headers/HeadersTableInterfaces'
import {
  isCheckboxSelectHeaderInterface,
  isRadioSelectHeaderInterface,
  isRelationExtendedInterface,
  isWeekCalendarHeaderInterface,
  type FilterInterface,
} from './FilterInterfaces'
import {
  FilterCalendar,
  FilterCheckboxSelect,
  FilterDefaultSelect, FilterDefaultSelectMultiple,
  FilterEnumSelect, FilterEnumSelectMultiple,
  FilterPeriodCalendar,
  FilterRadioSelect, FilterRangeNumber,
  FilterRelationSelect, FilterRelationSelectMultiple,
  FilterText,
  FilterTextSearch,
  FilterWeekCalendar,
} from './filters/index'

export interface IFilter {
  label: string
  addValue: (value: any) => void
  removeValue?: (value: any) => void
  removeValues?: () => void
  hasValues: () => boolean
  getValues: () => any[]
  showValues: () => string
  getQueryParams: () => { [code: string]: string | number | Array<string | number> }
  setParams?: (params: { [code: string]: string | string[] }) => void
  removeParams?: () => void
  render: () => JSX.Element
}

export type FilterValue = string | { id: string, name: string }

export class DefaultFilter {
  label: string

  constructor({ label }: FilterInterface) {
    this.label = label
  }
}

export class DefaultRelationFilter extends DefaultFilter {
  searchKey?: string

  params?: { [code: string]: string | string[] }

  itemConverter?: (v: any) => { id: string, name: string }

  paramsSerialization?: paramsSerializationOptions

  requestPageKey?: string

  requestPerPageKey?: string

  responseItemsKey?: string

  responseTotalKey?: string

  paginationType?: string

  additionalItems?: { id: string, name: string }[]

  constructor(f: FilterInterface) {
    super(f)

    if (isRelationExtendedInterface(f.filter)) {
      if (f.filter.searchKey) this.searchKey = f.filter.searchKey
      if (f.filter.params) this.params = f.filter.params
      if (f.filter.itemConverter) this.itemConverter = f.filter.itemConverter
      if (f.filter.paramsSerialization) this.paramsSerialization = f.filter.paramsSerialization
      if (f.filter.requestPageKey) this.requestPageKey = f.filter.requestPageKey
      if (f.filter.requestPerPageKey) this.requestPerPageKey = f.filter.requestPerPageKey
      if (f.filter.responseItemsKey) this.responseItemsKey = f.filter.responseItemsKey
      if (f.filter.paginationType) this.paginationType = f.filter.paginationType
      if (f.filter.additionalItems) this.additionalItems = f.filter.additionalItems
    }
  }
}

export class TextFilter extends DefaultFilter implements IFilter {
  addValue(value: FilterValue) {
    if (typeof value !== 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.label = value
  }

  // removeValue() {}
  //
  // removeValues() {}

  // eslint-disable-next-line class-methods-use-this
  hasValues() {
    return false
  }

  getValues() {
    return [this.label]
  }

  showValues() {
    return String([this.label].length)
  }

  // eslint-disable-next-line class-methods-use-this
  getQueryParams(): { [code: string]: string | string[] } {
    return {}
  }

  render() {
    return (
      <FilterText filter={this} />
    )
  }
}

export class TextSearchFilter extends DefaultFilter implements IFilter {
  value = ''

  draftValue = ''

  searchParam!: string

  constructor(f: FilterInterface) {
    super(f)

    if (!isTextSearchHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр TextTableFilter с типом ${f.filter.type}`)
    }

    this.searchParam = f.filter.searchParam
  }

  addValue(value: FilterValue) {
    if (typeof value !== 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.draftValue = value
  }

  removeValue() {
    this.draftValue = ''
  }

  removeValues() {
    this.value = ''
    this.draftValue = ''
  }

  hasValues() {
    return this.value !== '' || this.draftValue !== ''
  }

  getValues() {
    return [this.value]
  }

  showValues() {
    return String([this.value].length)
  }

  getQueryParams(): { [code: string]: string | string[] } {
    const result: { [code: string]: string | string[] } = {}

    if (this.hasValues()) {
      result[this.searchParam] = this.value
    }

    return result
  }

  save() {
    this.value = this.draftValue
  }

  render() {
    return (
      <FilterTextSearch filter={this} />
    )
  }
}

export class RangeNumberFilter extends DefaultFilter implements IFilter {
  from: number | undefined = undefined

  to: number | undefined = undefined

  fromParam!: string

  toParam!: string

  defaultValue?: { from?: number, to?: number }

  serializationType!: 'default' | 'arkspace'

  arkspaceSerializationTypeParam?: string

  constructor(f: FilterInterface) {
    super(f)

    if (!isRangeNumberHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр RangeNumberTableFilter с типом ${f.filter.type}`)
    }

    this.fromParam = f.filter.fromParam
    this.toParam = f.filter.toParam
    this.serializationType = f.filter.serializationType ?? 'default'
    this.arkspaceSerializationTypeParam = f.filter.arkspaceSerializationTypeParam
  }

  addValue(value: { from: number | undefined, to: number | undefined }) {
    if (typeof value === 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.from = value.from
    this.to = value.to
  }

  removeValue(val: number) {
    if (this.from === val) {
      this.from = undefined
    } else if (this.to === val) {
      this.to = undefined
    }
  }

  removeValues() {
    this.from = undefined
    this.to = undefined
  }

  hasValues() {
    return this.from !== undefined || this.to !== undefined
  }

  getValues() {
    const result: number[] = []

    if (this.from !== undefined) {
      result.push(this.from)
    }

    if (this.to !== undefined) {
      result.push(this.to)
    }

    return result
  }

  showValues() {
    let result = ''

    if (this.from !== undefined) {
      result += `от ${this.from}`
    }

    if (this.to !== undefined) {
      result += ` до ${this.to}`
    }

    return result
  }

  getQueryParams() {
    if (!this.hasValues()) return {}
    const result: { [code: string]: number | Array<string | number> } = {}

    switch (this.serializationType) {
      case 'arkspace':
        if (!this.arkspaceSerializationTypeParam) {
          throw new Error('Параметр arkspaceSerializationTypeParam не задан для NumberRange')
        }
        result[this.arkspaceSerializationTypeParam] = [
          this.from ? this.from : 'none',
          this.to ? this.to : 'none',
        ]

        break
      default:
        if (this.from !== undefined) {
          result[this.fromParam] = this.from
        }

        if (this.to !== undefined) {
          result[this.toParam] = this.to
        }
    }

    return result
  }

  render() {
    return (
      <FilterRangeNumber filter={this} />
    )
  }
}

/**
 * * Calendars
 */

export class CalendarFilter extends DefaultFilter implements IFilter {
  dateParam!: string

  date: Moment | null = null

  formatForDisplay = 'DD.MM.YYYY'

  defaultValue?: Moment

  saveDefaultFilterValueOnReset?: boolean

  constructor(f: FilterInterface) {
    super(f)

    if (!isCalendarHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр CalendarTableFilter с типом ${f.filter.type}`)
    }

    this.dateParam = f.filter.dateParam

    if (f.filter.defaultValue) {
      this.defaultValue = f.filter.defaultValue
      this.date = this.defaultValue
    }
    if (f.filter.saveDefaultFilterValueOnReset) {
      this.saveDefaultFilterValueOnReset = f.filter.saveDefaultFilterValueOnReset
    }

    // return this
  }

  addValue(value: Moment | null) {
    if (typeof value !== 'object') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.date = value
  }

  removeValue() {
    this.date = null
  }

  removeValues() {
    this.date = (this.defaultValue && this.saveDefaultFilterValueOnReset) ? this.defaultValue : null
  }

  hasValues() {
    return this.date !== null
  }

  getValues() {
    const result: string[] = []

    if (this.date) {
      result.push(this.date.format(this.formatForDisplay))
    }

    return result
  }

  showValues() {
    let result = ''

    if (this.date) {
      result = this.date.format(this.formatForDisplay)
    }

    return result
  }

  getQueryParams(): { [code: string]: string } {
    const result: { [code: string]: string } = {}

    if (this.date) {
      result[this.dateParam] = this.date.format('YYYY-MM-DD')
    }

    return result
  }

  render() {
    return (
      <FilterCalendar filter={this} />
    )
  }
}

export class PeriodCalendarFilter extends DefaultFilter implements IFilter {
  startDateParam!: string

  endDateParam!: string

  startDate: Moment | undefined = undefined

  endDate: Moment | undefined = undefined

  formatForDisplay = 'DD.MM.YYYY'

  defaultValue?: { startDate?: Moment, endDate?: Moment }

  saveDefaultFilterValueOnReset?: boolean

  constructor(f: FilterInterface) {
    super(f)

    if (!isPeriodCalendarHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр PeriodCalendarTableFilter с типом ${f.filter.type}`)
    }

    this.startDateParam = f.filter.startDateParam
    this.endDateParam = f.filter.endDateParam

    if (f.filter.defaultValue) {
      this.defaultValue = f.filter.defaultValue
    }
    if (this.defaultValue?.startDate) {
      this.startDate = this.defaultValue.startDate
    }
    if (this.defaultValue?.endDate) {
      this.endDate = this.defaultValue.endDate
    }
    if (f.filter.saveDefaultFilterValueOnReset) {
      this.saveDefaultFilterValueOnReset = f.filter.saveDefaultFilterValueOnReset
    }

    // return this
  }

  addValue(value: { startDate: Moment | undefined, endDate: Moment | undefined }) {
    if (typeof value !== 'object') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.startDate = value.startDate
    this.endDate = value.endDate
  }

  removeValue(value: string) {
    const date: Moment = moment(value, this.formatForDisplay)

    if (this.startDate?.isSame(date)) {
      this.startDate = undefined
    } else if (this.endDate?.isSame(date)) {
      this.endDate = undefined
    }
  }

  removeValues() {
    this.startDate = (this.defaultValue?.startDate && this.saveDefaultFilterValueOnReset)
      ? this.defaultValue.startDate : undefined
    this.endDate = (this.defaultValue?.endDate && this.saveDefaultFilterValueOnReset)
      ? this.defaultValue.endDate : undefined
  }

  hasValues() {
    return this.startDate !== undefined || this.endDate !== undefined
  }

  getValues() {
    const result: string[] = []

    if (this.startDate) {
      result.push(this.startDate.format(this.formatForDisplay))
    }

    if (this.endDate) {
      result.push(this.endDate.format(this.formatForDisplay))
    }

    return result
  }

  showValues() {
    let result = ''

    if (this.startDate) {
      result += this.startDate.format(this.formatForDisplay)
    }

    if (this.startDate && this.endDate) {
      result += ' - '
    }

    if (this.endDate) {
      result += this.endDate.format(this.formatForDisplay)
    }

    return result
  }

  getQueryParams(): { [code: string]: string } {
    const result: { [code: string]: string } = {}

    if (this.startDate) {
      result[this.startDateParam] = this.startDate.format('YYYY-MM-DD')
    }

    if (this.endDate) {
      result[this.endDateParam] = this.endDate.format('YYYY-MM-DD')
    }

    return result
  }

  render() {
    return (
      <FilterPeriodCalendar filter={this} />
    )
  }
}

export class WeekCalendarFilter extends DefaultFilter implements IFilter {
  startDateParam!: string

  endDateParam!: string

  startDate: Moment | undefined = undefined

  endDate: Moment | undefined = undefined

  formatForDisplay = 'DD.MM.YYYY'

  defaultValue?: { startDate?: Moment, endDate?: Moment }

  saveDefaultFilterValueOnReset?: boolean

  constructor(f: FilterInterface) {
    super(f)

    if (!isWeekCalendarHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр WeekCalendarTableFilter с типом ${f.filter.type}`)
    }

    this.startDateParam = f.filter.startDateParam
    this.endDateParam = f.filter.endDateParam

    if (f.filter.defaultValue) {
      this.defaultValue = f.filter.defaultValue
    }
    if (this.defaultValue?.startDate) {
      this.startDate = this.defaultValue.startDate
    }
    if (this.defaultValue?.endDate) {
      this.endDate = this.defaultValue.endDate
    }
    if (f.filter.saveDefaultFilterValueOnReset) {
      this.saveDefaultFilterValueOnReset = f.filter.saveDefaultFilterValueOnReset
    }

    // return this
  }

  addValue(value: { startDate: Moment | undefined, endDate: Moment | undefined }) {
    if (typeof value !== 'object') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.startDate = value.startDate
    this.endDate = value.endDate
  }

  removeValue(value: string) {
    const date: Moment = moment(value, this.formatForDisplay)

    if (this.startDate?.isSame(date)) {
      this.startDate = undefined
    } else if (this.endDate?.isSame(date)) {
      this.endDate = undefined
    }
  }

  removeValues() {
    this.startDate = (this.defaultValue?.startDate && this.saveDefaultFilterValueOnReset)
      ? this.defaultValue.startDate : undefined
    this.endDate = (this.defaultValue?.endDate && this.saveDefaultFilterValueOnReset)
      ? this.defaultValue.endDate : undefined
  }

  hasValues() {
    return this.startDate !== undefined || this.endDate !== undefined
  }

  getValues() {
    const result: string[] = []

    if (this.startDate) {
      result.push(this.startDate.format(this.formatForDisplay))
    }

    if (this.endDate) {
      result.push(this.endDate.format(this.formatForDisplay))
    }

    return result
  }

  showValues() {
    let result = ''

    if (this.startDate) {
      result += this.startDate.format(this.formatForDisplay)
    }

    if (this.startDate && this.endDate) {
      result += ' - '
    }

    if (this.endDate) {
      result += this.endDate.format(this.formatForDisplay)
    }

    return result
  }

  getQueryParams(): { [code: string]: string } {
    const result: { [code: string]: string } = {}

    if (this.startDate) {
      result[this.startDateParam] = this.startDate.format('YYYY-MM-DD')
    }

    if (this.endDate) {
      result[this.endDateParam] = this.endDate.format('YYYY-MM-DD')
    }

    return result
  }

  render() {
    return (
      <FilterWeekCalendar filter={this} />
    )
  }
}

/**
 * * Relation select
 */

export class RelationSelectFilter extends DefaultRelationFilter implements IFilter {
  endpoint!: string

  selectedParam!: string

  value: { id: string, name: string } | undefined = undefined

  defaultParams?: { [code: string]: string | string[] }

  constructor(f: FilterInterface) {
    super(f)

    if (!isRelationSelectHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр RelationSelectTableFilter с типом ${f.filter.type}`)
    }

    this.endpoint = f.filter.endpoint || ''
    this.selectedParam = f.filter.selectedParam
    this.defaultParams = f.filter.params

    // return this
  }

  addValue(value: FilterValue | undefined) {
    if (typeof value === 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.value = value
  }

  removeValue() {
    this.value = undefined
  }

  removeValues(): void {
    this.value = undefined
  }

  hasValues() {
    return this.value !== undefined
  }

  getValues() {
    const result: string[] = []

    if (this.hasValues()) {
      result.push(this.value!.name)
    }

    return result
  }

  showValues() {
    const result: string[] = []

    if (this.hasValues()) {
      result.push(this.value!.name)
    }

    return String(result.length)
  }

  getQueryParams(): { [code: string]: string } {
    const result: { [code: string]: string } = {}

    if (this.hasValues()) {
      result[this.selectedParam] = this.value!.id
    }

    return result
  }

  setParams(v: { [code: string]: string | string[] }) {
    const params = { ...v, ...this.defaultParams }
    delete params[this.selectedParam]
    this.params = { ...params }
  }

  removeParams() {
    this.params = {}
  }

  render() {
    return (
      <FilterRelationSelect filter={this} />
    )
  }
}

export class MultipleRelationSelectFilter extends DefaultRelationFilter implements IFilter {
  endpoint!: string

  selectedParam!: string

  value: Array<{ id: string, name: string }> = []

  defaultParams?: { [code: string]: string | string[] }

  constructor(f: FilterInterface) {
    super(f)

    if (!isMultipleRelationSelectHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр MultipleRelationSelectTableFilter с типом ${f.filter.type}`)
    }

    this.endpoint = f.filter.endpoint || ''
    this.selectedParam = f.filter.selectedParam
    this.defaultParams = f.filter.params

    // return this
  }

  addValue(value: Array<{ id: string, name: string }>) {
    if (typeof value === 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.value = value
  }

  removeValue(value: string) {
    this.value = this.value.filter((item) => item.name !== value)
  }

  removeValues(): void {
    this.value = []
  }

  hasValues() {
    return Boolean(this.value.length)
  }

  getValues() {
    return this.value.map((item) => item.name)
  }

  showValues() {
    return String(this.value.length)
  }

  getQueryParams(): { [code: string]: string[] } {
    const result: { [code: string]: string[] } = {}

    if (this.hasValues()) {
      result[this.selectedParam] = this.value.map((item) => item.id)
    }

    return result
  }

  setParams(v: { [code: string]: string | string[] }) {
    const params = { ...v, ...this.defaultParams }
    delete params[this.selectedParam]
    this.params = { ...params }
  }

  removeParams() {
    this.params = {}
  }

  render() {
    return (
      <FilterRelationSelectMultiple filter={this} />
    )
  }
}

/**
 * * Enum select
 */

export class EnumSelectFilter extends DefaultRelationFilter implements IFilter {
  endpoint!: string

  selectedParam!: string

  value: { id: string, name: string } | undefined = undefined

  defaultParams?: { [code: string]: string | string[] }

  constructor(f: FilterInterface) {
    super(f)

    if (!isEnumSelectHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр EnumSelectTableFilter с типом ${f.filter.type}`)
    }

    this.endpoint = f.filter.endpoint || ''
    this.selectedParam = f.filter.selectedParam
    this.defaultParams = f.filter.params

    // return this
  }

  addValue(value: FilterValue | undefined) {
    if (typeof value === 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.value = value
  }

  removeValue() {
    this.value = undefined
  }

  removeValues(): void {
    this.value = undefined
  }

  hasValues() {
    return this.value !== undefined
  }

  getValues() {
    const result: string[] = []

    if (this.hasValues()) {
      result.push(this.value!.name)
    }

    return result
  }

  showValues() {
    const result: string[] = []

    if (this.hasValues()) {
      result.push(this.value!.name)
    }

    return String(result.length)
  }

  getQueryParams(): { [code: string]: string } {
    const result: { [code: string]: string } = {}

    if (this.hasValues()) {
      result[this.selectedParam] = this.value!.id
    }

    return result
  }

  setParams(v: { [code: string]: string | string[] }) {
    const params = { ...v, ...this.defaultParams }
    delete params[this.selectedParam]
    this.params = { ...params }
  }

  removeParams() {
    this.params = {}
  }

  render() {
    return (
      <FilterEnumSelect filter={this} />
    )
  }
}

export class MultipleEnumSelectFilter extends DefaultRelationFilter implements IFilter {
  endpoint!: string

  selectedParam!: string

  value: Array<{ id: string, name: string }> = []

  defaultParams?: { [code: string]: string | string[] }

  constructor(f: FilterInterface) {
    super(f)

    if (!isMultipleEnumSelectHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр MultipleEnumSelectTableFilter с типом ${f.filter.type}`)
    }

    this.endpoint = f.filter.endpoint || ''
    this.selectedParam = f.filter.selectedParam
    this.defaultParams = f.filter.params

    // return this
  }

  addValue(value: Array<{ id: string, name: string }>) {
    if (typeof value === 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.value = value
  }

  removeValue(value: string) {
    this.value = this.value.filter((item) => item.name !== value)
  }

  removeValues(): void {
    this.value = []
  }

  hasValues() {
    return Boolean(this.value.length)
  }

  getValues() {
    return this.value.map((item) => item.name)
  }

  showValues() {
    return String(this.value.length)
  }

  getQueryParams(): { [code: string]: string[] } {
    const result: { [code: string]: string[] } = {}

    if (this.hasValues()) {
      result[this.selectedParam] = this.value.map((item) => item.id)
    }

    return result
  }

  setParams(v: { [code: string]: string | string[] }) {
    const params = { ...v, ...this.defaultParams }
    delete params[this.selectedParam]
    this.params = { ...params }
  }

  removeParams() {
    this.params = {}
  }

  render() {
    return (
      <FilterEnumSelectMultiple filter={this} />
    )
  }
}

/**
 * * Default select
 */

export class DefaultSelectFilter extends DefaultFilter implements IFilter {
  selectedParam!: string

  value: { id: string, name: string } | undefined = undefined

  options!: { id: string, name: string }[]

  constructor(f: FilterInterface) {
    super(f)

    if (!isDefaultSelectHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр DefaultSelectTableFilter с типом ${f.filter.type}`)
    }

    this.selectedParam = f.filter.selectedParam
    this.options = f.filter.options

    // return this
  }

  addValue(value: FilterValue | undefined) {
    if (typeof value === 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.value = value
  }

  removeValue() {
    this.value = undefined
  }

  removeValues(): void {
    this.value = undefined
  }

  hasValues() {
    return this.value !== undefined
  }

  getValues() {
    const result: string[] = []

    if (this.value) {
      result.push(this.value.name)
    }

    return result
  }

  showValues() {
    const result: string[] = []

    if (this.value) {
      result.push(this.value.name)
    }

    return String(result.length)
  }

  getQueryParams(): { [code: string]: string } {
    const result: { [code: string]: string } = {}

    if (this.hasValues()) {
      result[this.selectedParam] = this.value!.id
    }

    return result
  }

  render() {
    return (
      <FilterDefaultSelect filter={this} />
    )
  }
}

export class MultipleDefaultSelectFilter extends DefaultFilter implements IFilter {
  selectedParam!: string

  value: Array<{ id: string, name: string }> = []

  options!: { id: string, name: string }[]

  constructor(f: FilterInterface) {
    super(f)

    if (!isMultipleDefaultSelectHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр MultipleDefaultSelectTableFilter с типом ${f.filter.type}`)
    }

    this.selectedParam = f.filter.selectedParam
    this.options = f.filter.options

    // return this
  }

  addValue(value: Array<{ id: string, name: string }>) {
    if (typeof value === 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.value = value
  }

  removeValue(value: string) {
    this.value = this.value.filter((item) => item.name !== value)
  }

  removeValues(): void {
    this.value = []
  }

  hasValues() {
    return Boolean(this.value.length)
  }

  getValues() {
    return this.value.map((item) => item.name)
  }

  showValues() {
    return String(this.value.length)
  }

  getQueryParams(): { [code: string]: string[] } {
    const result: { [code: string]: string[] } = {}

    if (this.hasValues()) {
      result[this.selectedParam] = this.value.map((item) => item.id)
    }

    return result
  }

  render() {
    return (
      <FilterDefaultSelectMultiple filter={this} />
    )
  }
}

export class RadioSelectFilter extends DefaultFilter implements IFilter {
  selectedParam!: string

  value: { id: string, name: string } | undefined = undefined

  options!: { id: string, name: string }[]

  constructor(f: FilterInterface) {
    super(f)

    if (!isRadioSelectHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр RadioSelectFilter с типом ${f.filter.type}`)
    }

    this.selectedParam = f.filter.selectedParam
    this.options = f.filter.options

    // return this
  }

  addValue(value: FilterValue | undefined) {
    if (typeof value === 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.value = value
  }

  removeValue() {
    this.value = undefined
  }

  removeValues(): void {
    this.value = undefined
  }

  hasValues() {
    return this.value !== undefined
  }

  getValues() {
    const result: string[] = []

    if (this.value) {
      result.push(this.value.name)
    }

    return result
  }

  showValues() {
    const result: string[] = []

    if (this.value) {
      result.push(this.value.name)
    }

    return String(result.length)
  }

  getQueryParams(): { [code: string]: string } {
    const result: { [code: string]: string } = {}

    if (this.hasValues()) {
      result[this.selectedParam] = this.value!.id
    }

    return result
  }

  render() {
    return (
      <FilterRadioSelect filter={this} />
    )
  }
}

export class CheckboxSelectFilter extends DefaultFilter implements IFilter {
  selectedParam: string

  options: Array<{ id: string, name: string }>

  value: Array<{ id: string, name: string }> = []

  constructor(f: FilterInterface) {
    super(f)

    if (!isCheckboxSelectHeaderInterface(f.filter)) {
      throw new Error(`Невозможно собрать фильтр RadioSelectFilter с типом ${f.filter.type}`)
    }

    this.options = f.filter.options
    this.selectedParam = f.filter.selectedParam

    // return this
  }

  addValue(value: FilterValue) {
    if (typeof value === 'string') {
      throw new Error(`Для фильтра невозможно установить значение ${typeof (value)}`)
    }

    this.value.push(value)
  }

  removeValue(value: { id: string, name: string }) {
    this.value = this.value.filter((item) => item.id !== value.id)
  }

  removeValues(): void {
    this.value = []
  }

  hasValue(value: { id: string, name: string }) {
    return Boolean(this.value.find((item) => item.id === value.id))
  }

  hasValues() {
    return Boolean(this.value.length)
  }

  getValues() {
    return this.value.map((item) => item.name)
  }

  showValues() {
    return String(this.value.length)
  }

  getQueryParams(): { [code: string]: string[] } {
    const result: { [code: string]: string[] } = {}

    if (this.hasValues()) {
      result[this.selectedParam] = this.value.map((item) => item.id)
    }

    return result
  }

  render() {
    return (
      <FilterCheckboxSelect filter={this} />
    )
  }
}

export function filterFabric(f: FilterInterface) {
  const { filter } = f

  if (isRangeNumberHeaderInterface(filter)) {
    return new RangeNumberFilter(f)
  }

  /**
   * * Calendars
   */

  if (isCalendarHeaderInterface(filter)) {
    return new CalendarFilter(f)
  }

  if (isPeriodCalendarHeaderInterface(filter)) {
    return new PeriodCalendarFilter(f)
  }

  if (isWeekCalendarHeaderInterface(filter)) {
    return new WeekCalendarFilter(f)
  }

  /**
   * * Relation select
   */

  if (isRelationSelectHeaderInterface(filter)) {
    return new RelationSelectFilter(f)
  }

  if (isMultipleRelationSelectHeaderInterface(filter)) {
    return new MultipleRelationSelectFilter(f)
  }

  /**
   * * Enum select
   */

  if (isEnumSelectHeaderInterface(filter)) {
    return new EnumSelectFilter(f)
  }

  if (isMultipleEnumSelectHeaderInterface(filter)) {
    return new MultipleEnumSelectFilter(f)
  }

  /**
   * * Default select
   */

  if (isDefaultSelectHeaderInterface(filter)) {
    return new DefaultSelectFilter(f)
  }

  if (isMultipleDefaultSelectHeaderInterface(filter)) {
    return new MultipleDefaultSelectFilter(f)
  }

  if (isRadioSelectHeaderInterface(filter)) {
    return new RadioSelectFilter(f)
  }

  if (isCheckboxSelectHeaderInterface(filter)) {
    return new CheckboxSelectFilter(f)
  }

  if (isTextSearchHeaderInterface(filter)) {
    return new TextSearchFilter(f)
  }

  return new TextFilter(f)
}
