import { ParamMap, Params } from '@angular/router'
import {
  CustomFieldQueryLogicalOperator,
  CustomFieldQueryOperator,
} from '../data/custom-field-query'
import { FilterRule } from '../data/filter-rule'
import {
  FILTER_CUSTOM_FIELDS_QUERY,
  FILTER_HAS_CUSTOM_FIELDS_ALL,
  FILTER_HAS_CUSTOM_FIELDS_ANY,
  FILTER_RULE_TYPES,
  FILTER_SIMPLE_TEXT,
  FILTER_SIMPLE_TITLE,
  FILTER_TITLE,
  FILTER_TITLE_CONTENT,
  FilterRuleType,
  NEGATIVE_NULL_FILTER_VALUE,
  SIMPLE_TEXT_PARAMETER,
  SIMPLE_TITLE_PARAMETER,
} from '../data/filter-rule-type'
import { ListViewState } from '../services/document-list-view.service'

const SORT_FIELD_PARAMETER = 'sort'
const SORT_REVERSE_PARAMETER = 'reverse'
const PAGE_PARAMETER = 'page'

export function paramsFromViewState(
  viewState: ListViewState,
  pageOnly: boolean = false
): Params {
  let params = queryParamsFromFilterRules(viewState.filterRules)
  params[SORT_FIELD_PARAMETER] = viewState.sortField
  params[SORT_REVERSE_PARAMETER] = viewState.sortReverse ? 1 : undefined
  if (pageOnly) params = {}
  params[PAGE_PARAMETER] = isNaN(viewState.currentPage)
    ? 1
    : viewState.currentPage
  if (pageOnly && viewState.currentPage == 1) params[PAGE_PARAMETER] = undefined
  return params
}

export function paramsToViewState(queryParams: ParamMap): ListViewState {
  let filterRules = filterRulesFromQueryParams(queryParams)
  let sortField = queryParams.get(SORT_FIELD_PARAMETER)
  let sortReverse =
    queryParams.has(SORT_REVERSE_PARAMETER) ||
    (!queryParams.has(SORT_FIELD_PARAMETER) &&
      !queryParams.has(SORT_REVERSE_PARAMETER))
  let currentPage = queryParams.has(PAGE_PARAMETER)
    ? parseInt(queryParams.get(PAGE_PARAMETER))
    : 1
  return {
    currentPage: currentPage,
    filterRules: filterRules,
    sortField: sortField,
    sortReverse: sortReverse,
  }
}

export function transformLegacyFilterRules(
  filterRules: FilterRule[]
): FilterRule[] {
  const LEGACY_CUSTOM_FIELD_FILTER_RULE_TYPES = [
    FILTER_HAS_CUSTOM_FIELDS_ANY,
    FILTER_HAS_CUSTOM_FIELDS_ALL,
  ]
  if (
    filterRules.filter((rule) =>
      LEGACY_CUSTOM_FIELD_FILTER_RULE_TYPES.includes(rule.rule_type)
    ).length
  ) {
    const anyRules = filterRules.filter(
      (rule) => rule.rule_type === FILTER_HAS_CUSTOM_FIELDS_ANY
    )
    const allRules = filterRules.filter(
      (rule) => rule.rule_type === FILTER_HAS_CUSTOM_FIELDS_ALL
    )
    const customFieldQueryLogicalOperator = allRules.length
      ? CustomFieldQueryLogicalOperator.And
      : CustomFieldQueryLogicalOperator.Or
    const valueRules = allRules.length ? allRules : anyRules
    const customFieldQueryExpression = [
      customFieldQueryLogicalOperator,
      [
        ...valueRules.map((rule) => [
          parseInt(rule.value),
          CustomFieldQueryOperator.Exists,
          true,
        ]),
      ],
    ]
    filterRules.push({
      rule_type: FILTER_CUSTOM_FIELDS_QUERY,
      value: JSON.stringify(customFieldQueryExpression),
    })
  }
  // TODO: can we support FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS or FILTER_HAS_ANY_CUSTOM_FIELDS?
  return filterRules.filter(
    (rule) => !LEGACY_CUSTOM_FIELD_FILTER_RULE_TYPES.includes(rule.rule_type)
  )
}

export function filterRulesFromQueryParams(
  queryParams: ParamMap
): FilterRule[] {
  let filterRulesFromQueryParams: FilterRule[] = []

  const allFilterRuleQueryParams: string[] = FILTER_RULE_TYPES.map(
    (rt) => rt.filtervar
  )
    .concat(FILTER_RULE_TYPES.map((rt) => rt.isnull_filtervar))
    .filter((rt) => rt !== undefined)

  // transform query params to filter rules
  allFilterRuleQueryParams
    .filter((frqp) => queryParams.has(frqp))
    .forEach((filterQueryParamName) => {
      const rule_type: FilterRuleType = FILTER_RULE_TYPES.find(
        (rt) =>
          rt.filtervar == filterQueryParamName ||
          rt.isnull_filtervar == filterQueryParamName
      )
      const isNullRuleType = rule_type.isnull_filtervar == filterQueryParamName
      const nullRuleValue =
        queryParams.get(filterQueryParamName) == '1'
          ? null
          : NEGATIVE_NULL_FILTER_VALUE.toString()
      const valueURIComponent: string = queryParams.get(filterQueryParamName)
      const filterQueryParamValues: string[] = rule_type.multi
        ? valueURIComponent.split(',')
        : [valueURIComponent]

      filterRulesFromQueryParams = filterRulesFromQueryParams.concat(
        // map all values to filter rules
        filterQueryParamValues.map((val) => {
          if (rule_type.datatype == 'boolean')
            val = val.replace('1', 'true').replace('0', 'false')
          return {
            rule_type: rule_type.id,
            value: isNullRuleType ? nullRuleValue : val,
          }
        })
      )
    })
  filterRulesFromQueryParams = transformLegacyFilterRules(
    filterRulesFromQueryParams
  )
  return filterRulesFromQueryParams
}

export function queryParamsFromFilterRules(filterRules: FilterRule[]): Params {
  if (filterRules) {
    let params = {}
    for (let rule of filterRules) {
      let ruleType = FILTER_RULE_TYPES.find((t) => t.id == rule.rule_type)
      if (
        rule.rule_type === FILTER_TITLE_CONTENT ||
        rule.rule_type === FILTER_SIMPLE_TEXT
      ) {
        params[SIMPLE_TEXT_PARAMETER] = rule.value
      } else if (
        rule.rule_type === FILTER_TITLE ||
        rule.rule_type === FILTER_SIMPLE_TITLE
      ) {
        params[SIMPLE_TITLE_PARAMETER] = rule.value
      } else if (ruleType.isnull_filtervar && rule.value == null) {
        params[ruleType.isnull_filtervar] = 1
      } else if (
        ruleType.isnull_filtervar &&
        rule.value == NEGATIVE_NULL_FILTER_VALUE.toString()
      ) {
        params[ruleType.isnull_filtervar] = 0
      } else if (ruleType.multi) {
        params[ruleType.filtervar] = params[ruleType.filtervar]
          ? params[ruleType.filtervar] + ',' + rule.value
          : rule.value
      } else {
        params[ruleType.filtervar] = rule.value
        if (ruleType.datatype == 'boolean')
          params[ruleType.filtervar] =
            rule.value == 'true' || rule.value == '1' ? 1 : 0
      }
    }
    return params
  } else {
    return null
  }
}
