'use client';

import {
  ChangeEvent,
  ChangeEventHandler,
  FocusEventHandler,
  ReactNode,
  useEffect,
  useState,
} from 'react';

import { Icon } from '@iconify/react';
import { cn } from '@/lib/helper';
import FieldMessage from '@/components/helper/FieldMessage';

export type MaskType = 'currency' | 'weight' | 'decimal' | 'number';

export interface NumberInputProps {
  // Basic Props
  label?: string;
  bottomLabel?: string;
  name: string;
  value?: number | string;
  placeholder?: string;

  // Styling Props
  className?: {
    wrapper?: string;
    label?: string;
    inputWrapper?: string;
    input?: string;
  };

  // State Props
  isError?: boolean;
  isValid?: boolean;
  errorMessage?: string;
  disabled?: boolean;
  readOnly?: boolean;
  required?: boolean;
  isLoading?: boolean;

  // Adornment Props
  startAdornment?: ReactNode;
  endAdornment?: ReactNode;

  // Event Handlers
  onChange?: ChangeEventHandler<HTMLInputElement>;
  onBlur?: FocusEventHandler<HTMLInputElement>;
  onFocus?: FocusEventHandler<HTMLInputElement>;

  // Masking Options
  maskType?: MaskType;
  decimals?: number;
  thousandSeparator?: string;
  decimalSeparator?: string;
  currencyPrefix?: string;
  weightUnit?: string;

  // Validation Props
  min?: number;
  max?: number;
  allowNegative?: boolean;

  // Stepper Options
  showSteppers?: boolean;
  step?: number;
}

// UTILITY FUNCTIONS
/**
 * Core number formatting function
 * Formats number with thousand separator and decimal separator
 */
const formatNumber = (
  value: number | string,
  decimals: number = 0,
  thousandSeparator: string = '.',
  decimalSeparator: string = ','
): string => {
  if (value === '' || value === null || value === undefined) return '';

  const numValue = typeof value === 'string' ? parseFloat(value) : value;
  if (isNaN(numValue)) return '';

  const parts = numValue.toFixed(decimals).split('.');
  const integerPart = parts[0].replace(
    /\B(?=(\d{3})+(?!\d))/g,
    thousandSeparator
  );
  const decimalPart = parts[1];

  return decimals > 0 && decimalPart
    ? `${integerPart}${decimalSeparator}${decimalPart}`
    : integerPart;
};

/**
 * Parse formatted string to number
 * Converts formatted input back to raw number for processing
 */
const parseNumber = (
  value: string,
  thousandSeparator: string = '.',
  decimalSeparator: string = ','
): number => {
  if (!value) return 0;

  // Remove thousand separators and replace decimal separator with dot
  const cleaned = value
    .replace(new RegExp(`\\${thousandSeparator}`, 'g'), '')
    .replace(decimalSeparator, '.');

  const parsed = parseFloat(cleaned);
  return isNaN(parsed) ? 0 : parsed;
};

/**
 * Clean and validate numeric input while typing
 * Ensures only valid characters are allowed
 */
const cleanNumericInput = (
  value: string,
  allowDecimal: boolean = false,
  decimalSeparator: string = ','
): string => {
  // Only allow numbers, decimal separator (if allowed), and minus sign at the start
  let cleaned = value.replace(/[^\d,.-]/g, '');

  // Handle decimal separator
  if (allowDecimal) {
    const parts = cleaned.split(decimalSeparator);
    if (parts.length > 2) {
      // Keep only first decimal separator
      cleaned = parts[0] + decimalSeparator + parts.slice(1).join('');
    }
  } else {
    cleaned = cleaned.replace(new RegExp(decimalSeparator, 'g'), '');
  }

  // Handle minus sign (only at start)
  const hasMinusAtStart = cleaned.startsWith('-');
  cleaned = cleaned.replace(/-/g, '');
  if (hasMinusAtStart) cleaned = '-' + cleaned;

  return cleaned;
};

const NumberInput = ({
  label,
  bottomLabel,
  name,
  value,
  placeholder,
  className,
  isError,
  isValid,
  errorMessage,
  startAdornment,
  endAdornment,
  disabled = false,
  required = false,
  onChange,
  onBlur,
  onFocus,
  readOnly = false,
  isLoading = false,
  maskType = 'number',
  decimals = 0,
  thousandSeparator = '.',
  decimalSeparator = ',',
  currencyPrefix = 'Rp ',
  weightUnit = 'kg',
  min,
  max,
  allowNegative = false,
  showSteppers = false,
  step = 1,
}: NumberInputProps) => {
  const [displayValue, setDisplayValue] = useState<string>('');

  // CONFIG & HELPERS
  const allowDecimal =
    maskType === 'decimal' || maskType === 'weight' || decimals > 0;

  const getInputPrefix = (): string => {
    switch (maskType) {
      case 'currency':
        return currencyPrefix;
      default:
        return '';
    }
  };

  const getInputSuffix = (): string => {
    switch (maskType) {
      case 'weight':
        return weightUnit;
      default:
        return '';
    }
  };

  const getFormattedValue = (rawValue: number | string): string => {
    if (rawValue === '' || rawValue === null || rawValue === undefined)
      return '';

    switch (maskType) {
      case 'currency':
      case 'weight':
      case 'decimal':
      case 'number':
        return formatNumber(
          rawValue,
          decimals,
          thousandSeparator,
          decimalSeparator
        );
      default:
        return String(rawValue);
    }
  };

  // EFFECTS
  useEffect(() => {
    setDisplayValue(getFormattedValue(value || ''));
  }, [value]);

  // EVENT HANDLERS
  const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    const inputValue = e.target.value;

    // Clean input
    const cleaned = cleanNumericInput(
      inputValue,
      allowDecimal,
      decimalSeparator
    );

    // Parse to number
    let numericValue = parseNumber(
      cleaned,
      thousandSeparator,
      decimalSeparator
    );

    // Apply validation
    if (!allowNegative && numericValue < 0) {
      numericValue = 0;
    }
    if (min !== undefined && numericValue < min) {
      numericValue = min;
    }
    if (max !== undefined && numericValue > max) {
      numericValue = max;
    }

    // Update display value
    const formattedForDisplay = formatNumber(
      numericValue,
      decimals,
      thousandSeparator,
      decimalSeparator
    );

    setDisplayValue(formattedForDisplay);

    // Call onChange with modified event
    if (onChange) {
      const syntheticEvent = {
        ...e,
        target: {
          ...e.target,
          name,
          value: numericValue.toString(),
        },
      } as ChangeEvent<HTMLInputElement>;

      onChange(syntheticEvent);
    }
  };

  const handleIncrement = () => {
    if (disabled || readOnly) return;

    const currentValue = parseNumber(
      displayValue,
      thousandSeparator,
      decimalSeparator
    );
    let newValue = currentValue + step;

    // Apply max validation
    if (max !== undefined && newValue > max) {
      newValue = max;
    }

    // Update display
    const formattedForDisplay = formatNumber(
      newValue,
      decimals,
      thousandSeparator,
      decimalSeparator
    );
    setDisplayValue(formattedForDisplay);

    // Call onChange with synthetic event
    if (onChange) {
      const syntheticEvent = {
        target: {
          name,
          value: newValue.toString(),
        },
      } as ChangeEvent<HTMLInputElement>;

      onChange(syntheticEvent);
    }
  };

  const handleDecrement = () => {
    if (disabled || readOnly) return;

    const currentValue = parseNumber(
      displayValue,
      thousandSeparator,
      decimalSeparator
    );
    let newValue = currentValue - step;

    // Apply min validation (prevent negative if not allowed)
    if (!allowNegative && newValue < 0) {
      newValue = 0;
    }
    if (min !== undefined && newValue < min) {
      newValue = min;
    }

    // Update display
    const formattedForDisplay = formatNumber(
      newValue,
      decimals,
      thousandSeparator,
      decimalSeparator
    );
    setDisplayValue(formattedForDisplay);

    // Call onChange with synthetic event
    if (onChange) {
      const syntheticEvent = {
        target: {
          name,
          value: newValue.toString(),
        },
      } as ChangeEvent<HTMLInputElement>;

      onChange(syntheticEvent);
    }
  };

  // RENDER CALCULATIONS
  const showErrorMessage = Boolean(isError && errorMessage);
  const feedbackMessage = showErrorMessage ? errorMessage : bottomLabel;
  const inputPrefix = getInputPrefix();
  const inputSuffix = getInputSuffix();

  return (
    <div
      className={cn(
        'w-full flex flex-col gap-2 text-start',
        className?.wrapper
      )}
    >
      {label && (
        <label
          htmlFor={name}
          className={cn(
            'w-full text-sm font-normal leading-5',
            {
              'text-error': isError,
            },
            className?.label
          )}
        >
          {label}
          {required && (
            <>
              {' '}
              <span className='tooltip tooltip-error' data-tip='required'>
                <span className='text-error'> *</span>
              </span>
            </>
          )}
        </label>
      )}

      {/* Input Container */}
      <div className='relative flex'>
        {/* Prefix Block */}
        {inputPrefix && (
          <div
            className={cn(
              'inline-flex items-center px-4 py-2 border border-r-0 rounded-l-md transition-all duration-200',
              {
                'bg-gray-100 border-gray-300': !disabled,
                'bg-gray-50 border-gray-200': disabled,
              }
            )}
          >
            <span
              className={cn(
                'text-sm font-medium select-none whitespace-nowrap',
                {
                  'text-gray-600': !disabled,
                  'text-gray-400': disabled,
                }
              )}
            >
              {inputPrefix}
            </span>
          </div>
        )}

        {/* Input Wrapper */}
        <div
          className={cn(
            'input h-12 text-base font-normal leading-6 flex-1 rounded-lg! outline-none! transition-all duration-200 flex items-center bg-white',
            {
              'border-error': isError,
              'border-success!': isValid,
              'rounded-l-none!': inputPrefix,
              'rounded-r-none!': inputSuffix,
              'input-disabled': disabled,
              'cursor-not-allowed': disabled,
              'bg-gray-50': disabled,
            },
            className?.inputWrapper
          )}
        >
          {/* Stepper Buttons */}
          {showSteppers && (
            <button
              type='button'
              onClick={handleDecrement}
              disabled={disabled || readOnly}
              className={cn(
                'btn btn-ghost btn-sm h-8 w-8 min-h-0 p-0 rounded-md',
                {
                  'opacity-50 cursor-not-allowed': disabled || readOnly,
                }
              )}
              tabIndex={-1}
            >
              <Icon icon='ic:round-minus' width={20} height={20} />
            </button>
          )}

          {/* Start Adornment */}
          {startAdornment && startAdornment}

          {/* Main Input */}
          <input
            type='text'
            id={name}
            name={name}
            placeholder={placeholder}
            value={displayValue}
            onChange={handleInputChange}
            onFocus={onFocus}
            onBlur={onBlur}
            disabled={disabled}
            className={cn(
              'grow bg-transparent outline-none',
              {
                'cursor-not-allowed': disabled,
                'text-gray-500': disabled,
              },
              className?.input
            )}
            readOnly={readOnly}
            inputMode='decimal'
          />

          {/* End Adornment & Loading */}
          {(isLoading || endAdornment) && (
            <div className='flex flex-row gap-2'>
              {isLoading && <span className='loading loading-spinner' />}
              {endAdornment && endAdornment}
            </div>
          )}

          {/* Increment Button */}
          {showSteppers && (
            <button
              type='button'
              onClick={handleIncrement}
              disabled={disabled || readOnly}
              className={cn(
                'btn btn-ghost btn-sm h-8 w-8 min-h-0 p-0 rounded-md',
                {
                  'opacity-50 cursor-not-allowed': disabled || readOnly,
                }
              )}
              tabIndex={-1}
            >
              <Icon icon='ic:round-plus' width={20} height={20} />
            </button>
          )}
        </div>

        {/* Suffix Block */}
        {inputSuffix && (
          <div
            className={cn(
              'inline-flex items-center px-4 py-2 border border-l-0 rounded-r-md transition-all duration-200',
              {
                'bg-gray-100 border-gray-300': !disabled,
                'bg-gray-50 border-gray-200': disabled,
              }
            )}
          >
            <span
              className={cn(
                'text-sm font-medium select-none whitespace-nowrap',
                {
                  'text-gray-600': !disabled,
                  'text-gray-400': disabled,
                }
              )}
            >
              {inputSuffix}
            </span>
          </div>
        )}
      </div>

      {/* Field Message */}
      <FieldMessage
        message={feedbackMessage}
        tone={showErrorMessage ? 'error' : 'info'}
        isVisible={showErrorMessage || Boolean(bottomLabel)}
      />
    </div>
  );
};

export default NumberInput;
