import type { ForwardedRef } from 'react';
import { forwardRef, useMemo } from 'react';
import { View, type TextInput, type ViewProps } from 'react-native';
import {
  OtpCellIndexProvider,
  OtpInputField,
  OtpInputProvider,
  dataAttributes,
  mergeDataAttributes,
  useInputContext,
  useOtpInput,
  type IOtpInputRootProps,
  type OtpInputFieldProps,
} from '@cdx-ui/primitives';
import { ParentContext, cn, useParentContext, useStyleContext } from '@cdx-ui/utils';
import { Input, type InputProps } from '../Input';
import { type InputVariantProps, inputFieldVariants } from '../Input/styles';
import {
  otpCellVariants,
  otpFieldVariants,
  otpRootVariants,
  type OtpInputVariantProps,
} from './styles';

const SCOPE = 'OTP_INPUT';

const useOtpInputStyleContext = () => useStyleContext(SCOPE) as Partial<InputVariantProps>;

export interface OtpInputCellProps extends ViewProps, InputVariantProps {
  className?: string;
  children?: React.ReactNode;
  isInvalid?: boolean;
  isReadOnly?: boolean;
  isDisabled?: boolean;
}

const OtpStyledCell = forwardRef<View, OtpInputCellProps>(
  (
    { className, children, variant: variantProp, size: sizeProp, ...props },
    ref: ForwardedRef<View>,
  ) => {
    const context = useOtpInputStyleContext();
    const variant = variantProp ?? context.variant ?? 'outline';
    const size = sizeProp ?? context.size ?? 'default';
    return (
      <Input
        ref={ref}
        variant={variant}
        size={size}
        className={cn(otpCellVariants({ size }), className)}
        {...props}
      >
        {children}
      </Input>
    );
  },
);

OtpStyledCell.displayName = 'OtpInput.Cell';

export type OtpInputFieldStyledProps = OtpInputFieldProps & { className?: string };

/**
 * Bridges OtpInputField focus/blur events to the parent Input's context so
 * InputRoot emits `data-focus=true` and the `border-stroke-action` CSS activates.
 * The primitive OtpInputField doesn't know about InputContext — that coupling
 * belongs here in the styled layer.
 */
const OtpStyledField = forwardRef<TextInput, OtpInputFieldStyledProps>((props, ref) => {
  const { setIsFocused } = useInputContext();
  return (
    <OtpInputField
      ref={ref}
      {...props}
      onFocus={(e) => {
        setIsFocused?.(true);
        props.onFocus?.(e);
      }}
      onBlur={(e) => {
        setIsFocused?.(false);
        props.onBlur?.(e);
      }}
    />
  );
});

OtpStyledField.displayName = 'OtpInput.Field';

export interface OtpInputProps extends Omit<IOtpInputRootProps, 'size'>, OtpInputVariantProps {
  variant?: NonNullable<InputProps['variant']>;
  size?: NonNullable<InputProps['size']>;
  className?: string;
}

const OtpInputRoot = forwardRef<View, OtpInputProps>(
  (
    {
      variant = 'outline',
      size,
      fullWidth,
      className,
      style,
      cellCount,
      value,
      onChangeText,
      onComplete,
      isDisabled,
      isInvalid,
      isReadOnly,
      smsOtpAutofill,
      filterInput,
      allowedCharacters,
      characterPattern,
      getCellProps,
      cellTestIdPrefix,
      accessibilityLabel,
      ...rest
    },
    ref,
  ) => {
    const otpContext = useOtpInput({
      cellCount,
      value,
      onChangeText,
      onComplete,
      isDisabled,
      isInvalid,
      isReadOnly,
      smsOtpAutofill,
      filterInput,
      allowedCharacters,
      characterPattern,
      getCellProps,
      cellTestIdPrefix,
      accessibilityLabel,
    });

    const rootClassName = cn(otpRootVariants({ size, fullWidth }), className);

    const parent = useParentContext();
    const ctx = useMemo(() => ({ ...parent, [SCOPE]: { variant, size } }), [parent, variant, size]);

    const fieldClassName = cn(inputFieldVariants({ size }), otpFieldVariants({ size }));

    const {
      isDisabled: resolvedDisabled,
      isInvalid: resolvedInvalid,
      isReadOnly: resolvedReadOnly,
    } = otpContext;

    const cells: React.ReactElement[] = [];
    for (let i = 0; i < cellCount; i++) {
      cells.push(
        <OtpCellIndexProvider key={i} value={{ cellIndex: i }}>
          <OtpStyledCell
            isDisabled={resolvedDisabled}
            isInvalid={resolvedInvalid}
            isReadOnly={resolvedReadOnly}
          >
            <OtpStyledField className={fieldClassName} />
          </OtpStyledCell>
        </OtpCellIndexProvider>,
      );
    }

    return (
      <ParentContext.Provider value={ctx}>
        <OtpInputProvider value={otpContext}>
          <View
            ref={ref}
            className={rootClassName}
            style={style}
            {...rest}
            {...mergeDataAttributes(rest, dataAttributes({ slot: 'otp-input' }))}
          >
            {cells}
          </View>
        </OtpInputProvider>
      </ParentContext.Provider>
    );
  },
);

OtpInputRoot.displayName = 'OtpInput';

type OtpInputCompoundComponent = typeof OtpInputRoot & {
  Cell: typeof OtpStyledCell;
  Field: typeof OtpStyledField;
};

export const OtpInput = Object.assign(OtpInputRoot, {
  Cell: OtpStyledCell,
  Field: OtpStyledField,
}) as OtpInputCompoundComponent;
