import { cva } from 'class-variance-authority';
import * as React from 'react';
import { cn, ComponentAnatomy, defineStyleAnatomy } from '../core/styling';

/* -------------------------------------------------------------------------------------------------
 * Anatomy
 * -----------------------------------------------------------------------------------------------*/

export const BasicFieldAnatomy = defineStyleAnatomy({
  fieldLabel: cva([
    'UI-BasicField__fieldLabel',
    'text-base w-fit font-semibold self-start',
    'data-[error=true]:text-red-500',
  ]),
  fieldAsterisk: cva('UI-BasicField__fieldAsterisk ml-1 text-red-500 text-sm'),
  fieldDetails: cva('UI-BasicField__fieldDetails'),
  field: cva('UI-BasicField__field relative w-full space-y-1'),
  fieldHelpText: cva('UI-BasicField__fieldHelpText text-sm text-[--muted]'),
  fieldErrorText: cva('UI-BasicField__fieldErrorText text-sm text-red-500'),
});

/* -------------------------------------------------------------------------------------------------
 * BasicFieldOptions
 * - Field components inherit these props
 * -----------------------------------------------------------------------------------------------*/

export type BasicFieldOptions = ComponentAnatomy<typeof BasicFieldAnatomy> & {
  /**
   * The id of the field. If not provided, a unique id will be generated.
   */
  id?: string | undefined;
  /**
   * The form field name.
   */
  name?: string;
  /**
   * The label of the field.
   */
  label?: React.ReactNode;
  /**
   * Additional props to pass to the label element.
   */
  labelProps?: React.LabelHTMLAttributes<HTMLLabelElement>;
  /**
   * Help or description text to display below the field.
   */
  help?: React.ReactNode;
  /**
   * Error text to display below the field.
   */
  error?: string;
  /**
   * If `true`, the field will be required.
   */
  required?: boolean;
  /**
   * If `true`, the field will be disabled.
   */
  disabled?: boolean;
  /**
   * If `true`, the field will be readonly.
   */
  readonly?: boolean;
};

/* -------------------------------------------------------------------------------------------------
 * Extract BasicFieldProps
 * -----------------------------------------------------------------------------------------------*/

export function extractBasicFieldProps<Props extends BasicFieldOptions>(
  props: Props,
  id: string
) {
  const {
    name,
    label,
    labelProps,
    help,
    error,
    required,
    disabled = false,
    readonly = false,
    fieldDetailsClass,
    fieldLabelClass,
    fieldAsteriskClass,
    fieldClass,
    fieldErrorTextClass,
    fieldHelpTextClass,
    id: _id,
    ...rest
  } = props;
  return [
    rest,
    {
      id: _id || id,
      name,
      label,
      help,
      error,
      disabled,
      required,
      readonly,
      fieldAsteriskClass,
      fieldErrorTextClass,
      fieldHelpTextClass,
      fieldDetailsClass,
      fieldLabelClass,
      fieldClass,
      labelProps,
    },
  ] as [
    Omit<
      Props,
      | 'label'
      | 'name'
      | 'help'
      | 'error'
      | 'disabled'
      | 'required'
      | 'readonly'
      | 'fieldDetailsClass'
      | 'fieldLabelClass'
      | 'fieldClass'
      | 'fieldHelpTextClass'
      | 'fieldErrorTextClass'
      | 'id'
      | 'labelProps'
      | 'fieldAsteriskClass'
    >,
    Omit<BasicFieldOptions, 'id'> & {
      id: string;
    },
  ];
}

/* -------------------------------------------------------------------------------------------------
 * BasicField
 * -----------------------------------------------------------------------------------------------*/

export type BasicFieldProps = React.ComponentPropsWithoutRef<'div'> &
  BasicFieldOptions;

export const BasicField = React.memo(
  React.forwardRef<HTMLDivElement, BasicFieldProps>((props, ref) => {
    const {
      children,
      className,
      labelProps,
      id,
      label,
      error,
      help,
      disabled,
      readonly,
      required,
      fieldClass,
      fieldDetailsClass,
      fieldLabelClass,
      fieldAsteriskClass,
      fieldErrorTextClass,
      fieldHelpTextClass,
      ...rest
    } = props;

    return (
      <div
        className={cn(BasicFieldAnatomy.field(), className, fieldClass)}
        {...rest}
        ref={ref}
      >
        {!!label && (
          <label
            htmlFor={disabled ? undefined : id}
            className={cn(BasicFieldAnatomy.fieldLabel(), fieldLabelClass)}
            data-error={!!error}
            {...labelProps}
          >
            {label}
            {required && (
              <span
                className={cn(
                  BasicFieldAnatomy.fieldAsterisk(),
                  fieldAsteriskClass
                )}
              >
                *
              </span>
            )}
          </label>
        )}

        {children}

        {(!!help || !!error) && (
          <div
            className={cn(BasicFieldAnatomy.fieldDetails(), fieldDetailsClass)}
          >
            {!!help && (
              <div
                className={cn(
                  BasicFieldAnatomy.fieldHelpText(),
                  fieldHelpTextClass
                )}
              >
                {help}
              </div>
            )}
            {!!error && (
              <div
                className={cn(
                  BasicFieldAnatomy.fieldErrorText(),
                  fieldErrorTextClass
                )}
              >
                {error}
              </div>
            )}
          </div>
        )}
      </div>
    );
  })
);

BasicField.displayName = 'BasicField';
