import * as React from 'react';
import Alert from '@mui/material/Alert';
import Button from '@mui/material/Button';
import ButtonGroup from '@mui/material/ButtonGroup';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import { FormattedDate, FormattedNumber, FormattedTime } from 'react-intl';
import { __ } from '../../lang';
import {
  isCounter,
  isPassive,
  isStopped,
  isWithdrawalCounter
} from '../../configuration-checker';
import { store } from '../../statsStore';
import Lang from 'lang.js';
import axios from 'axios';
import moment, { Moment } from 'moment';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import trans from '../../translations';

const lang = new Lang({ messages: trans });
lang.setLocale(document.documentElement.lang || 'de');

type ResultInformationModalProps = {
  isOpen: boolean;
  toggle: () => any;
  configurationId: number;
};

type ResultFormValue = {
  dateTime: Moment;
  timeString: string;
  result?: string;
  difference?: string;
  hasCounterDiffField: boolean;
  withdrawalType?: Withdrawal;
};

type InputResultFormProps = {
  configuration: any;
  values: ResultFormValue;
  focusedCalendar: boolean;
  onFocusChangeCalendar: (e: any) => any;
  onChange: (e: React.FormEvent<HTMLInputElement>) => void;
  onDateChange: (m: Moment) => void;
  setSelectedWithdrawalType: (type: Withdrawal) => void;
};

enum Withdrawal {
  Increase,
  Decrease,
  Absolute
}

const initalFormValues: ResultFormValue = {
  dateTime: moment(),
  difference: '',
  hasCounterDiffField: false,
  result: '',
  timeString: moment().format('HH:mm')
};

const blueTextButtonStyle = {
  backgroundColor: 'rgb(0,123,255)',
  color: 'white',
  '&:hover': {
    backgroundColor: 'rgb(0,105,217)',
  },
  padding: '6px 12px 6px 12px'
};

const greyTextButtonStyle = {
  backgroundColor: 'rgb(108,117,125)',
  color: 'white',
  '&:hover': {
    backgroundColor: 'rgb(91,98,104)',
  },
  padding: '6px 12px 6px 12px'
};

const optionTextButtonStyle = (isPrimary) => ({
  backgroundColor: isPrimary === 'primary' ? 'rgb(0,123,255)' : 'rgb(108,117,125)',
  borderColor: isPrimary === 'primary' ? 'rgb(0,123,255)' : 'rgb(108,117,125)',
  color: 'white',
  '&:hover': {
    backgroundColor: isPrimary === 'primary' ? 'rgb(0,105,217)' : 'rgb(91,98,104)',
    borderColor: isPrimary === 'primary' ? 'rgb(0,105,217)' : 'rgb(91,98,104)',
  },
  padding: '6px 12px 6px 12px'
});

const FormattedDateTime = ({ value, ...otherProps }) => (
  <div>
    <FormattedDate value={value} {...otherProps} />
    {'  '}
    <FormattedTime value={value} {...otherProps} />
  </div>
);

const ResultInformationModal: React.FC<ResultInformationModalProps> = ({
  isOpen,
  toggle: originalToggle,
  configurationId
}) => {
  const [isLoading, setIsLoading] = React.useState(false);
  const [isError, setIsError] = React.useState(false);
  const [isFocusedCalendar, setFocusedCalendar] = React.useState(false);
  const [formValues, _setFormValues] = React.useState(initalFormValues);
  const [
    configurationInformation,
    setConfigurationInformation
  ] = React.useState(null);
  React.useEffect(() => {
    const setInitialFormValue = (configuration): ResultFormValue => {
      return configuration === null
        ? {
            ...initalFormValues,
            dateTime: moment(),
            timeString: moment().format('HH:mm')
          }
        : {
            ...initalFormValues,
            dateTime: moment(),
            timeString: moment().format('HH:mm'),
            ...(isWithdrawalCounter(configuration) && {
              withdrawalType: Withdrawal.Decrease
            })
          };
    };
    const fetchResultInformation = async () => {
      try {
        setIsLoading(true);
        const result = await axios.get(`agb/${configurationId}/latest`);
        setConfigurationInformation(result.data);
        _setFormValues(setInitialFormValue(result.data));
      } catch (error) {
        setIsError(true);
      }
    };

    if (isOpen) {
      fetchResultInformation();
    }
    setIsLoading(false);
  }, [configurationId, isOpen]);

  const toggle = () => {
    _setFormValues(initalFormValues);
    setIsError(false);
    originalToggle();
  };

  const setFormValues = (event: React.FormEvent<HTMLInputElement>) => {
    event.persist();

    const target = event.currentTarget;

    if (target.name === 'time') {
      const calculateDateTime = () => {
        const currentDateTimeObject = formValues.dateTime.clone();
        const timeString: string = target.value;
        const splitTimeString = timeString.split(':');

        currentDateTimeObject.set({
          hour: Number(splitTimeString[0]),
          minute: Number(splitTimeString[1])
        });

        return currentDateTimeObject;
      };

      _setFormValues(values => ({
        ...values,
        dateTime: calculateDateTime()
      }));
    }

    if (target.name === 'addCounterDiffField') {
      _setFormValues(values => ({
        ...values,
        hasCounterDiffField: !values.hasCounterDiffField
      }));
    } else {
      _setFormValues(values => {
        return {
          ...values,
          [target.name]: target.value
        };
      });
    }
  };

  const setCalendarDate = (dateTime: Moment) => {
    // Sometimes, the react-date library will call the event handler 
    // 'onDateChange' with a null date when you edit the date by typing it in.
    // This was causing the UI to crash.  To fix that, ignore null dateTimes.
    // -Ann
    if (dateTime === null) {
      return;
    }
    const currentDateTime = formValues.dateTime;
    const currentMinute = currentDateTime.get('minute');
    const currentHour = currentDateTime.get('hour');

    dateTime.set({ hour: currentHour, minute: currentMinute });

    _setFormValues(values => ({
      ...values,
      dateTime
    }));
  };

  const setSelectedWithdrawalType = (selected: Withdrawal) => {
    _setFormValues(values => ({
      ...values,
      withdrawalType: selected
    }));
  };

  const ErrorBody = () => {
    return isError && <Alert severity='error'>Error: {configurationId}</Alert>;
  };

  const LoadingBody = () => {
    return (
      isLoading && (
        <div className='spinner-border text-primary mx-auto' role='status'>
          <span className={'sr-only'}>Loading...</span>
        </div>
      )
    );
  };

  const DataBody = () => {
    if (!configurationInformation) {
      return null;
    }
    const results: any[] = configurationInformation.ergs;
    const value = results.length > 0 ? results[0].erg_value : false;
    const date = results.length > 0 ? results[0].erg_datum : false;
    const minimum = configurationInformation.agb_min2;
    const maximum = configurationInformation.agb_max2;
    return (
      <ResultInformation
        date={date}
        isPassive={isPassive(configurationInformation)}
        isStopped={isStopped(configurationInformation)}
        maximumFractionDigits={configurationInformation.precision}
        nextTaskTime={configurationInformation.nextTaskTime}
        value={value}
        minimum={minimum}
        maximum={maximum}
        configuration={configurationInformation}
      />
    );
  };

  const FormBody = () => {
    return (
      configurationInformation && (
        <InputResultForm
          configuration={configurationInformation}
          values={formValues}
          focusedCalendar={isFocusedCalendar}
          onFocusChangeCalendar={({ focused }) => setFocusedCalendar(focused)}
          onChange={setFormValues}
          onDateChange={setCalendarDate}
          setSelectedWithdrawalType={setSelectedWithdrawalType}
        />
      )
    );
  };

  const submitResult = async event => {
    event.preventDefault();

    const getResultToSubmit = () => {
      let result = formValues.result;
      let difference = formValues.difference;
      const lang = document.documentElement.lang;

      if (lang === 'de') {
        result = formValues.result;
        difference = formValues.difference;
      }

      const baseValue = {
        agbId: configurationInformation.agb_id,
        time: formValues.dateTime.format(),
        ...(Number(result) && { value: Number(result) }),
        ...(!isNaN(difference as any) && { difference: Number(difference) })
      };

      if (!isWithdrawalCounter(configurationInformation)) {
        return baseValue;
      }

      if (formValues.withdrawalType === Withdrawal.Decrease) {
        return { ...baseValue, difference: baseValue.difference * -1 };
      }

      if (formValues.withdrawalType === Withdrawal.Absolute) {
        const { difference, ...rest } = baseValue;
        return { ...rest, value: difference };
      }

      return baseValue;
    };

    try {
      await axios.post('erg/new/measurement', getResultToSubmit());
      toggle();
      if (isCounter(configurationInformation)) {
        store.dispatch({ type: 'GET_COUNTER' });
      } else {
        store.dispatch({ type: 'GET_MEASUREMENT' });
      }
    } catch (error) {
      setIsError(true);
    }
  };

  return (
    <Dialog open={isOpen} onClose={toggle} sx={{ "& .MuiDialog-container": { alignItems: "flex-start" } }}>
      {configurationInformation && (
        <DialogTitle>{configurationInformation.agb_name}</DialogTitle>
      )}
      <form onSubmit={submitResult}>
        <DialogContent>
          {LoadingBody()}
          {ErrorBody()}
          {DataBody()}
          {FormBody()}
        </DialogContent>
        <DialogActions>
          <Button type='submit' sx={blueTextButtonStyle}>
            {__('Save')}
          </Button>
          <Button onClick={toggle} sx={greyTextButtonStyle}>
            {__('Cancel')}
          </Button>
        </DialogActions>
      </form>
    </Dialog>
  );
};

type ResultInformationProps = {
  configuration: any;
  date: string;
  isPassive: boolean;
  isStopped: boolean;
  maximumFractionDigits: number;
  nextTaskTime: string;
  value: number;
  minimum: number;
  maximum: number;
};
const ResultInformation: React.FC<ResultInformationProps> = ({
  configuration,
  date,
  isPassive,
  isStopped,
  maximumFractionDigits,
  nextTaskTime,
  value,
  minimum,
  maximum
}) => (
  <>
    {isPassive ? (
      <div className='alert alert-success' role='alert'>
        {lang.get('stats.alwaysRunning')}
      </div>
    ) : isStopped ? (
      <div className='alert alert-danger' role='alert'>
        {lang.get('stats.stopped')}
      </div>
    ) : (
      <div>
        <div className='alert alert-success' role='alert'>
          {lang.get('stats.nextMeasurementTime')}
          <div>{nextTaskTime}</div>
        </div>
      </div>
    )}
    {value && (
      <>
        <div className='font-weight-bold'>{lang.get('stats.lastData')}:</div>
        <FormattedNumber
          maximumFractionDigits={maximumFractionDigits}
          value={Number(value)}
        />
      </>
    )}
    {date && (
      <>
        <div className='font-weight-bold'>{lang.get('stats.lastTime')}:</div>
        <FormattedDateTime value={date} />
      </>
    )}
    {}
    <MinMaxView
      min={minimum}
      max={maximum}
      maximumFractionDigits={maximumFractionDigits}
      configuration={configuration}
    />
  </>
);

type CalendarProps = {
  date: Moment;
  onDateChange: (m: Moment) => void;
};
const Calendar: React.FC<CalendarProps> = ({
  date,
  onDateChange,
}) => (
    <div>
      <LocalizationProvider dateAdapter={AdapterMoment}>
        <DatePicker
            autoFocus={true}
            value={date}
            onChange={onDateChange}
            slotProps={{
              popper: {
                disablePortal: false,
              },
              textField: {
                size: 'small',
                sx: {
                  '& .MuiInputBase-root': {
                    height: 38,
                  },
                  maxWidth: 150,
                },
              },
            }}
        />
      </LocalizationProvider>
    </div>
);

type MinMaxProps = {
  configuration: any;
  min: number | string;
  max: number | string;
  maximumFractionDigits: number | string;
};
const MinMaxView = ({
  min,
  max,
  maximumFractionDigits,
  configuration
}: MinMaxProps) => {
  const numberMin = Number(min);
  const numberMax = Number(max);
  const isMinDisplayed = !isNaN(numberMin) && numberMin !== 0;
  const isMaxDisplayed = !isNaN(numberMax) && numberMax !== 0;

  const isMinMaxNotSet = isMinDisplayed === false && isMaxDisplayed === false;

  if (isMinMaxNotSet || isCounter(configuration)) {
    return null;
  }
  return (
    <div>
      {isMinDisplayed && (
        <>
          <div>
            <span className='font-weight-bold'>Min</span>
          </div>
          <div>
            <FormattedNumber
              maximumFractionDigits={maximumFractionDigits}
              value={numberMin}
            />
          </div>
        </>
      )}
      {isMaxDisplayed && (
        <>
          <div>
            <span className='font-weight-bold'>max</span>
          </div>
          <div>
            <FormattedNumber
              maximumFractionDigits={maximumFractionDigits}
              value={numberMax}
            />
          </div>
        </>
      )}
    </div>
  );
};

const InputResultForm: React.FC<InputResultFormProps> = ({
  configuration,
  focusedCalendar,
  onChange,
  onDateChange,
  onFocusChangeCalendar,
  setSelectedWithdrawalType,
  values
}) => {
  const _isCounter = isCounter(configuration);
  const _isWithdrawal = isWithdrawalCounter(configuration);

  const renderDateTime = () => {
    const time = values.dateTime.format('HH:mm');
    return (
      <div className='form-row'>
        <div className='form-group col-sm'>
          <label>{__('Date')}</label>
          <Calendar
            onDateChange={onDateChange}
            date={values.dateTime}
          />
        </div>
        <div className='form-group col-sm'>
          <label>{__('Time')}</label>
          <input
            className='form-control'
            name='time'
            type='time'
            value={time}
            onChange={onChange}
          />
        </div>
      </div>
    );
  };

  const renderResultInput = () => (
    <div className='form-row'>
      <div className='form-group col'>
        <label>{__('Value')}</label>
        <input
          name={_isWithdrawal ? 'difference' : 'result'}
          type='number'
          step='any'
          className='form-control'
          value={_isWithdrawal ? values.difference || '' : values.result || ''}
          onChange={onChange}
        />
      </div>
    </div>
  );

  const renderRequiredDateFields = () => (
    <>
      {renderDateTime()}
      {renderResultInput()}
    </>
  );

  const InputCounterDifference = () => {
    if (!_isCounter || !values.hasCounterDiffField) {
      return null;
    }

    return (
      <div className='form-group'>
        <label htmlFor='difference'>{__('New counter value')}</label>
        <input
          id='difference'
          name='difference'
          className='form-control'
          type='number'
          step='any'
          value={values.difference}
          onChange={onChange}
        />
      </div>
    );
  };

  const renderMeterReplacementCounterInputToggle = () => {
    if (!_isCounter || isWithdrawalCounter(configuration)) {
      return null;
    }

    return (
      <div className='form-row'>
        <div className='form-group'>
          <div className='form-check'>
            <input
              id='addCounterDiffField'
              className='form-check-input'
              name='addCounterDiffField'
              type='checkbox'
              checked={values.hasCounterDiffField}
              onChange={onChange}
            />
            <label className='form-check-label' htmlFor='addCounterDiffField'>
              {__('Enter new counter')}
            </label>
          </div>
        </div>
      </div>
    );
  };

  const renderCounterWithdrawalAddOrSubtractSwitch = () => {
    if (!_isCounter || !isWithdrawalCounter(configuration)) {
      return null;
    }

    const withdrawalType = values.withdrawalType || Withdrawal.Increase;
    const increaseButtonColor =
      withdrawalType === Withdrawal.Increase ? 'primary' : 'secondary';
    const decreateButtonColor =
      withdrawalType === Withdrawal.Decrease ? 'primary' : 'secondary';
    const absoluteButtonColor =
      withdrawalType === Withdrawal.Absolute ? 'primary' : 'secondary';

    return (
      <div className='form-row'>
        <div className='form-group col'>
          <label>Withdrawal</label>
          <ButtonGroup className='w-100' fullWidth>
            <Button
              type='button'
              sx={optionTextButtonStyle(decreateButtonColor)}
              onClick={() => setSelectedWithdrawalType(Withdrawal.Decrease)}
            >
              <span>{__('Increase')}</span>
            </Button>
            <Button
              type='button'
              sx={optionTextButtonStyle(increaseButtonColor)}
              onClick={() => setSelectedWithdrawalType(Withdrawal.Increase)}
            >
              <span>{__('Decrease')}</span>
            </Button>
            <Button
              type='button'
              sx={optionTextButtonStyle(absoluteButtonColor)}
              onClick={() => setSelectedWithdrawalType(Withdrawal.Absolute)}
            >
              <span>{__('Absolute')}</span>
            </Button>
          </ButtonGroup>
        </div>
      </div>
    );
  };

  return (
    <div className='border-top'>
      {renderCounterWithdrawalAddOrSubtractSwitch()}
      {renderRequiredDateFields()}
      {InputCounterDifference()}
      {renderMeterReplacementCounterInputToggle()}
    </div>
  );
};

export default ResultInformationModal;
