import clsx from 'clsx';
import React, { useRef, useState } from 'react';
import {
  ConfirmDialog,
  ConfirmationConfig,
  ConfirmationMode,
} from '../../ConfirmDialog';
import { SvgElement } from '../../VisualElements/SvgElement';
import { BaseFormControl, BaseInputEvents } from '../Form.models';
import { FormElementContainer } from '../FormElementContainer';
import classes from './Radio.scss';

export interface RadioProps extends BaseFormControl, BaseInputEvents {
  /** Current value the form control has */
  value?: string | number;
  /** Array of options that can be selected from */
  options?: { value: string | number; label: string | number }[];
  /** Whether or not the control should start focused (default: false) */
  autoFocus?: boolean;
  /** If set to 'Advanced', action will require confirmation via a confirmation pop up. (default: 'None') */
  confirmationMode?: Extract<ConfirmationMode, 'None' | 'Advanced'>;
  /** Optional text overrides for the confirmation pop up. */
  confirmationConfig?: ConfirmationConfig;
}

interface RadioButtonProps {
  /** Whether or not this input is checked */
  checked: boolean;
  /** Whether or not the component has an error */
  hasError: boolean;
  /** This input's value */
  inputValue?: string | number;
  /** The new value currently stored as the confirmation dialog is rendered */
  confirmValue?: string | number;

  disabled?: boolean;
}

export const RadioButton: React.FC<RadioButtonProps> = ({
  checked,
  hasError,
  inputValue,
  confirmValue,
  disabled = false,
}) => (
  <SvgElement
    className={clsx(classes.buttonContainer, {
      [classes.checked]: checked,
      [classes.hasError]: hasError,
      [classes.hasConfirm]:
        confirmValue !== undefined &&
        String(inputValue) === String(confirmValue),
      [classes.unSelectable]:
        (confirmValue !== undefined &&
          String(inputValue) !== String(confirmValue)) ||
        disabled,
    })}
    preserveAspectRatio="xMidYMid meet"
    viewBox="0 0 34 34"
  >
    <circle className={classes.radioOutline} cx="17" cy="17" r="15" />
    <circle className={classes.radioDot} cx="17" cy="17" r="7.5" />
  </SvgElement>
);

/**
 * Radio input form control
 * @example
 *  <Radio
 *  name="numbers"
 *  label="Numbers"
 *  options={[{ value: '1', label: 'One' }, { value: '2', label: 'Two' }]}
 *  value={'1'}
 *  onChange={e => console.log(e)}
 *  />
 */
export const Radio: React.FC<RadioProps> = ({
  name,
  value = undefined,
  options = [],
  disabled = false,
  error,
  autoFocus = false,
  confirmationMode = 'None',
  confirmationConfig = {},
  onChange,
  onBlur,
  onFocus,
  className = '',
  ...rest
}) => {
  const errorMsg: string | undefined = error;

  const [confirmation, setConfirmation] = useState<boolean>(false);
  const [referenceElement, setReferenceElement] = useState<HTMLElement>();
  const [confirmValue, setConfirmValue] = useState<string>(); // ref doesn't seem to retain the current target value so its set here
  const ref = useRef<React.ChangeEvent<HTMLInputElement>>();

  const { title, body, cancelButtonText, confirmButtonText, onConfirmOpen } =
    confirmationConfig;

  const onCancelHandler = (): void => {
    setConfirmation(false);
    ref.current = undefined;
    setConfirmValue(undefined);
  };

  const onConfirmHandler = (): void => {
    const confirmed = {
      ...ref.current,
      currentTarget: { value: confirmValue as unknown as string },
    } as React.ChangeEvent<HTMLInputElement>;

    onChange && onChange(confirmed as React.ChangeEvent<HTMLInputElement>);

    setConfirmation(false);
    ref.current = undefined;
    setConfirmValue(undefined);
  };

  return (
    <FormElementContainer
      {...rest}
      className={clsx(classes.container, 'radio-container', className)}
      error={errorMsg}
      dataTestFieldType="Radio"
    >
      <div
        className={clsx(classes.options)}
        ref={setReferenceElement as React.LegacyRef<HTMLDivElement>}
      >
        {options.map(({ label: labe, value: val }) => {
          const radioId = `${name}-${val}`;
          const checked = String(val) === String(value);

          return (
            <div key={val} className={clsx(classes.option)}>
              <input
                type="radio"
                id={radioId}
                name={name}
                value={val}
                checked={checked}
                // Open issue https://github.com/facebook/react/issues/6321
                // "checked" does not update so we need another attribute:
                data-test-checked={checked}
                disabled={disabled || confirmation}
                autoFocus={checked && autoFocus}
                onChange={(e) => {
                  if (confirmationMode === 'Advanced') {
                    ref.current = e;
                    setConfirmValue(e.currentTarget.value);
                    setConfirmation(true);
                    return;
                  }

                  onChange && onChange(e);
                }}
                onBlur={onBlur}
                onFocus={onFocus}
              />
              <label
                htmlFor={radioId}
                className={clsx({ [classes.disabled]: disabled })}
              >
                <RadioButton
                  checked={checked}
                  hasError={errorMsg !== undefined}
                  inputValue={val}
                  confirmValue={confirmValue}
                  disabled={disabled}
                />
                {labe}
              </label>
            </div>
          );
        })}
        {confirmation && (
          <ConfirmDialog
            className={clsx(classes.confirmDialog)}
            referenceElement={referenceElement}
            title={title}
            cancelButtonText={cancelButtonText}
            confirmButtonText={confirmButtonText}
            onCancel={onCancelHandler}
            onConfirm={onConfirmHandler}
            onConfirmOpen={onConfirmOpen}
          >
            {body}
          </ConfirmDialog>
        )}
      </div>
    </FormElementContainer>
  );
};
