import React, {useCallback, useState} from 'react';
import {LayoutChangeEvent, StyleSheet, View} from 'react-native';
import {ScrollView} from 'react-native-gesture-handler';
import {Radio, Typography} from '@components';
import {SPACING, useTheme} from '@shared/theme';

import {styles} from './styles';
import {
  useInputController,
  UseInputControllerProps,
} from '../Inputs/hooks/useInputController';

const HEADER_ROW_INDEX = -1;

type TableItem = {
  id: number;
  label: string;
};

export type TableValue = Record<number, number[]>;

type TableProps = {
  rows?: TableItem[];
  columns?: TableItem[];
  value?: TableValue;
  onChange?: (value: TableValue) => void;
  multiple?: boolean;
  disabled?: boolean;
  error?: boolean;
};

export function Table({
  rows,
  columns,
  value,
  onChange,
  multiple = false,
  disabled = false,
}: TableProps) {
  const {theme} = useTheme();
  const lastRowIndex = rows?.length ? rows.length - 1 : 0;
  const lastColumnIndex = columns?.length ? columns.length - 1 : 0;
  const [rowHeights, setRowHeights] = useState<Record<number, number>>({});

  const handleRowLayout = useCallback(
    (rowIndex: number) => (event: LayoutChangeEvent) => {
      const height = Math.ceil(event.nativeEvent.layout.height);
      setRowHeights(prev => {
        const current = prev[rowIndex];
        if (rowIndex === HEADER_ROW_INDEX) {
          if (Number.isFinite(current) && height <= current) {
            return prev;
          }
        } else {
          if (current === height) {
            return prev;
          }
        }
        return {...prev, [rowIndex]: height};
      });
    },
    [],
  );

  const renderRowHeader = useCallback(
    (row: TableItem, rowIndex: number) => {
      const isLastRow = rowIndex === lastRowIndex;

      return (
        <View
          key={row.id}
          onLayout={handleRowLayout(rowIndex)}
          style={[
            styles.rowHeaderCell,
            {borderColor: theme.border.neutral.default},
            isLastRow && styles.rowHeaderCellLast,
          ]}>
          <Typography>{row.label}</Typography>
        </View>
      );
    },
    [handleRowLayout, lastRowIndex, theme],
  );

  const onPress = useCallback(
    (rowId: number, columnId: number) => () => {
      if (disabled) {
        return;
      }

      const currentValue = value ?? {};
      const currentRowValues = currentValue[rowId] ?? [];
      const isChecked = currentRowValues.includes(columnId);
      const newRowValues = multiple
        ? isChecked
          ? currentRowValues.filter(valueItem => valueItem !== columnId)
          : currentRowValues.concat(columnId)
        : isChecked
        ? []
        : [columnId];

      const newValue = {...currentValue};
      if (newRowValues.length) {
        newValue[rowId] = newRowValues;
      } else {
        delete newValue[rowId];
      }

      onChange?.(newValue);
    },
    [disabled, multiple, onChange, value],
  );

  const renderColumn = useCallback(
    (column: TableItem, columnIndex: number) => {
      const isLastColumn = columnIndex === lastColumnIndex;

      return (
        <View key={column.id} style={styles.column}>
          <View
            style={[
              styles.columnHeaderCell,
              {
                backgroundColor: theme.background.elements.grey,
                borderColor: theme.border.neutral.default,
              },
              isLastColumn && styles.columnHeaderCellLast,
              Number.isFinite(rowHeights[HEADER_ROW_INDEX]) && {
                height: rowHeights[HEADER_ROW_INDEX],
              },
            ]}>
            <View
              onLayout={handleRowLayout(HEADER_ROW_INDEX)}
              style={styles.columnHeaderContent}>
              <Typography weight="semiBold">{column.label}</Typography>
            </View>
          </View>
          {rows?.map((row, rowIndex) => {
            const isLastRow = rowIndex === lastRowIndex;
            const containerStyle = StyleSheet.flatten([
              styles.cell,
              {borderColor: theme.border.neutral.default},
              isLastColumn && styles.lastColumnBorder,
              isLastRow && isLastColumn && styles.bottomRightCell,
              Number.isFinite(rowHeights[rowIndex]) && {
                height: rowHeights[rowIndex],
              },
            ]);
            return (
              <View key={`${column.id}-${row.id}`} style={containerStyle}>
                <Radio
                  checked={value?.[row.id]?.includes(column.id)}
                  disabled={disabled}
                  onPress={onPress(row.id, column.id)}
                  hitSlop={SPACING['x0.25']}
                />
              </View>
            );
          })}
        </View>
      );
    },
    [
      disabled,
      handleRowLayout,
      lastColumnIndex,
      lastRowIndex,
      onPress,
      rowHeights,
      rows,
      theme.background.elements.grey,
      theme.border.neutral.default,
      value,
    ],
  );

  return (
    <View style={styles.container}>
      <View style={styles.rowHeaderGroup}>
        <View
          style={[
            styles.cornerCell,
            {
              backgroundColor: theme.background.elements.grey,
              borderColor: theme.border.neutral.default,
            },
            Number.isFinite(rowHeights[HEADER_ROW_INDEX]) && {
              height: rowHeights[HEADER_ROW_INDEX],
            },
          ]}
        />
        {rows?.map(renderRowHeader)}
      </View>
      <ScrollView
        horizontal
        alwaysBounceHorizontal={false}
        bounces={false}
        showsHorizontalScrollIndicator={false}
        contentContainerStyle={styles.scrollContent}
        style={styles.columnsContainer}>
        {columns?.map(renderColumn)}
      </ScrollView>
    </View>
  );
}

export function TableController({
  name,
  showSuccess,
  defaultValue,
  error,
  ...props
}: TableProps & UseInputControllerProps) {
  const inputController = useInputController({
    name,
    showSuccess,
    defaultValue,
  });

  return (
    <Table
      {...props}
      {...inputController}
      error={error ?? inputController.isError}
    />
  );
}
