import { forwardRef, useMemo, type ReactNode } from 'react';
import { type TextInput, View, type ViewProps } from 'react-native';
import {
  InputField as InputFieldPrimitive,
  InputRoot as InputRootPrimitive,
  Slot,
  dataAttributes,
  mergeDataAttributes,
  type IInputFieldProps,
  type IInputProps,
  type IInputSlotProps,
} from '@cdx-ui/primitives';
import { ParentContext, cn, useParentContext, useStyleContext } from '@cdx-ui/utils';
import { Icon, type IconProps } from '../Icon';
import {
  type InputVariantProps,
  inputFieldPlaceholderVariants,
  inputFieldVariants,
  inputIconVariants,
  inputRootVariants,
  inputSlotVariants,
} from './styles';

const SCOPE = 'INPUT';

const useInputStyleContext = () => useStyleContext(SCOPE) as InputVariantProps;

// =============================================================================
// INPUT (ROOT)
// =============================================================================

export interface InputProps extends ViewProps, IInputFieldProps {
  /** Render the consumer's child element in place of the default `View` host. */
  asChild?: boolean;
  /** Additional Uniwind/Tailwind classes, merged after the shell's own styles. */
  className?: string;
  /** Shell content — typically `Input.Field` plus optional `Input.Slot`/`Input.Icon`. */
  children?: ReactNode;
  /**
   * Visual style variant.
   * @default 'outline'
   */
  variant?: 'outline';
  /**
   * Height and padding scale.
   * @default 'default'
   */
  size?: 'default' | 'small';
}

const InputRoot = forwardRef<View, InputProps>(
  (
    { asChild, variant = 'outline', size = 'default', className, children, style, ...props },
    ref,
  ) => {
    const computedClassName = cn(inputRootVariants({ variant, size }), className);

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

    return (
      <ParentContext.Provider value={ctx}>
        <InputRootPrimitive
          ref={ref}
          asChild={asChild}
          className={computedClassName}
          style={style}
          {...props}
          {...mergeDataAttributes(props, dataAttributes({ slot: 'input' }))}
        >
          {children}
        </InputRootPrimitive>
      </ParentContext.Provider>
    );
  },
);

InputRoot.displayName = 'Input';

// =============================================================================
// INPUT FIELD
// =============================================================================

export type InputFieldProps = IInputProps & {
  /** Additional Uniwind/Tailwind classes, merged after the field's own styles. */
  className?: string;
  /** Uniwind class applied to the placeholder text color (native `placeholderTextColor`). */
  placeholderTextColorClassName?: string;
};

const InputField = forwardRef<TextInput, InputFieldProps>(
  ({ className, placeholderTextColorClassName, style, ...rest }, ref) => {
    const { size } = useInputStyleContext();

    const computedClassName = cn(inputFieldVariants({ size }), className);
    const computedPlaceholderColorClassName = cn(
      inputFieldPlaceholderVariants(),
      placeholderTextColorClassName,
    );

    return (
      <InputFieldPrimitive
        ref={ref}
        className={computedClassName}
        placeholderTextColorClassName={computedPlaceholderColorClassName}
        style={style}
        {...rest}
        {...mergeDataAttributes(rest, dataAttributes({ slot: 'input-field' }))}
      />
    );
  },
);

InputField.displayName = 'Input.Field';

// =============================================================================
// INPUT SLOT — styled-only (Standard 3: no behavior beyond layout)
// =============================================================================

export interface InputSlotProps extends ViewProps, IInputSlotProps {
  /** Render the consumer's child element in place of the default `View` host. */
  asChild?: boolean;
  /** Additional Uniwind/Tailwind classes, merged after the slot's own styles. */
  className?: string;
  /** Adornment content (icons, buttons, or adornment text). */
  children?: ReactNode;
}

const InputSlot = forwardRef<View, InputSlotProps>(
  ({ asChild, className, children, style, ...props }, ref) => {
    const computedClassName = cn(inputSlotVariants(), className);
    const Comp = asChild ? Slot : View;

    return (
      <Comp
        ref={ref}
        className={computedClassName}
        style={style}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'input-slot' }))}
      >
        {children}
      </Comp>
    );
  },
);

InputSlot.displayName = 'Input.Slot';

// =============================================================================
// INPUT ICON
// =============================================================================

export interface InputIconProps extends Omit<IconProps, 'children' | 'as'> {
  /** Render the consumer's child element in place of the default icon host. */
  asChild?: boolean;
  /**
   * Forge icon component to render. Required unless `asChild` is set, in which
   * case the consumer's substituted element is rendered instead.
   */
  as?: IconProps['as'];
}

const InputIcon = ({ asChild, className, style, as, ...props }: InputIconProps) => {
  const { size } = useInputStyleContext();
  const computedClassName = cn(inputIconVariants({ size }), className);

  if (asChild) {
    return (
      <Slot
        className={computedClassName}
        style={style}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'input-icon' }))}
      />
    );
  }

  if (!as) {
    return null;
  }

  return (
    <Icon
      as={as}
      className={computedClassName}
      style={style}
      {...props}
      {...mergeDataAttributes(props, dataAttributes({ slot: 'input-icon' }))}
    />
  );
};

InputIcon.displayName = 'Input.Icon';

// =============================================================================
// COMPOUND EXPORT
// =============================================================================

type InputCompoundComponent = typeof InputRoot & {
  Field: typeof InputField;
  Slot: typeof InputSlot;
  Icon: typeof InputIcon;
};

export const Input = Object.assign(InputRoot, {
  Field: InputField,
  Slot: InputSlot,
  Icon: InputIcon,
}) as InputCompoundComponent;
