import React, { forwardRef } from "react";
import classNames from "classnames";
import { Omit } from "../../_type";
import { TableAddon, TableProps, RowRenderContext } from "../TableProps";
import { ControlledProps } from "../../form/controlled";
import { Radio } from "../../radio";
import { injectPropsIfTargetNotExisted } from "../util/inject-props-if-target-not-existed";

/**
 * `radioable` 插件用于支持表格可单选行的样式及操作。
 */
export interface RadioableOptions<Record = any>
  extends ControlledProps<
    string,
    React.SyntheticEvent,
    { event: React.SyntheticEvent; record: Record }
  > {
  /**
   * 不支持非受控模式
   */
  defaultValue?: never;

  /**
   * 提供一个列的 `key`，将选择组件插入到一个目标列
   *
   * 默认在最前新建一列插入
   */
  targetColumnKey?: string;

  /**
   * 是否整行可选
   * @default false
   */
  rowSelect?: boolean;

  /**
   * 列宽度，可以指定 CSS 属性或数字 (单位：px)
   * @default 26
   */
  width?: string | number;

  /**
   * **高级用法**
   * 更改该插件的在每行的渲染内容，`element` 为默认渲染内容，`context` 中包含该行数据相关信息
   * @default x => x
   */
  render?: (
    element: JSX.Element,
    context: RowRenderContext<Record>
  ) => React.ReactNode;
}

const fallbackColumnKey = "__radioable_addon__";

let rowDisabled: TableProps["rowDisabled"] = null;

export function radioable({
  value,
  onChange,
  targetColumnKey,
  rowSelect,
  width = 26,
  render = x => x,
}: RadioableOptions): TableAddon {
  return {
    getInfo: () => ({ name: "radioable" }),
    onInjectProps: props => {
      rowDisabled = props.rowDisabled || (() => false);

      const columns = injectPropsIfTargetNotExisted(
        props.columns,
        targetColumnKey,
        {
          key: fallbackColumnKey,
          width,
          header: null,
          render: () => null,
        }
      );

      return { ...props, columns };
    },
    onInjectColumn: previous => (
      record,
      rowKey,
      recordIndex,
      column,
      columnIndex
    ) => {
      // 不是目标列
      if (column.key !== targetColumnKey && column.key !== fallbackColumnKey) {
        return previous(record, rowKey, recordIndex, column, columnIndex);
      }

      const { children: preChildren, props, ...result } = previous(
        record,
        rowKey,
        recordIndex,
        column,
        columnIndex
      );

      let children = preChildren;

      // 非表头
      if (recordIndex !== -1) {
        const element = (
          <Radio
            disabled={rowDisabled(record)}
            value={value === rowKey}
            onChange={(_, { event }) => onChange(rowKey, { event, record })}
          >
            {children || <>&nbsp;</>}
          </Radio>
        );
        children = render(element, {
          children: preChildren,
          record,
          rowKey,
          recordIndex,
          disabled: rowDisabled(record),
        });
      }
      return { ...result, props, children };
    },
    onInjectRow: renderRow => (record, rowKey, recordIndex, columns) => {
      const { prepends, appends, row: preRow } = renderRow(
        record,
        rowKey,
        recordIndex,
        columns
      );
      let row = preRow;
      // 支持整行选择
      row = (
        <SelectWrapper
          key={rowKey}
          name={rowKey}
          value={value}
          onChange={(value, context) => onChange(value, { ...context, record })}
          rowSelect={rowSelect && !rowDisabled(record)}
        >
          {row}
        </SelectWrapper>
      );
      return { prepends, row, appends };
    },
  };
}

const SelectWrapper = forwardRef(function SelectWrapper(
  {
    value,
    onChange,
    name,
    rowSelect,
    children,
    ...props
  }: {
    name: string;
    rowSelect: boolean;
    value: string;
    onChange: (name: string, context: { event: React.SyntheticEvent }) => void;
    children: React.ReactElement<React.HTMLAttributes<HTMLTableRowElement>>;
  } & Omit<React.HTMLAttributes<HTMLTableRowElement>, "onChange">,
  ref
): JSX.Element {
  const rowSelectProps = {
    onClick: (event: React.MouseEvent<HTMLTableRowElement>) => {
      // 事件合并
      if (typeof props.onClick === "function") {
        props.onClick(event);
      }
      if (typeof children.props.onClick === "function") {
        children.props.onClick(event);
      }

      return onChange(name, { event });
    },
  };

  return React.cloneElement(children, {
    ...props,
    ref,
    className: classNames(props.className, children.props.className, {
      "is-selected": value === name,
    }),
    ...(rowSelect ? rowSelectProps : {}),
  });
});
