import { useEffect, useState } from 'react';

import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
import WarningIcon from '@mui/icons-material/Warning';
import {
  Box,
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Grid,
  InputAdornment,
  MenuItem,
  Typography
} from '@mui/material';

import { dialogStyle } from 'CustomTheme';
import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils';
import { ValidationError, validate } from 'validators';

import { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types';
import type { AnalogSensor } from './types';

interface DashboardSensorsAnalogDialogProps {
  open: boolean;
  onClose: () => void;
  onSave: (as: AnalogSensor) => void;
  creating: boolean;
  selectedItem: AnalogSensor;
  analogGPIOList: number[];
  disabledTypeList: number[];
  validator: Schema;
}

const SensorsAnalogDialog = ({
  open,
  onClose,
  onSave,
  creating,
  selectedItem,
  analogGPIOList,
  disabledTypeList,
  validator
}: DashboardSensorsAnalogDialogProps) => {
  const { LL } = useI18nContext();
  const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
  const [editItem, setEditItem] = useState<AnalogSensor>(selectedItem);

  const updateFormValue = updateValue((updater) =>
    setEditItem(
      (prev) =>
        updater(
          prev as unknown as Record<string, unknown>
        ) as unknown as AnalogSensor
    )
  );

  const isCounterOrRate =
    editItem.t === AnalogType.COUNTER ||
    editItem.t === AnalogType.RATE ||
    (editItem.t >= AnalogType.CNT_0 && editItem.t <= AnalogType.CNT_2);
  const isCounter =
    editItem.t === AnalogType.COUNTER ||
    (editItem.t >= AnalogType.CNT_0 && editItem.t <= AnalogType.CNT_2);
  const isFreqType =
    editItem.t >= AnalogType.FREQ_0 && editItem.t <= AnalogType.FREQ_2;
  const isPWM =
    editItem.t === AnalogType.PWM_0 ||
    editItem.t === AnalogType.PWM_1 ||
    editItem.t === AnalogType.PWM_2;
  const isDACOutGPIO =
    editItem.t === AnalogType.DIGITAL_OUT &&
    (editItem.g === 25 || editItem.g === 26);
  const isDigitalOutGPIO =
    editItem.t === AnalogType.DIGITAL_OUT && editItem.g !== 25 && editItem.g !== 26;

  const analogTypeMenuItems = AnalogTypeNames.map((val, i) => ({
    name: val,
    value: i + 1
  }))
    .sort((a, b) => a.name.localeCompare(b.name))
    .map(({ name, value }) => (
      <MenuItem
        key={name}
        value={value}
        disabled={disabledTypeList?.includes(value)}
      >
        {name}
      </MenuItem>
    ));

  const uomMenuItems = DeviceValueUOM_s.map((val, i) => (
    <MenuItem key={val} value={i}>
      {val}
    </MenuItem>
  ));

  const analogGPIOMenuItems = () =>
    // add selectedItem.g to the list
    [
      ...(analogGPIOList?.includes(selectedItem.g) || selectedItem.g === undefined
        ? analogGPIOList
        : [selectedItem.g, ...analogGPIOList])
    ]
      .filter((gpio, idx, arr) => arr.indexOf(gpio) === idx)
      .sort((a, b) => a - b)
      .map((gpio: number) => {
        return (
          <MenuItem key={gpio} value={gpio}>
            {gpio}
          </MenuItem>
        );
      });

  // Reset form when dialog opens or selectedItem changes
  useEffect(() => {
    if (open) {
      setFieldErrors(undefined);
      setEditItem(selectedItem);
    }
  }, [open, selectedItem]);

  const handleClose = (
    _event: React.SyntheticEvent,
    reason: 'backdropClick' | 'escapeKeyDown'
  ) => {
    if (reason !== 'backdropClick') {
      onClose();
    }
  };

  const save = async () => {
    try {
      setFieldErrors(undefined);
      await validate(validator, editItem);
      onSave(editItem);
    } catch (error) {
      setFieldErrors((error as ValidationError).fieldErrors);
    }
  };

  const remove = () => {
    onSave({ ...editItem, d: true });
  };

  const dialogTitle = `${creating ? LL.ADD(1) + ' ' + LL.NEW(0) : LL.EDIT()} ${LL.ANALOG_SENSOR(0)}`;

  return (
    <Dialog sx={dialogStyle} open={open} onClose={handleClose}>
      <DialogTitle>{dialogTitle}</DialogTitle>
      <DialogContent dividers>
        <Grid container spacing={2}>
          <ValidatedTextField
            name="g"
            label="GPIO"
            value={editItem.g}
            sx={{ width: '9ch' }}
            disabled={editItem.s || !creating}
            select
            onChange={updateFormValue}
          >
            {analogGPIOMenuItems()}
          </ValidatedTextField>
          <Grid>
            <ValidatedTextField
              name="n"
              label={LL.NAME(0)}
              value={editItem.n}
              fullWidth
              onChange={updateFormValue}
            />
          </Grid>
          <Grid>
            <ValidatedTextField
              name="t"
              label={LL.TYPE(0)}
              value={editItem.t}
              fullWidth
              select
              onChange={updateFormValue}
              disabled={editItem.s}
            >
              {analogTypeMenuItems}
            </ValidatedTextField>
          </Grid>
          {(isCounterOrRate ||
            isFreqType ||
            editItem.t === AnalogType.ADC ||
            editItem.t === AnalogType.TIMER) && (
            <Grid>
              <ValidatedTextField
                name="u"
                label={LL.UNIT()}
                value={editItem.u}
                sx={{ width: '15ch' }}
                select
                onChange={updateFormValue}
                disabled={editItem.s}
              >
                {uomMenuItems}
              </ValidatedTextField>
            </Grid>
          )}
          {editItem.t === AnalogType.ADC && (
            <Grid>
              <ValidatedTextField
                name="o"
                label={LL.OFFSET()}
                value={numberValue(editItem.o)}
                type="number"
                sx={{ width: '11ch' }}
                onChange={updateFormValue}
                disabled={editItem.s}
                slotProps={{
                  input: {
                    startAdornment: (
                      <InputAdornment position="start">mV</InputAdornment>
                    )
                  },
                  htmlInput: { min: '0', max: '3300', step: '1' }
                }}
              />
            </Grid>
          )}
          {editItem.t === AnalogType.NTC && (
            <Grid>
              <ValidatedTextField
                name="o"
                label={LL.OFFSET()}
                value={numberValue(editItem.o)}
                sx={{ width: '11ch' }}
                type="number"
                onChange={updateFormValue}
                disabled={editItem.s}
                slotProps={{
                  input: {
                    startAdornment: (
                      <InputAdornment position="start">°C</InputAdornment>
                    )
                  },
                  htmlInput: { min: '-20', max: '20', step: '0.1' }
                }}
              />
            </Grid>
          )}
          {isCounter && (
            <Grid>
              <ValidatedTextField
                name="o"
                label={LL.STARTVALUE()}
                value={numberValue(editItem.o)}
                type="number"
                sx={{ width: '11ch' }}
                onChange={updateFormValue}
                disabled={editItem.s}
                slotProps={{
                  htmlInput: { step: '0.001' }
                }}
              />
            </Grid>
          )}
          {editItem.t === AnalogType.RGB && (
            <Grid>
              <ValidatedTextField
                name="o"
                label={'RGB ' + LL.VALUE(0)}
                value={numberValue(editItem.o)}
                type="number"
                sx={{ width: '11ch' }}
                onChange={updateFormValue}
                disabled={editItem.s}
              />
            </Grid>
          )}
          {(isCounterOrRate ||
            isFreqType ||
            editItem.t === AnalogType.ADC ||
            editItem.t === AnalogType.TIMER) && (
            <Grid>
              <ValidatedTextField
                name="f"
                label={LL.FACTOR()}
                value={numberValue(editItem.f)}
                sx={{ width: '14ch' }}
                type="number"
                onChange={updateFormValue}
                disabled={editItem.s}
                slotProps={{
                  htmlInput: { step: '0.001' }
                }}
              />
            </Grid>
          )}
          {isDACOutGPIO && (
            <Grid>
              <ValidatedTextField
                name="o"
                label={LL.VALUE(0)}
                value={numberValue(editItem.o)}
                sx={{ width: '11ch' }}
                type="number"
                onChange={updateFormValue}
                disabled={editItem.s}
                slotProps={{
                  htmlInput: { min: '0', max: '255', step: '1' }
                }}
              />
            </Grid>
          )}
          {isDigitalOutGPIO && (
            <>
              <Grid>
                <ValidatedTextField
                  name="o"
                  label={LL.VALUE(0)}
                  value={numberValue(editItem.o)}
                  select
                  onChange={updateFormValue}
                  disabled={editItem.s}
                >
                  <MenuItem value={0}>{LL.OFF()}</MenuItem>
                  <MenuItem value={1}>{LL.ON()}</MenuItem>
                </ValidatedTextField>
              </Grid>
              <Grid>
                <ValidatedTextField
                  name="f"
                  label={LL.POLARITY()}
                  value={editItem.f}
                  sx={{ width: '15ch' }}
                  select
                  onChange={updateFormValue}
                  disabled={editItem.s}
                >
                  <MenuItem value={1}>{LL.ACTIVEHIGH()}</MenuItem>
                  <MenuItem value={-1}>{LL.ACTIVELOW()}</MenuItem>
                </ValidatedTextField>
              </Grid>
              <Grid>
                <ValidatedTextField
                  name="u"
                  label={LL.STARTVALUE()}
                  sx={{ width: '15ch' }}
                  value={editItem.u}
                  select
                  onChange={updateFormValue}
                  disabled={editItem.s}
                >
                  <MenuItem value={0}>{LL.UNCHANGED()}</MenuItem>
                  <MenuItem value={1}>
                    {LL.ALWAYS()}&nbsp;{LL.OFF()}
                  </MenuItem>
                  <MenuItem value={2}>
                    {LL.ALWAYS()}&nbsp;{LL.ON()}
                  </MenuItem>
                </ValidatedTextField>
              </Grid>
            </>
          )}
          {isPWM && (
            <>
              <Grid>
                <ValidatedTextField
                  name="f"
                  label={LL.FREQ()}
                  value={numberValue(editItem.f)}
                  type="number"
                  sx={{ width: '11ch' }}
                  onChange={updateFormValue}
                  disabled={editItem.s}
                  slotProps={{
                    input: {
                      startAdornment: (
                        <InputAdornment position="start">Hz</InputAdornment>
                      )
                    },
                    htmlInput: { min: '1', max: '5000', step: '1' }
                  }}
                />
              </Grid>
              <Grid>
                <ValidatedTextField
                  name="o"
                  label={LL.DUTY_CYCLE()}
                  value={numberValue(editItem.o)}
                  type="number"
                  sx={{ width: '11ch' }}
                  onChange={updateFormValue}
                  disabled={editItem.s}
                  slotProps={{
                    input: {
                      startAdornment: (
                        <InputAdornment position="start">%</InputAdornment>
                      )
                    },
                    htmlInput: { min: '0', max: '100', step: '0.1' }
                  }}
                />
              </Grid>
            </>
          )}
          {editItem.t === AnalogType.PULSE && (
            <>
              <Grid>
                <ValidatedTextField
                  name="o"
                  label={LL.POLARITY()}
                  value={editItem.o}
                  sx={{ width: '11ch' }}
                  select
                  onChange={updateFormValue}
                  disabled={editItem.s}
                >
                  <MenuItem value={0}>{LL.ACTIVEHIGH()}</MenuItem>
                  <MenuItem value={1}>{LL.ACTIVELOW()}</MenuItem>
                </ValidatedTextField>
              </Grid>
              <Grid>
                <ValidatedTextField
                  name="f"
                  label="Pulse"
                  value={numberValue(editItem.f)}
                  type="number"
                  sx={{ width: '15ch' }}
                  onChange={updateFormValue}
                  disabled={editItem.s}
                  slotProps={{
                    input: {
                      startAdornment: (
                        <InputAdornment position="start">s</InputAdornment>
                      )
                    },
                    htmlInput: { min: '0', max: '10000', step: '0.1' }
                  }}
                />
              </Grid>
            </>
          )}
        </Grid>
        {fieldErrors && Object.keys(fieldErrors).length > 0 && (
          <Box sx={{ mt: 1 }}>
            {Object.values(fieldErrors).map((errArr, idx) =>
              Array.isArray(errArr)
                ? errArr.map((err, j) => (
                    <Typography
                      key={`${idx}-${j}`}
                      color="error"
                      variant="caption"
                      sx={{ display: 'block' }}
                    >
                      {err.message}
                    </Typography>
                  ))
                : null
            )}
          </Box>
        )}
        {editItem.s && (
          <Grid>
            <Typography sx={{ mt: 1 }} color="warning" variant="body2">
              <WarningIcon
                fontSize="small"
                sx={{ mr: 1, verticalAlign: 'middle' }}
                color="warning"
              />
              {LL.SYSTEM(0)} {LL.SENSOR(0)}
            </Typography>
          </Grid>
        )}
      </DialogContent>
      <DialogActions>
        {!creating && (
          <Box sx={{ flexGrow: 1, '& button': { mt: 0 } }}>
            <Button
              startIcon={<RemoveIcon />}
              disabled={editItem.s}
              variant="outlined"
              color="warning"
              onClick={remove}
            >
              {LL.REMOVE()}
            </Button>
          </Box>
        )}
        <Button
          startIcon={<CancelIcon />}
          variant="outlined"
          onClick={onClose}
          color="secondary"
        >
          {LL.CANCEL()}
        </Button>
        <Button
          startIcon={<DoneIcon />}
          variant="outlined"
          onClick={save}
          color="primary"
        >
          {creating ? LL.ADD(0) : LL.UPDATE()}
        </Button>
      </DialogActions>
    </Dialog>
  );
};

export default SensorsAnalogDialog;
