import React, {
  useContext,
  forwardRef,
  Ref,
  LabelHTMLAttributes,
  ReactNode,
} from "react";
import classNames from "classnames";
import { useRefAndForward } from "../_util/use-ref-and-forward";
import { WithoutMethod, Omit } from "../_type";
import {
  ControlledProps,
  ChangeContext,
  useDefaultValue,
} from "../form/controlled";
import { Tooltip } from "../tooltip";
import { callBoth } from "../_util/call-both";
import { useConfig } from "../_util/config-context";
import { useIsomorphicLayoutEffect } from "../_util/use-isomorphic-layout-effect";

/**
 * `CheckInput` 组件所接收的参数
 */
export interface CheckProps
  extends ControlledProps<boolean>,
    Omit<LabelHTMLAttributes<HTMLLabelElement>, "onChange" | "defaultValue"> {
  /**
   * 获取内部 `input` 的引用
   */
  inputRef?: React.Ref<HTMLInputElement>;

  /**
   * 名称，对于使用 `CheckGroup` 等管理时必传
   */
  name?: string;

  /**
   * TreeNode Content
   */
  content?: ReactNode;

  /**
   * 选项类型
   */
  type: "radio" | "checkbox";

  /**
   * 默认是否已经勾选
   */
  defaultValue?: boolean;

  /**
   * 是否已经勾选
   */
  value?: boolean;

  /**
   * 勾选状态发生变更时回调
   */
  onChange?: (value: boolean, context: CheckChangeContext) => void;

  /**
   * 是否半选状态（仅对 `checkbox` 生效）
   * @default false
   */
  indeterminate?: boolean;

  /**
   * 是否可用
   * @default false
   */
  disabled?: boolean;

  /**
   * 渲染方式，设置为 `block` 会独占一行
   * @default "inline"
   */
  display?: "inline" | "block";

  /** 文本解释内容，可用于解释选项的作用 */
  tooltip?: React.ReactNode;

  /** 点击时触发 */
  onClick?: (event: React.MouseEvent) => void;
}

/**
 * 为变更事件提供额外的内容
 */
export interface CheckChangeContext extends ChangeContext {
  /**
   * 发生变化的选项组件实例
   */
  check: ReadonlyCheckProps;
}

/**
 * 组件实例只读属性集合
 */
export interface ReadonlyCheckProps
  extends Readonly<WithoutMethod<CheckProps>> {}

/**
 * Check 组件支持使用 CheckContext 进行状态托管
 */
export const CheckContext = React.createContext<CheckContextValue>(null);

/**
 * 托管 Check 组件的状态，请提供 inject() 方法注入托管好的 props
 */
export interface CheckContextValue {
  inject: (props: CheckProps) => CheckProps;
}

export const Check = forwardRef(
  (_props: CheckProps, ref: Ref<HTMLLabelElement>) => {
    const { classPrefix } = useConfig();
    // 支持从 Context 注入
    const context = useContext(CheckContext);
    let props = _props;
    if (context) {
      props = context.inject(_props);
    }

    const {
      value,
      onChange,
      type,
      indeterminate,
      children,
      className,
      disabled = false,
      onClick,
      display,
      style = {},
      inputRef,
      tooltip,
      tabIndex,
      ...htmlProps
    } = useDefaultValue(props, false);

    // handle indeterminate
    const [checkInputRef, forwardInputRef] = useRefAndForward(null, inputRef);

    useIsomorphicLayoutEffect(() => {
      if (type === "checkbox" && checkInputRef.current) {
        checkInputRef.current.indeterminate = indeterminate;
      }
    });

    const labelClassName = classNames(
      `${classPrefix}-form-check`,
      {
        [`${classPrefix}-form-check--block`]: display === "block",
      },
      className
    );

    const input = (
      <input
        readOnly
        ref={forwardInputRef}
        type={type}
        className={`${classPrefix}-${type}`}
        checked={value}
        disabled={disabled}
        style={disabled ? { pointerEvents: "none" } : {}}
        tabIndex={tabIndex}
        onClick={callBoth(
          onClick,
          (event: React.MouseEvent<HTMLInputElement>) =>
            onChange(event.currentTarget.checked, {
              event,
              check: { ...props, value: event.currentTarget.checked },
            })
        )}
      />
    );

    let check = (
      <label
        ref={ref}
        className={labelClassName}
        style={disabled ? { cursor: "not-allowed", ...style } : style}
        {...htmlProps}
        onClick={event => event.stopPropagation()}
      >
        {type === "radio" ? (
          <span className={`${classPrefix}-radio-wrap`}>
            {input}
            <i className={`${classPrefix}-radio__dot`} />
          </span>
        ) : (
          input
        )}
        <span
          key="label"
          className={`${classPrefix}-form-check__label`}
          style={disabled ? { cursor: "not-allowed" } : {}}
        >
          {children}
        </span>
      </label>
    );

    if (tooltip) {
      check = <Tooltip title={tooltip}>{check}</Tooltip>;
    }

    return check;
  }
);

Check.displayName = "Check";
