import { RoleLane } from './roleLanes'

type FilterDefinition = {
  title: string
  label?: string
  type?: string
  number?: string
  date?: string
}

type FilterItemFields = {
  selectedField?: string
  filterValue?: string
  filterValueFrom?: string
  filterValueTo?: string
}

type FilterItemLike = {
  fields?: FilterItemFields
}

const hiddenFieldsByEntityAndLane: Record<string, Record<RoleLane, Set<string>>> = {
  booking_requests: {
    super_admin: new Set([]),
    admin: new Set(['tenant', 'organization', 'requested_by']),
    concierge: new Set(['tenant', 'organization', 'requested_by', 'approval_steps', 'comments']),
    customer: new Set(['tenant', 'organization', 'requested_by', 'approval_steps', 'comments', 'budget_code', 'max_budget_amount', 'currency']),
  },
  reservations: {
    super_admin: new Set([]),
    admin: new Set(['tenant', 'organization']),
    concierge: new Set(['tenant', 'organization', 'nightly_rate', 'monthly_rate', 'currency', 'internal_notes', 'invoices', 'comments']),
    customer: new Set(['tenant', 'organization', 'booking_request', 'nightly_rate', 'monthly_rate', 'currency', 'internal_notes', 'invoices', 'comments', 'service_requests']),
  },
  service_requests: {
    super_admin: new Set([]),
    admin: new Set(['tenant']),
    concierge: new Set(['tenant', 'estimated_cost', 'actual_cost', 'currency', 'comments']),
    customer: new Set(['tenant', 'requested_by', 'assigned_to', 'estimated_cost', 'actual_cost', 'currency', 'comments']),
  },
  documents: {
    super_admin: new Set([]),
    admin: new Set(['tenant', 'organization', 'storage_key', 'public_url']),
    concierge: new Set(['tenant', 'organization', 'storage_key', 'public_url', 'mime_type', 'file_size_bytes', 'invoice', 'is_private']),
    customer: new Set(['tenant', 'organization', 'storage_key', 'public_url', 'mime_type', 'file_size_bytes', 'uploaded_by', 'invoice', 'is_private', 'notes']),
  },
}

const isRangeFilter = (filter?: FilterDefinition) => Boolean(filter?.number || filter?.date)

const getHumanizedFieldName = (value?: string) => {
  if (!value) {
    return ''
  }

  return value
    .replace(/_/g, ' ')
    .replace(/\s+/g, ' ')
    .trim()
}

const cleanupFilterDisplayLabel = (value: string) => {
  if (!value) {
    return 'this field'
  }

  const cleanedValue = value
    .replace(/\bids\b/gi, '')
    .replace(/\bid\b$/i, '')
    .replace(/\bat\b$/i, '')
    .replace(/\burl\b/gi, 'URL')
    .replace(/\bapi\b/gi, 'API')
    .replace(/\bcheck in\b/gi, 'check-in')
    .replace(/\bcheck out\b/gi, 'check-out')
    .replace(/\s+/g, ' ')
    .trim()

  if (!cleanedValue) {
    return 'this field'
  }

  return cleanedValue.charAt(0).toLowerCase() + cleanedValue.slice(1)
}

const getFilterDisplayLabel = (filter?: FilterDefinition) => {
  const rawTitle = getHumanizedFieldName(filter?.title)
  const rawLabel = getHumanizedFieldName(filter?.label)
  const displaySource = rawTitle || rawLabel

  return cleanupFilterDisplayLabel(displaySource)
}

export const getHiddenFieldsForRole = (entityName: string, roleLane: RoleLane) => {
  return hiddenFieldsByEntityAndLane[entityName]?.[roleLane] ?? new Set<string>()
}

export const filterEntityFieldsForRole = <T extends { title: string }>(
  entityName: string,
  roleLane: RoleLane,
  fields: T[],
) => {
  const hiddenFields = getHiddenFieldsForRole(entityName, roleLane)

  return fields.filter((field) => !hiddenFields.has(field.title))
}

export const pruneHiddenFilterItems = <
  TFilter extends { title: string },
  TFilterItem extends { fields?: { selectedField?: string } },
>(
  filterItems: TFilterItem[],
  filters: TFilter[],
) => {
  if (!Array.isArray(filterItems) || !filterItems.length) {
    return filterItems
  }

  const availableFields = new Set(filters.map((filter) => filter.title))
  const nextFilterItems = filterItems.filter((item) => availableFields.has(item?.fields?.selectedField || ''))

  return nextFilterItems.length === filterItems.length ? filterItems : nextFilterItems
}

export const isFilterItemIncomplete = <TFilter extends FilterDefinition, TFilterItem extends FilterItemLike>(
  filterItem: TFilterItem,
  filters: TFilter[],
) => {
  const selectedField = filterItem?.fields?.selectedField || ''
  const selectedFilter = filters.find((filter) => filter.title === selectedField)

  if (!selectedFilter) {
    return true
  }

  if (isRangeFilter(selectedFilter)) {
    return !(filterItem?.fields?.filterValueFrom || filterItem?.fields?.filterValueTo)
  }

  return !filterItem?.fields?.filterValue
}

export const getIncompleteFilterHint = <TFilter extends FilterDefinition, TFilterItem extends FilterItemLike>(
  filterItem: TFilterItem,
  filters: TFilter[],
) => {
  const selectedField = filterItem?.fields?.selectedField || ''
  const selectedFilter = filters.find((filter) => filter.title === selectedField)
  const filterLabel = getFilterDisplayLabel(selectedFilter)

  if (!selectedFilter) {
    return 'Choose a filter field to continue.'
  }

  if (selectedFilter.number || selectedFilter.date) {
    return `Enter ${filterLabel} from/to to add another filter.`
  }

  if (selectedFilter.type === 'enum') {
    return `Choose ${filterLabel} to add another filter.`
  }

  return `Enter ${filterLabel} to add another filter.`
}

export const getNextAvailableFilter = <TFilter extends FilterDefinition, TFilterItem extends FilterItemLike>(
  filterItems: TFilterItem[],
  filters: TFilter[],
) => {
  const usedFields = new Set(
    filterItems
      .map((item) => item?.fields?.selectedField)
      .filter((selectedField): selectedField is string => Boolean(selectedField)),
  )

  return filters.find((filter) => !usedFields.has(filter.title)) ?? null
}

export const getAddFilterState = <TFilter extends FilterDefinition, TFilterItem extends FilterItemLike>(
  filterItems: TFilterItem[],
  filters: TFilter[],
) => {
  if (!filters.length) {
    return {
      canAddFilter: false,
      buttonLabel: 'No filters available',
      helperText: 'No filters are available for this view.',
    }
  }

  if (filterItems.some((item) => isFilterItemIncomplete(item, filters))) {
    return {
      canAddFilter: false,
      buttonLabel: 'Complete current filter',
      helperText: 'Finish the current filter before adding another.',
    }
  }

  const nextFilter = getNextAvailableFilter(filterItems, filters)

  if (!nextFilter) {
    return {
      canAddFilter: false,
      buttonLabel: 'All filters added',
      helperText: 'All available filters are already in use.',
    }
  }

  return {
    canAddFilter: true,
    buttonLabel: 'Add filter',
    helperText: `Next filter: ${nextFilter.label ?? nextFilter.title}`,
  }
}

export const buildNextFilterItem = <TFilter extends FilterDefinition>(
  filterItems: FilterItemLike[],
  filters: TFilter[],
  createId: () => string,
) => {
  if (!filters.length || filterItems.some((item) => isFilterItemIncomplete(item, filters))) {
    return null
  }

  const nextFilter = getNextAvailableFilter(filterItems, filters)

  if (!nextFilter) {
    return null
  }

  return {
    id: createId(),
    fields: {
      filterValue: '',
      filterValueFrom: '',
      filterValueTo: '',
      selectedField: nextFilter.title,
    },
  }
}
