import * as RadixForm from '@radix-ui/react-form';
import React, { createContext, forwardRef, useContext, useMemo } from 'react';
import type { HeadingProps } from '@radix-ui/themes';
import { Heading } from '../../atoms/heading';
import type { TypoProps } from '../../atoms/typo';
import { Typo } from '../../atoms/typo';

const Root = forwardRef<HTMLFormElement, RadixForm.FormProps>(
  ({ children, className, ...rest }, ref) => {
    return (
      <RadixForm.Root
        {...rest}
        className={`FormRoot ${className || ''}`}
        ref={ref}
      >
        {children}
      </RadixForm.Root>
    );
  }
);

Root.displayName = 'FORM_ROOT';

export type FormFieldProps = RadixForm.FormFieldProps & { required?: boolean };

const FieldContext = createContext<FormFieldProps>({
  name: '',
});

const Field = forwardRef<HTMLDivElement, FormFieldProps>(
  ({ children, className, ...rest }, ref) => {
    return (
      <RadixForm.Field
        {...rest}
        className={`FormField ${className || ''}`}
        ref={ref}
      >
        <FieldContext.Provider value={rest}>{children}</FieldContext.Provider>
      </RadixForm.Field>
    );
  }
);
Field.displayName = 'FORM_FIELD';

export type FormLabelProps = RadixForm.FormLabelProps & {
  /** label의 타입을 지정  */
  variant?: 'title' | 'body' | 'caption';
};

function HeadingLabel(props: HeadingProps): React.ReactNode {
  return (
    <Heading variant="heading5" {...props}>
      {props.children}
    </Heading>
  );
}

function CaptionLabel(props: TypoProps): React.ReactNode {
  return (
    <Typo color="gray" variant="caption" {...props}>
      {props.children}
    </Typo>
  );
}

const Label = forwardRef<HTMLLabelElement, FormLabelProps>(
  ({ children, className, variant, ...rest }, ref) => {
    const Comp = useMemo(() => {
      switch (variant) {
        case 'title':
          return HeadingLabel;
        case 'caption':
          return CaptionLabel;
        case 'body':
        default:
          return Typo;
      }
    }, [variant]);

    const fieldProps = useContext(FieldContext);
    return (
      <RadixForm.Label
        {...rest}
        asChild
        className={`FormLabel ${className || ''}`}
        ref={ref}
      >
        <Comp>
          {children}
          {fieldProps.required ? (
            <Typo as="span" color="tomato">
              {` *`}
            </Typo>
          ) : null}
        </Comp>
      </RadixForm.Label>
    );
  }
);
Label.displayName = 'FORM_Label';

const Message = forwardRef<HTMLSpanElement, RadixForm.FormMessageProps>(
  ({ children, className, ...rest }, ref) => {
    return (
      <RadixForm.Message
        {...rest}
        asChild
        className={`FormMessage ${className || ''}`}
        ref={ref}
      >
        <Typo color="red">{children}</Typo>
      </RadixForm.Message>
    );
  }
);
Message.displayName = 'FORM_Message';

type ControlProps = Omit<RadixForm.FormControlProps, 'required'>;
const Control = forwardRef<HTMLInputElement, ControlProps>(
  ({ className, ...rest }, ref) => {
    const fieldProps = useContext(FieldContext);
    return (
      <RadixForm.Control
        {...rest}
        className={`FormControl ${className || ''}`}
        ref={ref}
        required={fieldProps.required}
      />
    );
  }
);
Control.displayName = 'FORM_Control';

interface FormItemProps extends FormFieldProps {
  children: React.ReactNode;
  label: string;
  labelVariant?: FormLabelProps['variant'];
  errorMessage?: string;
}

export function FormItem({
  label,
  labelVariant,
  children,
  errorMessage,
  ...fieldProps
}: FormItemProps): React.ReactNode {
  return (
    <Form.Field {...fieldProps}>
      <Form.Label variant={labelVariant}>{label}</Form.Label>
      {children}
      <Form.Message forceMatch={Boolean(errorMessage)}>
        {errorMessage}
      </Form.Message>
    </Form.Field>
  );
}

export const Form = {
  Root,
  Field,
  Label,
  Message,
  Control: RadixForm.Control,
  Submit: RadixForm.Submit,
  FormItem,
};
