/**
 * @file FieldInputControl.tsx - Reusable form control for folder and document template fields.
 * Supports multiple input types (text, number, datetime-local, boolean (select), and dropdown (select)), which are determined at runtime from the field's FieldInstanceType or DocumentFieldType.
 * Also exports helper functions to format a Date/string for the datetime-local input type and to apply type-aware formatting to raw field values.
 */

import type { ChangeEventHandler, FC, FormEventHandler, Ref } from 'react';
import {
  type FieldInstanceType,
  FieldInstanceTypes,
} from '../../types/fieldInstance.ts';
import {
  type DocumentFieldType,
  DocumentFieldTypes,
} from '../../types/documentField.ts';
import { Form } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';

/**
 * Props for the FieldInputControl component.
 */
interface FieldInputControlProps {
  /** Discriminated union that identifies whether the field belongs to a folder (FieldInstanceType) or a document template (DocumentFieldType */
  type:
    | { folder: true; instanceType: FieldInstanceType }
    | { document: true; instanceType: DocumentFieldType };

  /** Extra configuration keyed to specific field types (e.g., dropdown options for folder Option fields) */
  typeSpecificProps?: { folderFieldOptions?: string[] };

  /** Forwarded ref to the underlying input or select element */
  ref?: Ref<HTMLInputElement | HTMLSelectElement>;

  /** HTML name attribute passed to the input */
  name?: string;

  /** Placeholder text (formatted through {@link getFormattedFieldValue} for dates) */
  placeholder?: string;

  /** Initial value for the input */
  defaultValue?: string | number | readonly string[];

  /** Maximum character length for text inputs */
  maxLength?: number;

  /** Disables the control */
  disabled?: boolean;

  /** Change event handler forwarded to the underlying element */
  onChange?: ChangeEventHandler<
    HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
  >;

  /** Input event handler forwarded to the underlying element */
  onInput?: FormEventHandler<
    HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
  >;

  /** Marks the field as required for form validation */
  required?: boolean;
}

/** Supported HTML input types that FieldInputControl can render. */
type InputType = 'datetime-local' | 'number' | 'boolean' | 'option' | 'text';

/**
 * Returns a date/time string compatible with the HTML input type = 'datetime-local';
 *
 * @param dateOrString Date or string to convert
 */
export function toDatetimeLocalString(dateOrString: Date | string): string {
  const date = new Date(dateOrString);
  const pad = (n: number) => n.toString().padStart(2, '0');

  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}

/**
 * Returns a formatted value based on the type of field input.
 *
 * @param instanceType Folder or document template field instance type
 * @param fieldType Field type (folder or document)
 * @param inputType HTML input type
 * @param value Value to be formatted
 * @returns Formatted value
 */
export function getFormattedFieldValue(
  instanceType: FieldInstanceType | DocumentFieldType,
  fieldType: 'folder' | 'document',
  inputType: InputType,
  value?: string | number | readonly string[]
): string | number | readonly string[] | undefined {
  return inputType === 'datetime-local'
    ? toDatetimeLocalString(value as string)
    : ((fieldType === 'folder' && instanceType === FieldInstanceTypes.Number) ||
          (fieldType === 'document' &&
            instanceType === DocumentFieldTypes.Number)) &&
        !Number.isNaN(value as string)
      ? parseFloat(value as string).toPrecision()
      : value;
}

/**
 * React Bootstrap form control for folder and document template fields.
 *
 * Renders the appropriate input element based on the resolved InputType:
 *  - 'boolean'        -> <Form.Select> with True/False options
 *  - 'option'         -> <Form.Select> populated from folderFieldOptions
 *  - 'datetime-local' -> <Form.Control type="datetime-local"> with formatted value
 *  - 'number'         -> <Form.Control type="number"> with precision formatting
 *  - 'text'           -> <Form.Control type="text"> (default)
 */
export const FieldInputControl: FC<FieldInputControlProps> = ({
  type,
  typeSpecificProps,
  ref,
  name,
  placeholder,
  defaultValue,
  maxLength,
  disabled,
  onChange,
  onInput,
  required,
}) => {
  const { t } = useTranslation(undefined, {
    keyPrefix: 'components.shared.field_input_control',
  });

  // Determine whether this is a folder or document field, then resolve the HTML input type
  const fieldType = 'folder' in type ? 'folder' : 'document';
  let inputType: InputType;
  if (fieldType === 'folder') {
    switch (type.instanceType) {
      case FieldInstanceTypes.Date:
        inputType = 'datetime-local';
        break;
      case FieldInstanceTypes.Number:
        inputType = 'number';
        break;
      case FieldInstanceTypes.Option:
        inputType = 'option';
        break;
      default:
        inputType = 'text';
        break;
    }
  } else {
    switch (type.instanceType) {
      case DocumentFieldTypes.Boolean:
        inputType = 'boolean';
        break;
      case DocumentFieldTypes.Integer:
      case DocumentFieldTypes.Number:
        inputType = 'number';
        break;
      case DocumentFieldTypes.Date:
        inputType = 'datetime-local';
        break;
      default:
        inputType = 'text';
        break;
    }
  }

  if (inputType === 'boolean') {
    // Handle string-based boolean value from API
    if (typeof defaultValue === 'string') {
      defaultValue = ['true', '1'].includes(defaultValue.toLowerCase()) ? 1 : 0;
    }

    // Boolean type uses select element with true/false options
    return (
      <Form.Select
        ref={ref as Ref<HTMLSelectElement>}
        name={name}
        defaultValue={defaultValue}
        disabled={disabled}
        onChange={onChange}
        onInput={onInput}
      >
        <option value={1}>{t('boolean.options.true')}</option>
        <option value={0}>{t('boolean.options.false')}</option>
      </Form.Select>
    );
  } else if (inputType === 'option' && typeSpecificProps?.folderFieldOptions) {
    // Handle case where defaultValue is not in list
    if (
      !typeSpecificProps.folderFieldOptions.includes(defaultValue as string)
    ) {
      defaultValue = '';
    }

    // Option type uses standard select element
    return (
      <Form.Select
        ref={ref as Ref<HTMLSelectElement>}
        name={name}
        defaultValue={defaultValue}
        disabled={disabled || !typeSpecificProps.folderFieldOptions.length}
        onChange={onChange}
        onInput={onInput}
      >
        {typeSpecificProps.folderFieldOptions.length ? (
          <option value={''} disabled>
            {t('option.default')}
          </option>
        ) : (
          <option value={''} disabled>
            {t('option.none')}
          </option>
        )}
        {typeSpecificProps.folderFieldOptions.map((option, index) => (
          <option key={index} value={option}>
            {option}
          </option>
        ))}
      </Form.Select>
    );
  } else {
    // Text, number, and datetime-local types use a standard <Form.Control>
    // Date and number values are run through getFormattedFieldValue for display
    return (
      <Form.Control
        ref={ref as Ref<HTMLInputElement>}
        name={name}
        type={inputType}
        placeholder={
          getFormattedFieldValue(
            type.instanceType,
            fieldType,
            inputType,
            placeholder
          ) as string
        }
        defaultValue={getFormattedFieldValue(
          type.instanceType,
          fieldType,
          inputType,
          defaultValue
        )}
        maxLength={maxLength}
        disabled={disabled}
        onChange={onChange}
        onInput={onInput}
        step={
          inputType === 'datetime-local'
            ? 1
            : (fieldType === 'folder' &&
                  type.instanceType === FieldInstanceTypes.Number) ||
                (fieldType === 'document' &&
                  type.instanceType === DocumentFieldTypes.Number)
              ? 0.01
              : undefined
        }
        min={inputType === 'datetime-local' ? '1970-01-01T00:00:01' : undefined}
        required={required}
      />
    );
  }
};
