/* eslint-disable react/default-props-match-prop-types */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { SingleDatePicker } from '@availity/react-dates';
import { InputGroup } from 'reactstrap';
import classNames from 'classnames';
import moment from 'moment';
import '@availity/react-dates/initialize';
import '../polyfills';

import { inputType, isoDateFormat } from 'availity-reactstrap-validation/lib/AvValidator/utils';
import { AvInput } from 'availity-reactstrap-validation';

import { isOutsideRange, limitPropType } from './utils.js';

class AvDate extends Component {
  static getDerivedStateFromProps({ value }, prevState) {
    if (value !== undefined && value !== prevState.value) {
      return { value };
    }
    return null;
  }

  getDateValue = () => {
    const { value, format } = this.state;
    const date = moment(value, [isoDateFormat, format, 'MMDDYYYY', 'YYYYMMDD'], true);
    if (date.isValid()) return date;
    return null;
  };

  constructor(props, context) {
    super(props);

    this.state = {};

    // eslint-disable-next-line unicorn/prefer-ternary
    if (props.type.toLowerCase() === 'date' && inputType.date) {
      this.state.format = isoDateFormat;
    } else {
      this.state.format = props.validate?.date?.format || 'MM/DD/YYYY';
    }

    this.state.focused = false;

    const { getDefaultValue } = context.FormCtrl;

    if (getDefaultValue(props.name)) {
      this.state.value = getDefaultValue(props.name);
    }
  }

  onFocusChange = ({ focused }) => {
    const { onPickerFocusChange, name } = this.props;
    const touched = this.context.FormCtrl.isTouched(name);
    if (!touched && !focused) {
      this.context.FormCtrl.setTouched(name);
    }

    this.setState({ focused });

    if (onPickerFocusChange) onPickerFocusChange({ focused });
  };

  // For updating when we delete the current input
  onInputChange = async (value) => {
    const { name, onChange } = this.props;
    const date = moment(value, [isoDateFormat, this.state.format, 'MMDDYYYY', 'YYYYMMDD'], true);

    this.context.FormCtrl.getInput(name).getValidatorProps().onChange(value);
    const isoFormatted = date.format(isoDateFormat);

    this.setState({ value }, () => {
      if (date.isValid()) {
        this.setState({
          focused: false,
        });
        this.context.FormCtrl.setTouched(name);
        if (onChange) onChange(isoFormatted);
      }
    });
  };

  onPickerChange = async (value) => {
    if (value === null) return;
    const { format } = this.state;
    const { name, onChange } = this.props;
    if (value === null) return;

    let val = value;
    if (val instanceof Object && val.isValid()) {
      val = val.format(format);
    }

    this.context.FormCtrl.getInput(name).getValidatorProps().onChange(val);

    this.setState({ value: val }, () => {
      if (onChange) onChange(val);
    });
  };

  onClose = ({ date }) => {
    const { format } = this.state;

    const { onBlur } = this.context.FormCtrl.getInput(this.props.name).getValidatorProps();

    onBlur(date && date.format(format));
  };

  valueParser = (value) => {
    if (this.state.format === isoDateFormat) return value;
    const date = moment(value, [this.state.format, 'MMDDYYYY', 'YYYYMMDD'], true);
    if (date.isValid()) return date.format(isoDateFormat);
    return value;
  };

  valueFormatter = (value) => {
    const date = moment(value, [isoDateFormat, this.state.format, 'MMDDYYYY', 'YYYYMMDD'], true);
    if (date.isValid()) return date.format(this.state.format);
    return value;
  };

  render() {
    const { className, datePickerProps, hideIcon, max, min, name, type, validate, ...attributes } = this.props;
    const { focused, format, value } = this.state;
    const { FormCtrl } = this.context;

    const minDate = validate && validate.min ? validate.min.value : min;
    const maxDate = validate && validate.max ? validate.max.value : max;

    const pickerId = `${(this.props.id || name).replaceAll(/[^\da-z]/gi, '')}-btn`;

    const touched = FormCtrl.isTouched(name);
    const hasError = FormCtrl.hasError(name);

    const classes = classNames(
      className,
      touched ? 'is-touched' : 'is-untouched',
      FormCtrl.isDirty(name) ? 'is-dirty' : 'is-pristine',
      FormCtrl.isBad(name) ? 'is-bad-input' : null,
      hasError ? 'av-invalid' : 'av-valid',
      touched && hasError && 'is-invalid',
      !value && 'current-day-highlight'
    );

    const input = (
      <AvInput
        name={name}
        placeholder={format.toLowerCase()}
        {...attributes}
        type="text"
        min={minDate}
        max={maxDate}
        style={{ display: 'none' }}
        value={value || ''}
        valueFormatter={this.valueFormatter}
        valueParser={this.valueParser}
        validate={{ date: true, ...validate }}
      />
    );

    return (
      <>
        {input}
        <InputGroup
          disabled={attributes.disabled}
          className={classes}
          onChange={({ target }) => target.id === pickerId && this.onInputChange(target.value)}
          data-testid={`date-input-group-${name}`}
        >
          <SingleDatePicker
            {...datePickerProps}
            placeholder={format.toLowerCase()}
            id={pickerId}
            disabled={attributes.disabled}
            date={this.getDateValue()}
            onDateChange={this.onPickerChange}
            focused={focused}
            onFocusChange={this.onFocusChange}
            numberOfMonths={1}
            isOutsideRange={isOutsideRange(minDate, maxDate, format)}
            onClose={this.onClose}
            autoComplete="date"
          />
        </InputGroup>
      </>
    );
  }
}

AvDate.propTypes = {
  ...AvInput.propTypes,
  /**  Minimum date to allow the datepicker and input to take. You can either pass the min here or in the validate object if you want a custom error message with it. */
  min: limitPropType,
  /** Max date to allow the datepicker and input to take. You can either pass the max here or in the validate object if you want a custom error message with it. */
  max: limitPropType,
  /** Function to be run when focus on the input changes. */
  onPickerFocusChange: PropTypes.func,
  /** Props to be spread onto the datepicker component from [react-dates](https://github.com/react-dates/react-dates#singledatepicker). */
  datePickerProps: PropTypes.object,
};

AvDate.contextTypes = {
  FormCtrl: PropTypes.object.isRequired,
};

AvDate.defaultProps = {
  type: 'text',
  datePickerProps: {},
};

export default AvDate;
