import { useState, forwardRef, useMemo, useEffect, useRef, useImperativeHandle } from 'react';
import { View, TextInput, NativeSyntheticEvent, TextInputContentSizeChangeEventData, TextInput as RNTextInput } from 'react-native';
import { textAreaStyles } from './TextArea.styles';
import Text from '../Text';
import type { TextAreaProps } from './types';
import { getNativeFormAccessibilityProps } from '../utils/accessibility';
import type { TextInputHandle } from '../utils/refTypes';

const TextArea = forwardRef<TextInputHandle, TextAreaProps>(({
  value: controlledValue,
  defaultValue = '',
  onChange,
  onKeyDown: _onKeyDown, // Web-only, no-op on native
  placeholder,
  disabled = false,
  minHeight,
  maxHeight,
  autoGrow = false,
  fill = false,
  maxLength,
  rows = 4,
  label,
  error,
  helperText,
  showCharacterCount = false,
  intent = 'primary',
  size = 'md',
  type = 'outlined',
  // Spacing variants from FormInputStyleProps
  margin,
  marginVertical,
  marginHorizontal,
  style,
  textareaStyle,
  testID,
  id,
  // Accessibility props
  accessibilityLabel,
  accessibilityHint,
  accessibilityDisabled,
  accessibilityHidden,
  accessibilityRole,
  accessibilityLabelledBy,
  accessibilityDescribedBy,
  accessibilityRequired,
  accessibilityInvalid,
  accessibilityErrorMessage,
}, ref) => {
  const [internalValue, setInternalValue] = useState(defaultValue);
  const [isFocused, setIsFocused] = useState(false);
  const [contentHeight, setContentHeight] = useState<number | undefined>(undefined);
  const textInputRef = useRef<RNTextInput>(null);

  // Expose focus/blur via imperative handle
  useImperativeHandle(ref, () => ({
    focus: () => textInputRef.current?.focus(),
    blur: () => textInputRef.current?.blur(),
  }), []);

  const value = controlledValue !== undefined ? controlledValue : internalValue;

  // When value is cleared externally, trigger a layout recalculation via setNativeProps
  useEffect(() => {
    if (autoGrow && value === '' && textInputRef.current) {
      // Force the TextInput to recalculate its layout by setting the text again
      textInputRef.current.setNativeProps({ text: '' });
    }
  }, [value, autoGrow]);
  const hasError = Boolean(error);

  // Generate native accessibility props
  const nativeA11yProps = useMemo(() => {
    const computedLabel = accessibilityLabel ?? label ?? placeholder;
    const isInvalid = accessibilityInvalid ?? hasError;

    return getNativeFormAccessibilityProps({
      accessibilityLabel: computedLabel,
      accessibilityHint: accessibilityHint ?? (error || helperText),
      accessibilityDisabled: accessibilityDisabled ?? disabled,
      accessibilityHidden,
      accessibilityRole: accessibilityRole ?? 'textbox',
      accessibilityLabelledBy,
      accessibilityDescribedBy,
      accessibilityRequired,
      accessibilityInvalid: isInvalid,
      accessibilityErrorMessage: accessibilityErrorMessage ?? error,
    });
  }, [
    accessibilityLabel,
    label,
    placeholder,
    accessibilityHint,
    error,
    helperText,
    accessibilityDisabled,
    disabled,
    accessibilityHidden,
    accessibilityRole,
    accessibilityLabelledBy,
    accessibilityDescribedBy,
    accessibilityRequired,
    accessibilityInvalid,
    hasError,
    accessibilityErrorMessage,
  ]);

  // Apply variants
  textAreaStyles.useVariants({
    size,
    intent,
    type,
    focused: isFocused,
    disabled,
    hasError,
    autoGrow,
    resize: 'none',
    isNearLimit: maxLength ? value.length >= maxLength * 0.9 : false,
    isAtLimit: maxLength ? value.length >= maxLength : false,
    margin,
    marginVertical,
    marginHorizontal,
  });

  const handleChange = (newValue: string) => {
    if (maxLength && newValue.length > maxLength) {
      return;
    }

    if (controlledValue === undefined) {
      setInternalValue(newValue);
    }

    onChange?.(newValue);
  };

  const handleFocus = () => {
    setIsFocused(true);
  };

  const handleBlur = () => {
    setIsFocused(false);
  };

  const handleContentSizeChange = (e: NativeSyntheticEvent<TextInputContentSizeChangeEventData>) => {
    if (!autoGrow) return;

    let newHeight = e.nativeEvent.contentSize.height;

    // Apply min/max height constraints
    if (minHeight && newHeight < minHeight) {
      newHeight = minHeight;
    }

    if (maxHeight && newHeight > maxHeight) {
      newHeight = maxHeight;
    }

    setContentHeight(newHeight);
  };

  const showFooter = (error || helperText) || (showCharacterCount && maxLength);

  // Get dynamic styles - call as functions for theme reactivity
  const containerStyleComputed = (textAreaStyles.container as any)({});
  const labelStyleComputed = (textAreaStyles.label as any)({ disabled });
  const textareaContainerStyleComputed = (textAreaStyles.textareaContainer as any)({ type, focused: isFocused, hasError, disabled });
  const textareaStyleComputed = (textAreaStyles.textarea as any)({ autoGrow, disabled });
  const footerStyleComputed = (textAreaStyles.footer as any)({});
  const helperTextStyleComputed = (textAreaStyles.helperText as any)({ hasError });
  const characterCountStyleComputed = (textAreaStyles.characterCount as any)({
    isNearLimit: maxLength ? value.length >= maxLength * 0.9 : false,
    isAtLimit: maxLength ? value.length >= maxLength : false,
  });

  return (
    <View nativeID={id} style={[containerStyleComputed, fill && { flex: 1 }, style]} testID={testID}>
      {label && (
        <Text style={labelStyleComputed}>{label}</Text>
      )}

      <View style={[textareaContainerStyleComputed, fill && { flex: 1 }]}>
        <TextInput
          ref={textInputRef}
          {...nativeA11yProps}
          style={[
            textareaStyleComputed,
            {
              // Center text vertically when autoGrow (single-line behavior), top-align for multi-line
              textAlignVertical: autoGrow ? 'center' : 'top',
              backgroundColor: 'transparent',
            },
            // fill: expand to fill available space via flex
            // autoGrow: don't set height, let it grow naturally with minHeight constraint
            // default: use rows-based height
            fill
              ? { flex: 1 }
              : autoGrow
                ? {
                    minHeight: minHeight ?? 44,
                    maxHeight: maxHeight,
                    // Force height to minHeight when empty to ensure shrinking
                    ...(value === '' ? { height: minHeight ?? 44 } : {}),
                  }
                : { height: rows * 24 },
            textareaStyle,
          ]}
          value={value}
          onChangeText={handleChange}
          onFocus={handleFocus}
          onBlur={handleBlur}
          onContentSizeChange={handleContentSizeChange}
          placeholder={placeholder}
          editable={!disabled}
          multiline
          // Disable internal scrolling when autoGrow - let the TextInput expand instead
          scrollEnabled={!autoGrow || (maxHeight !== undefined && contentHeight !== undefined && contentHeight >= maxHeight)}
          numberOfLines={autoGrow ? undefined : rows}
          maxLength={maxLength}
          placeholderTextColor="#999"
        />
      </View>

      {showFooter && (
        <View style={footerStyleComputed}>
          <View style={{ flex: 1 }}>
            {error && (
              <Text style={helperTextStyleComputed}>
                {error}
              </Text>
            )}
            {!error && helperText && (
              <Text style={helperTextStyleComputed}>
                {helperText}
              </Text>
            )}
          </View>

          {showCharacterCount && maxLength && (
            <Text style={characterCountStyleComputed}>
              {value.length}/{maxLength}
            </Text>
          )}
        </View>
      )}
    </View>
  );
});

TextArea.displayName = 'TextArea';

export default TextArea;
