import React, {
  Dispatch,
  FunctionComponent,
  SetStateAction,
  SyntheticEvent,
  useEffect,
  useMemo,
  useState,
} from 'react'
import { Trans, useTranslation } from 'next-i18next'
import { styled } from '@mui/system'
import {
  IFieldGuesserProps,
  ISearchParameters,
  ISourceField,
  ITableConfig,
  ITableRow,
  LoadStatus,
  defaultPageSize,
  defaultRowsPerPageOptions,
  getNameFromDefault,
} from '@elastic-suite/gally-admin-shared'

import {
  useApiEditableList,
  useFilterParameters,
  useFiltersRedirect,
  usePage,
  useResource,
  useSearch,
} from '../../../hooks'

import NoAttributes, {
  INoAttributesProps,
} from '../../atoms/noAttributes/NoAttributes'
import Button from '../../atoms/buttons/Button'
import FieldGuesser from '../../stateful/FieldGuesser/FieldGuesser'
import FiltersGuesser from '../../stateful/FiltersGuesser/FiltersGuesser'
import TableGuesser from '../../stateful/TableGuesser/TableGuesser'
import IonIcon from '../../atoms/IonIcon/IonIcon'
import { TestId, generateTestId } from '../../../utils/testIds'

const FilterContent = styled('div')({
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'flex-end',
})

const Paragraph = styled('p')(({ theme }) => ({
  marginBottom: 0,
  marginTop: theme.spacing(1),
}))

function isObjectNotEmpty(object: object): boolean {
  return Object.values(object).some((value) => {
    // Consider empty: empty string, null, undefined, empty array
    // Consider non-empty: 0, false, and all other values
    if (Array.isArray(value)) {
      return value.length > 0
    }
    return value !== '' && value !== null && value !== undefined
  })
}
export interface IResourceTable {
  Field?: FunctionComponent<IFieldGuesserProps>
  active?: boolean
  activeFilters: ISearchParameters
  diffDefaultValues?: boolean
  getTableConfigs?: (rows: ITableRow[]) => ITableConfig[]
  filters?: ISearchParameters
  filtersPrefix?: string
  resourceName: string
  setActiveFilters: Dispatch<SetStateAction<ISearchParameters>>
  urlParams?: string
  showSearch?: boolean
  hasEditLink?: boolean
  editLink?: string
  rowsPerPage?: number
  noAttributesProps?: INoAttributesProps
  refreshTable?: number | string
}

const listOfDefaultFacets = [
  'defaultCoverageRate',
  'defaultDisplayMode',
  'defaultIsRecommendable',
  'defaultIsVirtual',
  'defaultMaxSize',
  'defaultSortOrder',
  'defaultPosition',
]

function ResourceTable(props: IResourceTable): JSX.Element {
  const { t } = useTranslation('resourceTable')
  const {
    Field,
    active,
    activeFilters,
    diffDefaultValues,
    getTableConfigs,
    filters,
    filtersPrefix,
    resourceName,
    setActiveFilters,
    urlParams,
    showSearch,
    hasEditLink,
    editLink,
    rowsPerPage: rowsPerPageValue,
    noAttributesProps,
    refreshTable,
  } = props

  const resource = useResource(resourceName)
  const validFiltersPrefix = filtersPrefix ?? resource.title.toLowerCase()
  const [page, setPage] = usePage(validFiltersPrefix)
  const [searchValue, setSearchValue] = useSearch(validFiltersPrefix)

  const filtersWithRefreshTrigger = useMemo(() => {
    return {
      ...filters,
      ...{ refresh: refreshTable },
    }
  }, [filters, refreshTable])

  const parameters = useFilterParameters(
    activeFilters,
    filtersWithRefreshTrigger
  )
  useFiltersRedirect(
    page,
    activeFilters,
    searchValue,
    active,
    validFiltersPrefix
  )

  const rowsPerPageOptions = defaultRowsPerPageOptions
  const [rowsPerPage, setRowsPerPage] = useState<number>(
    rowsPerPageValue ?? defaultPageSize
  )

  const [withDebounce, setWithDebounce] = useState(false)

  const [resourceData, { massUpdate, massReplace, replace, update }] =
    useApiEditableList<ISourceField>(
      resource,
      page,
      rowsPerPage,
      parameters,
      searchValue,
      urlParams ? `${resource.url}${urlParams}` : null,
      searchValue.trim() === '' ? false : withDebounce
    )

  useEffect(() => {
    setWithDebounce(true)
  }, [resourceData])

  const { data, error } = resourceData

  const tableRows = data?.['hydra:member'] as unknown as ITableRow[]
  const diffRows: ITableRow[] = useMemo(() => {
    if (diffDefaultValues && tableRows) {
      const newTableRows = tableRows.map((itemTableRow) => {
        const newDefault = listOfDefaultFacets.map((itemDefaultFacets) => {
          return { [itemDefaultFacets]: itemTableRow[itemDefaultFacets] }
        })
        return Object.assign({}, itemTableRow, ...newDefault)
      })

      return newTableRows.map((row) =>
        Object.fromEntries([
          ...Object.entries(row)
            .filter(([key]) => key.startsWith('default'))
            .map(([key, value]) => [getNameFromDefault(key), value]),
        ])
      ) as ITableRow[]
    }
    return []
  }, [diffDefaultValues, tableRows])

  const diffCount = useMemo(
    () =>
      diffRows.reduce(
        (acc, row, index) =>
          acc +
          Object.entries(row).reduce(
            (acc, [key, value]) =>
              acc + Number(value !== tableRows?.[index][key]),
            0
          ),
        0
      ),
    [diffRows, tableRows]
  )

  if (error || !data) {
    return null
  }

  function handleFilterChange(activeFilters: ISearchParameters): void {
    setActiveFilters(activeFilters)
    setPage(0)
  }

  function handleSearchValue(search: string): void {
    setSearchValue(search)
    setPage(0)
  }

  function handlePageChange(page: number): void {
    setPage(page)
  }

  function handleRowChange(
    id: string | number,
    name: string,
    value: boolean | number | string,
    event: SyntheticEvent,
    showError: boolean
  ): void {
    const validity = (event.target as HTMLInputElement)?.checkValidity()
    if (update) {
      update(id, { [name]: value }, !showError || validity)
    } else if (replace) {
      replace(
        { id, [name]: value } as unknown as ISourceField,
        !showError || validity
      )
    }
  }

  function onRowsPerPageChange(
    event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
  ): void {
    event && setRowsPerPage(Number(event.target.value))
    setPage(0)
  }

  function handleReset(): void {
    diffRows.forEach((row, index) => {
      const entries = Object.entries(row)
        .filter(([key, value]) => value !== tableRows?.[index][key])
        .map((item) => {
          return [item[0], item?.[1] ?? null]
        })
      if (entries.length > 0) {
        const entity = {
          ...tableRows?.[index],
          ...Object.fromEntries(entries),
        } as unknown as ISourceField

        if (update) {
          update(entity.id, entity)
        } else if (replace) {
          replace(entity)
        }
      }
    }, [])
  }

  const filterOrSearchAreUp =
    searchValue !== '' || isObjectNotEmpty(activeFilters)

  if (
    data['hydra:member'].length === 0 &&
    resourceData.status === LoadStatus.SUCCEEDED &&
    !filterOrSearchAreUp
  ) {
    return <NoAttributes {...noAttributesProps} />
  }
  return (
    <>
      <FiltersGuesser
        activeFilters={activeFilters}
        apiData={data}
        onFilterChange={handleFilterChange}
        onSearch={handleSearchValue}
        resource={resource}
        searchValue={searchValue}
        showSearch={showSearch}
        componentId={resourceName}
      >
        {Boolean(diffDefaultValues) && (
          <FilterContent>
            <Button
              disabled={diffCount === 0}
              display="secondary"
              onClick={handleReset}
              endIcon={<IonIcon name="reload-outline" />}
              componentId="resetDefaultValues"
            >
              {t('default.button')}
            </Button>
            <Paragraph
              data-testid={generateTestId(
                TestId.RESOURCE_TABLE_NB_CUSTOM_VALUES_MESSAGE
              )}
            >
              <Trans
                ns="resourceTable"
                i18nKey="default.customValue"
                count={diffCount}
                values={{ value: diffCount }}
                components={[<strong key="0" />]}
              />
            </Paragraph>
          </FilterContent>
        )}
      </FiltersGuesser>
      <TableGuesser
        Field={Field}
        count={data['hydra:totalItems']}
        currentPage={page}
        diffRows={diffRows}
        noResult={
          data['hydra:member'].length === 0 &&
          resourceData.status === LoadStatus.SUCCEEDED &&
          filterOrSearchAreUp
        }
        onMassupdate={massUpdate}
        onMassreplace={massReplace}
        onPageChange={handlePageChange}
        onRowUpdate={handleRowChange}
        onRowsPerPageChange={onRowsPerPageChange}
        resource={resource}
        rowsPerPage={rowsPerPage}
        rowsPerPageOptions={rowsPerPageOptions}
        tableConfigs={getTableConfigs?.(tableRows)}
        tableRows={tableRows}
        hasEditLink={hasEditLink}
        editLink={editLink}
        componentId={resourceName}
      />
    </>
  )
}

ResourceTable.defaultProps = {
  Field: FieldGuesser,
}

export default ResourceTable
