import clsx from 'clsx';
import { LocationDescriptor } from 'history';
import React, { PropsWithChildren, useCallback, useMemo } from 'react';
import { Link } from 'react-router-dom';
import { noop } from '../../../helpers/utils';
import { Data } from '../../../types/data';
import { ActionData } from '../../Actions/Actions.models';
import { Button } from '../../Buttons';
import { IconName } from '../../Icons';
import { InlineMenu } from '../../InlineMenu/InlineMenu';
import { Column, ListSelectMode } from '../List.model';
import { ListCheckBox } from '../ListCheckBox/ListCheckBox';
import classes from './ListRow.scss';
import { ListRowCell } from './ListRowCell/ListRowCell';

export interface ListRowProps<T extends Data> {
  /** Spacing between columns */
  columnSizes: string;
  /** Header row height */
  columnGap: string;
  /** List row height */
  rowHeight?: string;
  /** Size of action button and checkbox */
  actionSize: string;
  /** Horizontal alignment of text (default: left) */
  horizontalTextAlign?: 'left' | 'right' | 'center';
  /** Vertical alignment of text (default: right) */
  verticalTextAlign?: 'start' | 'center' | 'end';
  /** If set to true, column text overflow will be wrapped to a new line. Otherwise, it will be truncated with an ellipsis (default: false) */
  textWrap?: boolean;
  /** List data */
  data: T;
  /** The column definition */
  columns: Column<T>[];
  /** Whether or not the item is selected (default: false) */
  itemSelected?: boolean;
  /** Determines which selection mode the list is in. (default: ListSelectMode.None) */
  selectionMode?: ListSelectMode;
  /** Defines whether an action button will be rendered (default: true) */
  showActionButton?: boolean;
  /** Defines whether a check mark action button will be rendered (default: true) */
  showCheckMark?: boolean;
  /** Defines whether a checkbox for each item should be rendered (default: false) */
  showItemCheckbox?: boolean;
  /** Defines whether a row is disabled (default: false) */
  isRowDisabled?: boolean;
  /** Used for when a row is clicked. This can be either a callback function that will be executed
   * or a URL where the user should be navigated to.
   * When the list should navigate the user to e.g. a details page of a entry it is recommended to
   * directly pass the URL here instead of a callback function that does the routing in JavaScript. */
  onItemClicked?: LocationDescriptor<unknown> | ((data: T) => void);
  /**
   * A function that is getting called, when the selection state of the row changes.
   */
  onItemSelected?: (checked: boolean) => void;
  /** Provide inline actions which are available through '...' context menu */
  inlineMenuActions?: (data: T) => ActionData[];
}

let dateFormatter: Intl.DateTimeFormat;

let numberFormatter: Intl.NumberFormat;

export const setLocale = (locale: string): void => {
  dateFormatter = new Intl.DateTimeFormat(locale, {
    year: 'numeric',
    month: 'numeric',
    day: 'numeric',
    hour: 'numeric',
    minute: 'numeric',
    second: 'numeric',
  });

  numberFormatter = new Intl.NumberFormat(navigator.language);
};

setLocale(navigator.language);

/**
 * Renders the rows for the list component
 * @example
 * <ListRow
 *  key={index}
 *  data={[{id: '1',desc: 'Description 1',title: 'Item 1'}]}
 *  columnSizes={'1fr'}
 *  columnGap={'5px'}
 *  rowHeight={'50px'}
 *  horizontalTextAlign={'left'}
 *  verticalTextAlign={'center'}
 * />
 */
const ListRowComponent = <T extends Data>(
  {
    columnSizes,
    columnGap,
    actionSize,
    horizontalTextAlign = 'left',
    verticalTextAlign = 'center',
    textWrap = false,
    rowHeight = textWrap ? 'auto' : '50px',
    data,
    itemSelected = false,
    columns,
    selectionMode = ListSelectMode.None,
    showActionButton = true,
    showCheckMark = false,
    showItemCheckbox = false,
    onItemClicked = noop,
    onItemSelected = noop,
    isRowDisabled = false,
    inlineMenuActions,
  }: PropsWithChildren<ListRowProps<T>>,
  ref: React.Ref<HTMLDivElement>,
): JSX.Element => {
  const customRootStyles = {
    gridAutoRows: `minmax(50px, ${rowHeight})`,
    gridColumnGap: columnGap,
    justifyItems: horizontalTextAlign,
    alignItems: verticalTextAlign,
    gridTemplateColumns: columnSizes,
  } as React.CSSProperties;

  const onItemClickedHandler = useCallback(
    (data: T) => {
      // Do nothing if row is disabled
      if (isRowDisabled) {
        return;
      }

      // Emit onItemSelected only if list is in Multi mode
      if (selectionMode === ListSelectMode.Multi) {
        onItemSelected(!itemSelected);
        // Emit onItemClicked for all other modes
      } else if (typeof onItemClicked === 'function') {
        onItemClicked(data);
      }
    },
    [isRowDisabled, itemSelected, onItemClicked, onItemSelected, selectionMode],
  );

  const isLinkable: boolean =
    selectionMode === ListSelectMode.None &&
    typeof onItemClicked !== 'function';

  const generateCells = useMemo(() => {
    return columns.map((column: Column<T>) => {
      return (
        <ListRowCell<T>
          key={column.key ?? (column.propertyName as string)}
          column={column}
          data={data}
          horizontalTextAlign={horizontalTextAlign}
          verticalTextAlign={verticalTextAlign}
          textWrap={textWrap}
          dateFormatter={dateFormatter}
          numberFormatter={numberFormatter}
        />
      );
    });
  }, [columns, data, horizontalTextAlign, textWrap, verticalTextAlign]);

  const inlineActionMenuData = inlineMenuActions
    ? inlineMenuActions(data)
    : undefined;

  return (
    <div
      className={clsx(classes.columnsRoot, {
        [classes.selected]: itemSelected,
        [classes.disabled]: isRowDisabled,
      })}
      style={customRootStyles}
      ref={ref}
      data-test-id="list-entry"
    >
      {/* Items */}
      {isLinkable ? (
        <Link
          to={onItemClicked as string}
          className={clsx(classes.content, classes.link)}
        >
          {generateCells}
        </Link>
      ) : (
        <div
          className={classes.content}
          onClick={() => onItemClickedHandler(data)}
        >
          {generateCells}
        </div>
      )}
      {(inlineMenuActions ||
        showActionButton ||
        showCheckMark ||
        showItemCheckbox) && (
        <div className={classes.actions}>
          {inlineMenuActions &&
            inlineActionMenuData &&
            inlineActionMenuData.length > 0 && (
              <InlineMenu
                actions={inlineActionMenuData}
                showArrow={false}
                placement="bottom-end"
              />
            )}
          {showActionButton && selectionMode === ListSelectMode.None && (
            <>
              {showActionButton &&
                (typeof onItemClicked !== 'function' ? (
                  <Button
                    icon={IconName.ChevronRight}
                    height={actionSize}
                    width={actionSize}
                    path={onItemClicked}
                    dataTestId="list-entry-action"
                  />
                ) : (
                  <Button
                    icon={IconName.ChevronRight}
                    height={actionSize}
                    width={actionSize}
                    onButtonClicked={() => onItemClickedHandler(data)}
                    dataTestId="list-entry-action"
                  />
                ))}
            </>
          )}

          {showCheckMark && (
            <Button
              icon={IconName.Checkmark}
              height={actionSize}
              width={actionSize}
              dataTestId="list-entry-select-button"
              className={classes.selectionCheckMark}
              onButtonClicked={() => onItemClickedHandler(data)}
            />
          )}
          {showItemCheckbox && (
            <ListCheckBox
              height={actionSize}
              width={actionSize}
              onCheckBoxToggled={() => onItemClickedHandler(data)}
              isChecked={itemSelected}
              isDisabled={isRowDisabled}
            />
          )}
        </div>
      )}
    </div>
  );

  // return Row;
};

export const ListRow = React.forwardRef(ListRowComponent) as <T extends Data>(
  props: PropsWithChildren<ListRowProps<T>> & {
    ref?: React.Ref<HTMLDivElement>;
  },
) => JSX.Element;
