import { forwardRef } from 'react';
import type { NumericFormatProps } from 'react-number-format';
import { NumericFormat } from 'react-number-format';

/**
 * Type definition for negative value display mask
 * @typedef {string} NegativeMaskType
 * @property {'prefix'} prefix - Display negative as: -1.000.000
 * @property {'suffix'} suffix - Display negative as: 1.000.000-
 * @property {'both'} both - Display negative as: (1.000.000)
 */
export type NegativeMaskType = 'prefix' | 'suffix' | 'both';

/**
 * Props for NumberFormatRupiahWithAllowedNegative component
 */
export interface NumberFormatRupiahWithAllowedNegativeProps
  extends Omit<NumericFormatProps, 'onChange' | 'value'> {
  /** Field name for form integration */
  name: string;

  /** Controlled value from parent component */
  value?: string | number;

  /** Callback fired when value changes */
  onChange: (event: { target: { name: string; value: string } }) => void;

  /** Allow negative values. Default: false */
  allowNegativeValue?: boolean;

  /** Allow decimal values (2 decimal places). Default: false */
  allowDecimalValue?: boolean;

  /** Format style for negative values. Default: 'prefix' */
  negativeMask?: NegativeMaskType;

  /** Maximum allowed value (inclusive) */
  maxValue?: number;

  /** Minimum allowed value (inclusive) */
  minValue?: number;

  /** Maximum length of integer part (excluding separators) */
  maxLength?: number;

  /** Minimum length of integer part (excluding separators) */
  minLength?: number;
}

/**
 * NumberFormatRupiahWithAllowedNegative Component
 *
 * A customized numeric input component for Indonesian Rupiah format with support for negative values.
 * Built on top of react-number-format's NumericFormat component.
 *
 * @component
 * @example
 * // Basic usage (positive only)
 * <NumberFormatRupiahWithAllowedNegative
 *   name="amount"
 *   value={value}
 *   onChange={handleChange}
 * />
 *
 * @example
 * // With negative values (accounting style)
 * <NumberFormatRupiahWithAllowedNegative
 *   name="expense"
 *   value={-100000}
 *   onChange={handleChange}
 *   allowNegativeValue={true}
 *   allowDecimalValue={true}
 *   negativeMask="both"  // Displays as: (100.000,00)
 *   maxValue={0}
 *   minValue={-1000000000}
 * />
 *
 * @example
 * // With validation constraints
 * <NumberFormatRupiahWithAllowedNegative
 *   name="bonus"
 *   value={value}
 *   onChange={handleChange}
 *   allowNegativeValue={true}
 *   negativeMask="prefix"  // Displays as: -100.000
 *   maxValue={10000000}
 *   minValue={-5000000}
 *   maxLength={7}
 * />
 */
const NumberFormatRupiahWithAllowedNegative = forwardRef<
  HTMLInputElement,
  NumberFormatRupiahWithAllowedNegativeProps
>(function NumberFormatRupiahWithAllowedNegative(props, ref) {
  const {
    onChange,
    name,
    allowNegativeValue = false,
    allowDecimalValue = false,
    negativeMask = 'prefix',
    value: controlledValue,
    maxValue,
    minValue,
    maxLength,
    minLength,
    ...other
  } = props;

  // Parse the controlled value to determine if it's negative
  const numValue = parseFloat((controlledValue || '0').toString().replace(/[^\d.-]/g, ''));
  const isNegative = numValue < 0;

  // Determine prefix and suffix based on negative mask configuration
  let prefix = '';
  let suffix = '';

  if (isNegative && allowNegativeValue) {
    switch (negativeMask) {
      case 'prefix':
        prefix = '-';
        break;
      case 'suffix':
        suffix = '-';
        break;
      case 'both':
        prefix = '(';
        suffix = ')';
        break;
      default:
        prefix = '-';
    }
  }

  /**
   * Handles keyboard press events to toggle negative/positive values
   * Press '-' or '_' key to toggle between negative and positive
   *
   * @param {React.KeyboardEvent<HTMLInputElement>} e - Keyboard event
   */
  const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (!allowNegativeValue) return;

    if (e.key === '-' || e.key === '_') {
      e.preventDefault();

      const currentValue = controlledValue || '0';
      const cleanValue = currentValue.toString().replace(/[^\d.-]/g, '');
      const currentNum = parseFloat(cleanValue);

      // Only toggle if value is a valid non-zero number
      if (!isNaN(currentNum) && currentNum !== 0) {
        const newValue = currentNum > 0 ? -Math.abs(currentNum) : Math.abs(currentNum);

        // Validate against min/max constraints before updating
        if (
          (maxValue === undefined || newValue <= maxValue) &&
          (minValue === undefined || newValue >= minValue)
        ) {
          onChange({
            target: {
              name,
              value: newValue.toString(),
            },
          });
        }
      }
    }
  };

  return (
    <NumericFormat
      {...other}
      // Display absolute value; prefix/suffix will indicate sign
      value={Math.abs(numValue) || ''}
      getInputRef={ref}
      name={name}
      // Indonesian locale formatting
      thousandSeparator="."
      decimalSeparator=","
      decimalScale={allowDecimalValue ? 2 : 0}
      // Disable built-in negative handling (we handle it manually)
      allowNegative={false}
      // Apply calculated prefix/suffix for negative indication
      prefix={prefix}
      suffix={suffix}
      onKeyDown={handleKeyPress}
      /**
       * Validates input value against various constraints
       *
       * @param {Object} values - Value object from NumericFormat
       * @returns {boolean} Whether the input is allowed
       */
      isAllowed={(values) => {
        const { floatValue, value } = values;

        if (floatValue === undefined) return true;

        // Constraint 1: Maximum absolute value (12 digits)
        if (Math.abs(floatValue) > 999999999999) {
          return false;
        }

        // Constraint 2: Maximum 2 decimal places
        if (value.includes(',') && value.split(',')[1]?.length > 2) {
          return false;
        }

        // Constraint 3: Integer length validation
        const integerPart = value
          .replace(/[^\d,]/g, '')
          .split(',')[0]
          .replace(/^0+/, '');

        if (maxLength !== undefined && integerPart.length > maxLength) {
          return false;
        }
        if (minLength !== undefined && integerPart.length < minLength) {
          return false;
        }

        return true;
      }}
      /**
       * Handles value changes and applies min/max constraints
       * Preserves negative sign when converting to final value
       *
       * @param {Object} values - Value object from NumericFormat
       */
      onValueChange={(values) => {
        const { floatValue } = values;

        // Handle empty/cleared input
        if (floatValue === undefined) {
          onChange({ target: { name, value: '' } });
          return;
        }

        // Preserve the negative sign based on original value
        // eslint-disable-next-line prefer-const
        let finalValue = isNegative ? -Math.abs(floatValue) : Math.abs(floatValue);

        // Validate against constraints
        if (maxValue !== undefined && finalValue > maxValue) {
          return;
        }
        if (minValue !== undefined && finalValue < minValue) {
          return;
        }

        // Emit change event with string value
        onChange({
          target: {
            name,
            value: finalValue.toString(),
          },
        });
      }}
    />
  );
});

// Set display name for debugging purposes
NumberFormatRupiahWithAllowedNegative.displayName = 'NumberFormatRupiahWithAllowedNegative';

export default NumberFormatRupiahWithAllowedNegative;
