import { Switch, type SwitchProps } from 'antd';
import React, { useEffect, useState } from 'react';

interface AsyncSwitchProps extends Omit<SwitchProps, 'onChange'> {
  // Execute the function asynchronously. If successful, the state will be switched. If failed, the state will remain unchanged.
  onAsyncChange: (checked: boolean) => Promise<any>;
  // Standard onChange, automatically injected in Form mode
  onChange?: (checked: boolean) => void;
  // Form.Item binds the value attribute to value by default, and the value attribute of Switch is checked. You can modify the bound value property through valuePropName.
  /**
    <Form.Item name="fieldA" valuePropName="checked">
      <Switch />
    </Form.Item>
   */
  value?: boolean;
  defaultChecked?: boolean;
  // Successfully/unsuccessfully hook
  onSuccess?: () => void;
  onError?: (err: any) => void;
}

const AsyncSwitch: React.FC<AsyncSwitchProps> = (props) => {
  const {
    value,
    defaultChecked,
    onChange,
    onAsyncChange,
    onSuccess,
    onError,
    disabled,
    ...rest
  } = props;
  // 1. Internal maintenance status, the initial value is checked first, followed by defaultChecked
  const [innerChecked, setInnerChecked] = useState<boolean>(
    !!(value ?? defaultChecked),
  );
  const [loading, setLoading] = useState<boolean>(false);

  // 2. When the external checked changes (such as Form reset or external manual modification), synchronize the internal state
  useEffect(() => {
    if (value !== void 0) {
      setInnerChecked(value);
    }
  }, [value]);

  const handleChange = async (val: boolean) => {
    setLoading(true);
    try {
      // Execute asynchronous logic
      await onAsyncChange(val);

      // Processing after success:
      // If checked is not passed externally (uncontrolled mode), the internal state is updated
      if (value === void 0) {
        setInnerChecked(val);
      }

      // Trigger external onChange (if it is Form, Form takes over the status update)
      onChange?.(val);

      onSuccess?.();
    } catch (error) {
      console.warn('AsyncSwitch Error:', error);
      onError?.(error);
      // Failure does nothing and leaves the Switch as is
    } finally {
      setLoading(false);
    }
  };

  return (
    <Switch
      {...rest}
      loading={loading}
      // Always use innerChecked in preference as it is the final representation after synchronizing with the outside
      checked={innerChecked}
      onChange={handleChange}
      disabled={disabled || loading}
    />
  );
};

export default AsyncSwitch;
