import { PropsWithChildren, createContext, useContext, useMemo } from 'react';

import { useId } from '../../../hooks/useId';
import { TableColumnVisibility } from '../types';

import { useTablePropsContext } from './TablePropsContext';

/** Value of {@link TableVariablesContext} */
export interface TableVariablesContextValue<C extends string> {
  /** If of element that used as `aria-labelledby` {@link TableGrid} attribute */
  labelId: string;
  /** If of element that used as `aria-describedby` {@link TableGrid} attribute */
  descriptionId: string;
  /** List of column visibility */
  columnVisibility: TableColumnVisibility<C>[];
  /** List of column that should be rendered in table with correct order */
  visibleDataColumns: C[];
  /** Total number of rows (including header row) that should be rendered */
  totalTableRenderedRows: number;
  /** Total number of columns (including selection and actions columns) that should be rendered */
  totalTableRenderedColumns: number;
}

const TableVariablesContext = createContext<TableVariablesContextValue<any> | undefined>(undefined);

/** Provider that provides some data that calculated from table props */
export function TableVariablesContextProvider<C extends string>({ children }: PropsWithChildren<{}>) {
  const { columns, rowActionsEnabled, rowSelectionEnabled, rows } = useTablePropsContext();

  const labelId = useId();
  const descriptionId = useId();

  const columnVisibility = useMemo(() => {
    return columns.map((column) => {
      if (typeof column === 'string') {
        return {
          column,
          visible: true,
        } as TableColumnVisibility<C>;
      } else {
        return column;
      }
    });
  }, [columns]);

  const visibleDataColumns: C[] = useMemo(() => {
    return columnVisibility.filter((column) => column.visible).map((column) => column.column);
  }, [columnVisibility]);

  const totalTableRenderedColumns = useMemo(() => {
    if (visibleDataColumns.length === 0) {
      return 0;
    }
    return visibleDataColumns.length + (rowSelectionEnabled ? 1 : 0) + (rowActionsEnabled ? 1 : 0);
  }, [visibleDataColumns.length, rowSelectionEnabled, rowActionsEnabled]);

  const headerRows = 1;
  const totalTableRenderedRows = rows.length + headerRows;

  const value = useMemo(
    () => ({
      labelId,
      descriptionId,
      columnVisibility,
      visibleDataColumns,
      totalTableRenderedRows,
      totalTableRenderedColumns,
    }),
    [
      columnVisibility,
      descriptionId,
      labelId,
      totalTableRenderedColumns,
      totalTableRenderedRows,
      visibleDataColumns,
    ],
  );

  return <TableVariablesContext.Provider value={value}>{children}</TableVariablesContext.Provider>;
}

/** Returns various data that calculated from table props */
export function useTableVariablesContext<C extends string>(): TableVariablesContextValue<C> {
  const value = useContext(TableVariablesContext);
  if (!value) {
    throw new Error('useTableVariablesContext should be used only inside TableVariablesContextProvider');
  }

  return value;
}
