import {
  Button,
  CircularProgress,
  DialogActions,
  DialogContent,
  DialogTitle,
  Divider,
  Grid2 as Grid,
  IconButton,
  List,
  ListItem,
  TextField,
  Tooltip,
  Typography
} from "@mui/material";
import { FileCopy } from "@mui/icons-material";
import ResponsiveDialog from "common/ResponsiveDialog";
import SearchSelect, { Option } from "common/SearchSelect";
import SelectDevicePlatform from "device/SelectDevicePlatform";
import { useBackpackAPI, useDeviceAPI, usePrevious, useSnackbar } from "hooks";
import { cloneDeep } from "lodash";
import { Backpack, Device } from "models";
import { backpackOptions, genericBackpacks } from "pbHelpers/Backpack";
import { pond } from "protobuf-ts/pond";
import React, { useEffect, useState } from "react";
import { useGlobalState } from "providers";

interface Props {
  isOpen: boolean;
  refreshCallback: Function;
  closeDialogCallback: Function;
}

export default function ProvisionDevice(props: Props) {
  const deviceAPI = useDeviceAPI();
  const backpackAPI = useBackpackAPI();
  const { error, success, warning, info } = useSnackbar();
  const [{as}] = useGlobalState();
  const { isOpen, closeDialogCallback, refreshCallback } = props;
  const [provisioned, setProvisioned] = useState<boolean>(false);
  const [idHex, setIdHex] = useState<string>("");
  const [keyHex, setKeyHex] = useState<string>("");
  const [backpack, setBackpack] = useState<Backpack | undefined>(undefined);
  const [name, setName] = useState<string>("");
  const [description, setDescription] = useState<string>("");
  const [loading, setLoading] = useState<boolean>(false);
  const [backpacks, setBackpacks] = useState<Backpack[]>([]);
  const [command, setCommand] = useState<string>("");
  const [deviceLoading, setDeviceLoading] = useState<boolean>(false);
  const [platform, setPlatform] = useState<pond.DevicePlatform>(
    pond.DevicePlatform.DEVICE_PLATFORM_PHOTON
  );
  const [device, setDevice] = useState<Device | undefined>(undefined);
  const prevIsOpen = usePrevious(isOpen);
  const deviceID: number = parseInt(idHex, 16);

  const setDefaultState = () => {
    setProvisioned(false);
    setIdHex("");
    setKeyHex("");
    setBackpack(undefined);
    setName("");
    setDescription("");
    setLoading(false);
    setBackpacks([]);
    setCommand("");
    setDeviceLoading(false);
    setDevice(undefined);
  };

  useEffect(() => {
    if (isOpen && !prevIsOpen) {
      setLoading(true);

      backpackAPI
        .listBackpacks()
        .then((response: any) => {
          const rawBackpacks = response.data.backpacks;
          let updatedBackpacks: Backpack[] = genericBackpacks();
          if (rawBackpacks && rawBackpacks.length > 0) {
            rawBackpacks.forEach((backpack: any) => {
              updatedBackpacks.push(Backpack.any(backpack));
            });
          }

          setLoading(false);
          setBackpacks(updatedBackpacks.sort((a, b) => (a.name() > b.name() ? 1 : -1)));
          setBackpack(undefined);
        })
        .catch((err: any) => {
          setDefaultState();
          error(err ? err : "Error occured while loading device profiles");
        })
        .finally(() => setLoading(false));
    }
  }, [backpackAPI, isOpen, prevIsOpen, error]);

  useEffect(() => {
    if (isOpen && provisioned && !device && !deviceLoading) {
      setDeviceLoading(true);
      const deviceID = parseInt(idHex, 16);
      deviceAPI
        .get(deviceID, undefined, undefined, undefined, as)
        .then(response => {
          let rDevice = Device.any(response.data);
          setDevice(rDevice);
          setName(rDevice.name());
          setDescription(rDevice.settings.description);
        })
        .catch(error => {
          console.error(error);
          setDevice(
            Device.create(
              pond.Device.create({ settings: pond.DeviceSettings.create({ deviceId: deviceID }) })
            )
          );
        })
        .finally(() => setDeviceLoading(false));
    }
  }, [device, deviceAPI, deviceLoading, idHex, isOpen, provisioned]);

  const close = () => {
    if (provisioned) {
      refreshCallback();
    }

    closeDialogCallback();
    setDefaultState();
  };

  const generateCommand = (idHex: any, keyHex: any): string => {
    if (!idHex || !keyHex) {
      return "";
    }
    return "set creds " + idHex + ":" + keyHex;
  };

  const assembleBackpack = (): pond.BackpackSettings => {
    let assembledBackpack = backpack ? cloneDeep(backpack) : Backpack.create();
    let agnosticDevice = assembledBackpack.settings.device
      ? cloneDeep(assembledBackpack.settings.device)
      : ({} as pond.DeviceSettings);
    agnosticDevice.deviceId = 0;
    agnosticDevice.platform = platform;
    agnosticDevice.product = assembledBackpack.settings.product;
    assembledBackpack.settings.device = agnosticDevice;

    return assembledBackpack.settings;
  };

  const submitProvision = () => {
    setProvisioned(false);
    deviceAPI
      .add("", "", assembleBackpack(), as)
      .then((response: any) => {
        let updatedIdHex = response.data.idHex;
        let updatedKeyHex = response.data.keyHex;
        setIdHex(updatedIdHex);
        setKeyHex(updatedKeyHex);
        success("Successfully provisioned a new device (id: " + parseInt(updatedIdHex, 16) + ")");
        setProvisioned(true);
        setCommand(generateCommand(updatedIdHex, updatedKeyHex));
      })
      .catch((error: any) => {
        error.response.data.error
          ? warning(error.response.data.error)
          : error("Error occured while provisioning a new device.");
        close();
      });
  };

  const submitDeviceUpdate = () => {
    const deviceID = parseInt(idHex, 16);
    let settings = pond.DeviceSettings.create(device ? device.settings : { deviceId: deviceID });
    settings.name = name;
    settings.description = description;
    deviceAPI
      .update(deviceID, settings, as)
      .then(() => {
        success("Device " + deviceID.toString() + " was successfully updated");
      })
      .catch((error: any) => {
        error.response.data.error
          ? warning(error.response.data.error)
          : error("Error occured while updating device " + deviceID.toString() + ".");
        close();
      })
      .finally(() => {
        setDevice(undefined);
      });
  };

  const copyID = () => {
    navigator.clipboard.writeText(idHex);
    info("ID copied to clipboard");
  };

  const copyKey = () => {
    navigator.clipboard.writeText(keyHex);
    info("Key copied to clipboard");
  };

  const copyCommand = () => {
    navigator.clipboard.writeText(command + "\n");
    info("Command copied to clipboard");
  };

  const changeBackpack = (option: Option | null) => {
    let selectedBackpack = undefined;
    if (option) {
      const value = Number(option.value);
      selectedBackpack = backpacks.find(backpack => {
        let idMatch: boolean = backpack.id() === value;
        return idMatch && (value === 0 ? backpack.name() === option.label : true);
      });
    }
    setBackpack(selectedBackpack);
  };

  const content = () => {
    if (provisioned) {
      return (
        <Grid container spacing={1}>
          <Grid size={{xs:12}} container justifyContent="center" alignItems="center">
            <Grid size={{xs: 11}}>
              <TextField
                id="id"
                name="ID"
                label="ID Hex"
                disabled
                value={idHex}
                margin="normal"
                variant="outlined"
                type="text"
                fullWidth
                InputLabelProps={{ shrink: true }}
              />
            </Grid>
            <Grid size={{xs:1}}>
              <Tooltip title="Copy ID">
                <IconButton onClick={copyID}>
                  <FileCopy />
                </IconButton>
              </Tooltip>
            </Grid>
          </Grid>
          <Grid size={{xs:12}} container justifyContent="center" alignItems="center">
            <Grid size={{xs:11}}>
              <TextField
                id="key"
                name="Key"
                label="Key Hex"
                disabled
                value={keyHex}
                margin="normal"
                variant="outlined"
                type="text"
                fullWidth
                InputLabelProps={{ shrink: true }}
              />
            </Grid>
            <Grid size={{xs:1}}>
              <Tooltip title="Copy Key">
                <IconButton onClick={copyKey}>
                  <FileCopy />
                </IconButton>
              </Tooltip>
            </Grid>
          </Grid>
          <Grid size={{xs:12}} container justifyContent="center" alignItems="center">
            <Grid size={{xs:11}}>
              <TextField
                id="command"
                name="command"
                label="Command"
                variant="outlined"
                value={command}
                disabled
                fullWidth
                InputLabelProps={{ shrink: true }}
              />
            </Grid>
            <Grid size={{xs:1}}>
              <Tooltip title="Copy Command">
                <IconButton onClick={copyCommand}>
                  <FileCopy />
                </IconButton>
              </Tooltip>
            </Grid>
          </Grid>
          {/* {window.NDEFReader && (
            <Button
              variant="contained"
              color="primary"
              onClick={() => {
                const ndef = new NDEFReader();
                ndef
                  .write({
                    records: [{ recordType: "text", data: command }]
                  })
                  .then(resp => {
                    success("Comand sent");
                  })
                  .catch(error => {
                    error("Failed to send command");
                  });
              }}>
              Write to Device
            </Button>
          )} */}
          <Grid size={{xs:12}}>
            <Divider />
          </Grid>
          {deviceLoading ? (
            <CircularProgress />
          ) : (
            <Grid size={{xs:12}}>
              <Typography color="textPrimary" variant="body1" gutterBottom>
                Update device name/description
              </Typography>
              <TextField
                margin="normal"
                id="deviceName"
                label="Name"
                type="text"
                fullWidth
                variant="outlined"
                value={name}
                onChange={(event: any) => setName(event.target.value)}
              />
              <TextField
                margin="normal"
                id="deviceDescription"
                label="Description"
                type="text"
                multiline
                rows="4"
                // rowsMax="4"
                fullWidth
                variant="outlined"
                value={description}
                onChange={(event: any) => setDescription(event.target.value)}
              />
            </Grid>
          )}
        </Grid>
      );
    }

    const selectedBackpack = backpack
      ? ({ label: backpack.name(), value: backpack.id() } as Option)
      : undefined;

    return (
      <List>
        <ListItem>
          <SelectDevicePlatform value={platform} onChange={platform => setPlatform(platform)} />
        </ListItem>
        <ListItem>
          <SearchSelect
            label="Select a profile to provision with"
            selected={selectedBackpack}
            options={backpackOptions(backpacks)}
            changeSelection={changeBackpack}
            loading={loading}
          />
        </ListItem>
      </List>
    );
  };

  const actions = () => {
    return (
      <React.Fragment>
        <Button onClick={close} color="primary">
          Close
        </Button>
        {!provisioned ? (
          <Button onClick={submitProvision} color="primary" disabled={!backpack}>
            Provision
          </Button>
        ) : (
          <Button onClick={submitDeviceUpdate} color="primary" disabled={!backpack}>
            Update
          </Button>
        )}
      </React.Fragment>
    );
  };

  return (
    <ResponsiveDialog open={isOpen} onClose={close} aria-labelledby="form-dialog-title" fullWidth>
      <DialogTitle id="form-dialog-title">
        Provision Device
        {provisioned && (
          <Typography variant="body2" color="textSecondary">
            Device ID: {deviceID}
          </Typography>
        )}
      </DialogTitle>
      <DialogContent>{content()}</DialogContent>
      <DialogActions>{actions()}</DialogActions>
    </ResponsiveDialog>
  );
}
