import { FC, useState, useEffect, useCallback } from 'react';

import {
  Button,
  Typography,
  Box,
  MenuItem,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  ToggleButton,
  ToggleButtonGroup,
  Tooltip,
  Grid,
  TextField
} from '@mui/material';

import { Table } from '@table-library/react-table-library/table';
import { useTheme } from '@table-library/react-table-library/theme';
import { useSort, SortToggleType } from '@table-library/react-table-library/sort';
import { Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';

import { useSnackbar } from 'notistack';

import SaveIcon from '@mui/icons-material/Save';
import CancelIcon from '@mui/icons-material/Cancel';
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
import StarIcon from '@mui/icons-material/Star';
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore';
import KeyboardArrowUpOutlinedIcon from '@mui/icons-material/KeyboardArrowUpOutlined';
import KeyboardArrowDownOutlinedIcon from '@mui/icons-material/KeyboardArrowDownOutlined';
import UnfoldMoreOutlinedIcon from '@mui/icons-material/UnfoldMoreOutlined';
import SearchIcon from '@mui/icons-material/Search';
import FilterListIcon from '@mui/icons-material/FilterList';

import { ButtonRow, FormLoader, ValidatedTextField, SectionContent } from '../components';

import * as EMSESP from './api';

import { extractErrorMessage } from '../utils';

import { DeviceShort, Devices, DeviceEntity, DeviceEntityMask } from './types';

const SettingsCustomization: FC = () => {
  const { enqueueSnackbar } = useSnackbar();

  const [deviceEntities, setDeviceEntities] = useState<DeviceEntity[]>([{ id: '', v: 0, n: '', m: 0, w: false }]);
  const [devices, setDevices] = useState<Devices>();
  const [errorMessage, setErrorMessage] = useState<string>();
  const [selectedDevice, setSelectedDevice] = useState<number>(0);
  const [confirmReset, setConfirmReset] = useState<boolean>(false);
  const [selectedFilters, setSelectedFilters] = useState<number>(0);
  const [search, setSearch] = useState('');

  // eslint-disable-next-line
  const [masks, setMasks] = useState(() => ['']);

  const entities_theme = useTheme({
    BaseRow: `
      font-size: 14px;
      color: white;
      height: 32px;
      min-height: 32px;
    `,
    HeaderRow: `
      text-transform: uppercase;
      background-color: black;
      border-bottom: 1px solid #e0e0e0;
      color: #90CAF9;
      font-weight: 500;
    `,
    Row: `
      background-color: #1e1e1e;
      border-top: 1px solid #565656;
      border-bottom: 1px solid #565656;
      position: relative;
      z-index: 1;
      &:not(:last-of-type) {
        margin-bottom: -1px;
      }
      &:not(:first-of-type) {
        margin-top: -1px;
      }
      &:hover {
        z-index: 2;
        color: white;
        border-top: 1px solid #177ac9;
        border-bottom: 1px solid #177ac9;
      },
      &.tr.tr-body.row-select.row-select-single-selected, &.tr.tr-body.row-select.row-select-selected {
        background-color: #3d4752;
        color: white;
        font-weight: normal;
        z-index: 2;
        border-top: 1px solid #177ac9;
        border-bottom: 1px solid #177ac9;
      }
    `,
    BaseCell: `
      border-top: 1px solid transparent;
      border-right: 1px solid transparent;
      border-bottom: 1px solid transparent;
      &:not(.stiff) > div {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      &:nth-of-type(1) {
        width: 120px;
        min-width: 120px;
        max-width: 120px;
      }
      &:nth-of-type(2) {
        padding-left: 8px;
        flex: 1;
      }
      &:nth-of-type(3) {
        padding-right: 8px;
        text-align: right;
        width: 120px;
        min-width: 120px;
      }
    `,
    HeaderCell: `
      &:nth-of-type(1) {
        padding-left: 24px;
      }
      &:nth-of-type(2) {
        padding-left: 0px;
      }
      &:not(:last-of-type) {
        border-right: 1px solid #565656;
      }
    `
  });

  const getSortIcon = (state: any, sortKey: any) => {
    if (state.sortKey === sortKey && state.reverse) {
      return <KeyboardArrowDownOutlinedIcon />;
    }
    if (state.sortKey === sortKey && !state.reverse) {
      return <KeyboardArrowUpOutlinedIcon />;
    }
    return <UnfoldMoreOutlinedIcon />;
  };

  const entity_sort = useSort(
    { nodes: deviceEntities },
    {},
    {
      sortIcon: {
        iconDefault: <UnfoldMoreOutlinedIcon />,
        iconUp: <KeyboardArrowUpOutlinedIcon />,
        iconDown: <KeyboardArrowDownOutlinedIcon />
      },
      sortToggleType: SortToggleType.AlternateWithReset,
      sortFns: {
        NAME: (array) => array.sort((a, b) => a.id.localeCompare(b.id))
      }
    }
  );

  const fetchDevices = useCallback(async () => {
    try {
      setDevices((await EMSESP.readDevices()).data);
    } catch (error: unknown) {
      setErrorMessage(extractErrorMessage(error, 'Failed to fetch device list'));
    }
  }, []);

  const setInitialMask = (data: DeviceEntity[]) => {
    setDeviceEntities(data.map((de) => ({ ...de, om: de.m })));
  };

  const fetchDeviceEntities = async (unique_id: number) => {
    try {
      const data = (await EMSESP.readDeviceEntities({ id: unique_id })).data;
      setInitialMask(data);
    } catch (error: unknown) {
      setErrorMessage(extractErrorMessage(error, 'Problem fetching device entities'));
    }
  };

  useEffect(() => {
    fetchDevices();
  }, [fetchDevices]);

  function formatValue(value: any) {
    if (typeof value === 'number') {
      return new Intl.NumberFormat().format(value);
    } else if (value === undefined) {
      return '';
    } else if (typeof value === 'boolean') {
      return value ? 'true' : 'false';
    }
    return value;
  }

  function formatName(de: DeviceEntity) {
    if (de.n === undefined || de.n === de.id) {
      return de.id;
    }
    return de.n + ' (' + de.id + ')';
  }

  function isCmd(de: DeviceEntity) {
    return de.n === undefined;
  }

  const getMaskNumber = (newMask: string[]) => {
    var new_mask = 0;
    for (let entry of newMask) {
      new_mask |= Number(entry);
    }
    return new_mask;
  };

  const getMaskString = (m: number) => {
    var new_masks = [];
    if ((m & 1) === 1) {
      new_masks.push('1');
    }
    if ((m & 2) === 2) {
      new_masks.push('2');
    }
    if ((m & 4) === 4) {
      new_masks.push('4');
    }
    if ((m & 8) === 8) {
      new_masks.push('8');
    }
    return new_masks;
  };

  const maskDisabled = (set: boolean) => {
    setDeviceEntities(
      deviceEntities.map(function (de) {
        if ((de.m & selectedFilters || !selectedFilters) && de.id.toLowerCase().includes(search.toLowerCase())) {
          return {
            ...de,
            m: set
              ? de.m | (DeviceEntityMask.DV_API_MQTT_EXCLUDE | DeviceEntityMask.DV_WEB_EXCLUDE)
              : de.m & ~(DeviceEntityMask.DV_API_MQTT_EXCLUDE | DeviceEntityMask.DV_WEB_EXCLUDE)
          };
        } else {
          return de;
        }
      })
    );
  };

  function compareDevices(a: DeviceShort, b: DeviceShort) {
    if (a.s < b.s) {
      return -1;
    }
    if (a.s > b.s) {
      return 1;
    }
    return 0;
  }

  const changeSelectedDevice = (event: React.ChangeEvent<HTMLInputElement>) => {
    const selected_device = parseInt(event.target.value, 10);
    setSelectedDevice(selected_device);
    fetchDeviceEntities(selected_device);
  };

  const resetCustomization = async () => {
    try {
      await EMSESP.resetCustomizations();
      enqueueSnackbar('All customizations have been removed. Restarting...', { variant: 'info' });
    } catch (error: unknown) {
      enqueueSnackbar(extractErrorMessage(error, 'Problem resetting customizations'), { variant: 'error' });
    } finally {
      setConfirmReset(false);
    }
  };

  const saveCustomization = async () => {
    if (deviceEntities && selectedDevice) {
      const masked_entities = deviceEntities
        .filter((de) => de.m !== de.om)
        .map((new_de) => new_de.m.toString(16).padStart(2, '0') + new_de.id);

      if (masked_entities.length > 60) {
        enqueueSnackbar('Selected entities exceeded limit of 60. Please Save in batches', { variant: 'warning' });
        return;
      }

      try {
        const response = await EMSESP.writeMaskedEntities({
          id: selectedDevice,
          entity_ids: masked_entities
        });
        if (response.status === 200) {
          enqueueSnackbar('Customization saved', { variant: 'success' });
        } else {
          enqueueSnackbar('Customization save failed', { variant: 'error' });
        }
      } catch (error: unknown) {
        enqueueSnackbar(extractErrorMessage(error, 'Problem sending entity list'), { variant: 'error' });
      }
      setInitialMask(deviceEntities);
    }
  };

  const renderDeviceList = () => {
    if (!devices) {
      return <FormLoader errorMessage={errorMessage} />;
    }

    return (
      <>
        <Box mb={2} color="warning.main">
          <Typography variant="body2">Select a device and customize each of its entities using the options:</Typography>
          <Typography variant="body2">
            <StarIcon sx={{ fontSize: 16, verticalAlign: 'middle' }} />
            =mark/unmark as a favorite&nbsp;&nbsp;
            <EditOffOutlinedIcon sx={{ fontSize: 16, verticalAlign: 'middle' }} />
            =enable/disable write action&nbsp;&nbsp;
            <CommentsDisabledOutlinedIcon sx={{ fontSize: 16, verticalAlign: 'middle' }} />
            =include/excluded from MQTT and API outputs&nbsp;&nbsp;
            <VisibilityOffOutlinedIcon sx={{ fontSize: 16, verticalAlign: 'middle' }} />
            =show/hide from Web Dashboard
          </Typography>
        </Box>
        <ValidatedTextField
          name="device"
          label="EMS Device"
          variant="outlined"
          fullWidth
          value={selectedDevice}
          onChange={changeSelectedDevice}
          margin="normal"
          select
        >
          <MenuItem disabled key={0} value={0}>
            Select a device...
          </MenuItem>
          {devices.devices.sort(compareDevices).map((device: DeviceShort, index) => (
            <MenuItem key={index} value={device.i}>
              {device.s}
            </MenuItem>
          ))}
        </ValidatedTextField>
      </>
    );
  };

  const renderDeviceData = () => {
    if (devices?.devices.length === 0 || deviceEntities[0].id === '') {
      return;
    }

    const shown_data = deviceEntities.filter(
      (de) => (de.m & selectedFilters || !selectedFilters) && de.id.toLowerCase().includes(search.toLowerCase())
    );

    return (
      <>
        <Grid container mb={1} mt={0} spacing={1} direction="row" justifyContent="flex-start" alignItems="center">
          <Grid item>
            <Typography variant="subtitle2" color="primary">
              #:
            </Typography>
          </Grid>
          <Grid item>
            <Typography variant="subtitle2">
              {shown_data.length}/{deviceEntities.length}
            </Typography>
          </Grid>
          <Grid item>
            <SearchIcon color="primary" sx={{ fontSize: 16, verticalAlign: 'middle' }} />:
          </Grid>
          <Grid item xs={2}>
            <TextField
              size="small"
              variant="outlined"
              onChange={(event) => {
                setSearch(event.target.value);
              }}
            />
          </Grid>
          <Grid item>
            <FilterListIcon color="primary" sx={{ fontSize: 14, verticalAlign: 'middle' }} />:
          </Grid>
          <Grid item>
            <ToggleButtonGroup
              size="small"
              color="secondary"
              value={getMaskString(selectedFilters)}
              onChange={(event, mask) => {
                setSelectedFilters(getMaskNumber(mask));
              }}
            >
              <ToggleButton value="8">
                <Tooltip arrow placement="top" title="filter favorites">
                  <StarIcon sx={{ fontSize: 14 }} />
                </Tooltip>
              </ToggleButton>
              <ToggleButton value="4">
                <Tooltip arrow placement="top" title="filter entities with write action disabled">
                  <EditOffOutlinedIcon sx={{ fontSize: 14 }} />
                </Tooltip>
              </ToggleButton>
              <ToggleButton value="2">
                <Tooltip arrow placement="top" title="filter entities excluded from MQTT and API outputs">
                  <CommentsDisabledOutlinedIcon sx={{ fontSize: 14 }} />
                </Tooltip>
              </ToggleButton>
              <ToggleButton value="1">
                <Tooltip arrow placement="top" title="filter entities hidden from Web Dashboard">
                  <VisibilityOffOutlinedIcon sx={{ fontSize: 14 }} />
                </Tooltip>
              </ToggleButton>
            </ToggleButtonGroup>
          </Grid>

          <Grid item>
            <Tooltip arrow placement="top" title="set selected entities to be both visible and output">
              <Button
                size="small"
                sx={{ fontSize: 10 }}
                variant="outlined"
                color="inherit"
                onClick={() => maskDisabled(false)}
              >
                set&nbsp;
                <CommentsDisabledOutlinedIcon color="primary" sx={{ fontSize: 14, verticalAlign: 'middle' }} />
                <VisibilityOffOutlinedIcon color="primary" sx={{ fontSize: 14, verticalAlign: 'middle' }} />
              </Button>
            </Tooltip>
          </Grid>
          <Grid item>
            <Tooltip arrow placement="top" title="set selected entities to be not visible and not output">
              <Button
                size="small"
                sx={{ fontSize: 10 }}
                variant="outlined"
                color="inherit"
                onClick={() => maskDisabled(true)}
              >
                unset&nbsp;
                <CommentsDisabledOutlinedIcon sx={{ fontSize: 14, verticalAlign: 'middle' }} />
                <VisibilityOffOutlinedIcon sx={{ fontSize: 14, verticalAlign: 'middle' }} />
              </Button>
            </Tooltip>
          </Grid>
        </Grid>
        <Table data={{ nodes: shown_data }} theme={entities_theme} sort={entity_sort} layout={{ custom: true }}>
          {(tableList: any) => (
            <>
              <Header>
                <HeaderRow>
                  <HeaderCell>OPTIONS</HeaderCell>
                  <HeaderCell resize>
                    <Button
                      fullWidth
                      style={{ fontSize: '14px', justifyContent: 'flex-start' }}
                      endIcon={getSortIcon(entity_sort.state, 'NAME')}
                      onClick={() => entity_sort.fns.onToggleSort({ sortKey: 'NAME' })}
                    >
                      NAME
                    </Button>
                  </HeaderCell>
                  <HeaderCell>VALUE</HeaderCell>
                </HeaderRow>
              </Header>
              <Body>
                {tableList.map((de: DeviceEntity) => (
                  <Row key={de.id} item={de}>
                    <Cell>
                      <ToggleButtonGroup
                        size="small"
                        color="secondary"
                        value={getMaskString(de.m)}
                        onChange={(event, mask) => {
                          de.m = getMaskNumber(mask);
                          if (de.m & DeviceEntityMask.DV_WEB_EXCLUDE) {
                            de.m = de.m & ~DeviceEntityMask.DV_FAVORITE;
                          }
                          setMasks(['']);
                        }}
                      >
                        <ToggleButton value="8" disabled={(de.m & 1) !== 0}>
                          <StarIcon sx={{ fontSize: 14 }} />
                        </ToggleButton>
                        <ToggleButton value="4" disabled={!de.w || (de.m & 3) === 3 || isCmd(de)}>
                          <EditOffOutlinedIcon sx={{ fontSize: 14 }} />
                        </ToggleButton>
                        <ToggleButton value="2" disabled={isCmd(de)}>
                          <CommentsDisabledOutlinedIcon sx={{ fontSize: 14 }} />
                        </ToggleButton>
                        <ToggleButton value="1">
                          <VisibilityOffOutlinedIcon sx={{ fontSize: 14 }} />
                        </ToggleButton>
                      </ToggleButtonGroup>
                    </Cell>
                    <Cell>{formatName(de)}</Cell>
                    <Cell>{formatValue(de.v)}</Cell>
                  </Row>
                ))}
              </Body>
            </>
          )}
        </Table>
      </>
    );
  };

  const renderResetDialog = () => (
    <Dialog open={confirmReset} onClose={() => setConfirmReset(false)}>
      <DialogTitle>Reset</DialogTitle>
      <DialogContent dividers>
        Are you sure you want remove all customizations including the custom settings of the Temperature and Analog
        sensors?
      </DialogContent>
      <DialogActions>
        <Button startIcon={<CancelIcon />} variant="outlined" onClick={() => setConfirmReset(false)} color="secondary">
          Cancel
        </Button>
        <Button
          startIcon={<SettingsBackupRestoreIcon />}
          variant="outlined"
          onClick={resetCustomization}
          autoFocus
          color="error"
        >
          Reset
        </Button>
      </DialogActions>
    </Dialog>
  );

  const content = () => {
    return (
      <>
        <Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
          Device Entities
        </Typography>
        {renderDeviceList()}
        {renderDeviceData()}
        <Box display="flex" flexWrap="wrap">
          <Box flexGrow={1}>
            <ButtonRow>
              <Button startIcon={<SaveIcon />} variant="outlined" color="primary" onClick={() => saveCustomization()}>
                Save
              </Button>
            </ButtonRow>
          </Box>
          <ButtonRow>
            <Button
              startIcon={<SettingsBackupRestoreIcon />}
              variant="outlined"
              color="error"
              onClick={() => setConfirmReset(true)}
            >
              Reset
            </Button>
          </ButtonRow>
        </Box>
        {renderResetDialog()}
      </>
    );
  };

  return (
    <SectionContent title="User Customization" titleGutter>
      {content()}
    </SectionContent>
  );
};

export default SettingsCustomization;
