import React, { useMemo } from 'react'
import { TypeGuards } from '@codeleap/types'
import { useCallback } from '@codeleap/hooks'
import { SectionList, SectionListProps as RNSectionProps } from 'react-native'
import { View, ViewProps } from '../View'
import { RefreshControl } from '../RefreshControl'
import { useKeyboardPaddingStyle } from '../../utils'
import { AugmentedSectionRenderItemInfo, SectionComponentProps, SectionProps, SectionRenderComponentProps } from './types'
import { AnyRecord, IJSX, StyledComponentProps } from '@codeleap/styles'
import { MobileStyleRegistry } from '../../Registry'
import { useStylesFor } from '../../hooks'
import { EmptyPlaceholder } from '../EmptyPlaceholder'

export * from './styles'
export * from './types'

const RenderSeparator = (props: { separatorStyles: ViewProps['style'] }) => {
  return <View style={props.separatorStyles} />
}

/**
 * Wraps React Native's SectionList. The `sections` data is augmented with a sequential `index`
 * field internally — callers must not add their own `index` to section objects or it will be
 * overwritten and position helpers (`isFirst`, `isLast`, `isOnly`) will break.
 *
 * There is no `keyExtractor` default — SectionList falls back to array index, which causes
 * re-mount flicker on reorder. Always pass a `keyExtractor` when items can change order or identity.
 */
export function Sections<T>(sectionsProps: SectionProps<T>) {
  const {
    style,
    onRefresh,
    refreshing,
    placeholder,
    refreshControlProps,
    loading,
    keyboardAware,
    fakeEmpty = loading,
    contentContainerStyle,
    refreshControl,
    renderItem: providedRenderItem,
    sections: data,
    renderSectionHeader: providedRenderSectionHeader,
    renderSectionFooter: providedRenderSectionFooter,
    ...props
  } = {
    ...Sections.defaultProps,
    ...sectionsProps,
  }

  /** Deep-equality via JSON.stringify to avoid re-renders on reference changes; will miss non-serialisable values (functions, Dates) in section data. */
  const sections = useMemo(() => {
    return data?.map((section, index) => ({
      ...section,
      index,
    }))
  }, [JSON.stringify(data)])

  const styles = useStylesFor(Sections.styleRegistryName, style)

  const separator = useCallback(() => {
    if (!props?.separators) return null
    return <RenderSeparator separatorStyles={styles.separator} />
  }, [])

  const getSectionProps = (data: SectionRenderComponentProps<T>) => {
    const listLength = sections?.length || 0

    const isFirst = data?.section?.index === sections?.[0]?.index
    const isLast = data?.section?.index === sections?.[listLength - 1]?.index
    const isOnly = isFirst && isLast
    const title = data?.section?.title
    const index = data?.section?.index

    return { isFirst, isLast, isOnly, title, index }
  }

  const renderSectionHeader = useCallback((data: SectionRenderComponentProps<T>) => {
    if (!providedRenderSectionHeader) return null

    const positionProps = getSectionProps(data)

    return providedRenderSectionHeader({ ...data.section, ...positionProps })
  }, [providedRenderSectionHeader, sections?.length])

  const renderSectionFooter = useCallback((data: SectionRenderComponentProps<T>) => {
    if (!providedRenderSectionFooter) return null

    const positionProps = getSectionProps(data)

    return providedRenderSectionFooter({ ...data.section, ...positionProps })
  }, [providedRenderSectionFooter, sections?.length])

  const renderItem = useCallback((data: AugmentedSectionRenderItemInfo<T>) => {
    if (!providedRenderItem) return null

    const listLength = data?.section?.data?.length || 0

    const isFirst = data?.index === 0
    const isLast = data?.index === listLength - 1
    const isOnly = isFirst && isLast

    return providedRenderItem({
      ...data,
      isFirst,
      isLast,
      isOnly,
    })
  }, [providedRenderItem])

  const isEmpty = !sections || !sections?.length

  const _placeholder = {
    ...placeholder,
    loading: TypeGuards.isBoolean(placeholder?.loading) ? placeholder.loading : loading,
  }

  const keyboardStyle = useKeyboardPaddingStyle([
    styles.content,
    contentContainerStyle,
    isEmpty && styles['content:empty'],
    loading && styles['content:loading'],
  ], keyboardAware && !props.horizontal)

  const wrapperStyle = [styles.wrapper, isEmpty && styles['wrapper:empty'], loading && styles['wrapper:loading']]

  return (
    <SectionList
      ItemSeparatorComponent={separator}
      refreshControl={TypeGuards.isFunction(onRefresh) ? (
        <RefreshControl
          refreshing={refreshing}
          onRefresh={onRefresh}
          {...refreshControlProps}
        />
      ) : null}
      ListEmptyComponent={<EmptyPlaceholder {..._placeholder} />}
      showsVerticalScrollIndicator={false}
      showsHorizontalScrollIndicator={false}
      {...props}
      ListHeaderComponentStyle={styles.header}
      ListFooterComponentStyle={styles.footer}
      style={wrapperStyle}
      contentContainerStyle={keyboardStyle}
      sections={sections}
      renderItem={renderItem}
      renderSectionHeader={renderSectionHeader as unknown as RNSectionProps<T>['renderSectionHeader']}
      renderSectionFooter={renderSectionFooter as unknown as RNSectionProps<T>['renderSectionHeader']}
    />
  )
}

Sections.styleRegistryName = 'Sections'
Sections.elements = ['wrapper', 'content', 'separator', 'header', 'footer', 'refreshControl']
Sections.rootElement = 'wrapper'

Sections.withVariantTypes = <S extends AnyRecord>(styles: S) => {
  return Sections as <T>(props: StyledComponentProps<SectionProps<T>, typeof styles>) => IJSX
}

Sections.defaultProps = {
  keyboardShouldPersistTaps: 'handled',
  fakeEmpty: false,
  loading: false,
  keyboardAware: true,
} as Partial<SectionProps>

MobileStyleRegistry.registerComponent(Sections)
