import {
  Button,
  DialogActions,
  DialogContent,
  DialogTitle,
  Grid2 as Grid,
  MenuItem,
  TextField,
  Theme,
  Typography
} from "@mui/material";
import { makeStyles } from "@mui/styles";
import DeleteButton from "common/DeleteButton";
import ResponsiveDialog from "common/ResponsiveDialog";
import SearchSelect, { Option } from "common/SearchSelect";
import { usePreferenceAPI, usePrevious, useSnackbar } from "hooks";
import { cloneDeep } from "lodash";
import { Backpack, Component, Device, Interaction } from "models";
import { backpackOptions } from "pbHelpers/Backpack";
import { DeviceComponentKey } from "pbHelpers/Component";
import { ListDeviceProductDescribers } from "products/DeviceProduct";
import { pond } from "protobuf-ts/pond";
import { useBackpackAPI } from "providers/pond/backpackAPI";
import React, { useCallback, useEffect, useState } from "react";

const useStyles = makeStyles((theme: Theme) => {
  return ({
    contentSpacing: {
      marginTop: theme.spacing(1),
      marginBottom: theme.spacing(1)
    }
  })
});

interface Props {
  isDialogOpen: boolean;
  closeDialogCallback: Function;
  refreshCallback: Function;
  device: Device;
  components: Component[];
  interactions: Interaction[];
  tagIds: string[];
}

export default function SaveDeviceProfile(props: Props) {
  const backpackAPI = useBackpackAPI();
  const preferenceAPI = usePreferenceAPI();
  const classes = useStyles();
  const {
    isDialogOpen,
    closeDialogCallback,
    refreshCallback,
    device,
    components,
    interactions,
    tagIds
  } = props;
  const { success, error } = useSnackbar();
  const prevOpen = usePrevious(isDialogOpen);
  const [backpacks, setBackpacks] = useState<Backpack[]>([]);
  const [backpackName, setBackpackName] = useState<string>();
  const [loading, setLoading] = useState<boolean>(false);
  const [isNew, setIsNew] = useState<boolean>(false);
  const [targetBackpackID, setTargetBackpackID] = useState<number>(0);
  const [product, setProduct] = useState<pond.DeviceProduct>(0);

  const defaultState = () => {
    setBackpacks([]);
    setBackpackName("");
    setIsNew(false);
    setTargetBackpackID(0);
  };

  const close = () => {
    closeDialogCallback();
    defaultState();
  };

  const loadBackpacks = useCallback(() => {
    const { listBackpacks } = backpackAPI;
    setLoading(true);
    listBackpacks()
      .then((response: any) => {
        const rawBackpacks = response.data.backpacks;
        const backpacks: Backpack[] = [];

        if (rawBackpacks && rawBackpacks.length > 0) {
          rawBackpacks.forEach((b: any) => {
            backpacks.push(Backpack.create(pond.Backpack.fromObject(b)));
          });
        }

        setBackpacks(backpacks.sort((b1, b2) => (b1.name() > b2.name() ? 1 : -1)));
      })
      .catch((err: any) => {
        setBackpacks([]);
        error(err ? err : "Error occured while loading device profiles");
      })
      .finally(() => setLoading(false));
  }, [backpackAPI, error]);

  useEffect(() => {
    if (isDialogOpen === true && isDialogOpen !== prevOpen) {
      loadBackpacks();
    }
  }, [isDialogOpen, prevOpen, loadBackpacks]);

  const assembleBackpack = (): Promise<pond.BackpackSettings> => {
    const { getPreferences } = preferenceAPI;
    return new Promise((resolve, reject) => {
      let getDevicePreference = getPreferences("device", [device.id().toString()]);
      let getComponentPreferences = getPreferences(
        "component",
        components.map(c => DeviceComponentKey(device, c))
      );
      let promises = [getDevicePreference, getComponentPreferences];
      Promise.all(promises)
        .then(responses => {
          let rDevicePreferences = responses[0];
          let rComponentPreferences = responses[1];
          let componentPreferences = {} as {
            [k: string]: pond.UserPreferences;
          };
          rComponentPreferences.forEach(pref => {
            let match = components.find(c => DeviceComponentKey(device, c) === pref.key);
            if (match && pref.preferences) {
              componentPreferences[match.locationString()] = pref.preferences;
            }
          });
          let deviceCopy = cloneDeep(device.settings);
          deviceCopy.deviceId = 0;
          deviceCopy.platform = pond.DevicePlatform.DEVICE_PLATFORM_INVALID;
          if (rDevicePreferences.length < 1) {
            rDevicePreferences.push(pond.ModelPreferences.create());
          }
          let backpack = pond.BackpackSettings.create({
            backpackId: targetBackpackID,
            product: product,
            name: backpackName,
            device: deviceCopy,
            components: components.map(c => {
              let component = cloneDeep(c.settings);
              component.key = "";
              return component;
            }),
            interactions: interactions.map(i => {
              let interaction = cloneDeep(i.settings);
              interaction.key = "";
              return interaction;
            }),
            tagKeys: tagIds,
            devicePreferences: rDevicePreferences[0].preferences,
            componentPreferences: componentPreferences
          });
          resolve(backpack);
        })
        .catch(() => {
          let reason = "Failed loading user preferences";
          error(reason);
          reject(reason);
        });
    });
  };

  const submit = () => {
    const { addBackpack, updateBackpack } = backpackAPI;
    assembleBackpack().then((backpack: pond.BackpackSettings) => {
      if (isNew) {
        addBackpack(backpack)
          .then((response: any) => {
            success("Successfully created " + backpackName + " using " + device.name() + "!");
            refreshCallback();
          })
          .catch((err: any) => {
            error(err ? err : "Error occured while creating " + backpackName + ".");
          })
          .finally(() => close());
      } else {
        updateBackpack(targetBackpackID, backpack)
          .then((response: any) => {
            success("Successfully updated " + backpackName + " using " + device.name() + "!");
            refreshCallback();
          })
          .catch((err: any) => {
            error(err ? err : "Error occured while updating " + backpackName + ".");
          })
          .finally(() => close());
      }
    });
  };

  const removeSelectedProfile = () => {
    const { removeBackpack } = backpackAPI;

    const backpackID = targetBackpackID;
    const backpack = backpacks.find(b => b.id() === backpackID);
    if (targetBackpackID && backpack) {
      let backpackName = backpack.name();
      removeBackpack(backpackID)
        .then(() => {
          success("Successfully removed " + backpackName);
          let updatedBackpacks = cloneDeep(backpacks);
          defaultState();
          setBackpacks(updatedBackpacks.filter(b => b.id() !== backpackID));
        })
        .catch(err => error(err ? err : "Error occured while removing " + backpackName));
    }
  };

  const changeTargetBackpack = (option: Option | null) => {
    let isNew = option && option.new === true ? true : false;
    setIsNew(isNew);
    setTargetBackpackID(isNew || !(option && option.value) ? 0 : Number(option.value));
    setBackpackName(option && option.label ? option.label : backpackName);
  };

  const isFormValid = (): boolean => {
    const isValidBackpackName = isNew ? backpackName !== "" : true;
    const isValidTargetBackpack = isNew ? true : targetBackpackID > 0;
    return isValidBackpackName && isValidTargetBackpack;
  };

  // UI Begins

  const content = () => {
    const options = backpackOptions(backpacks);
    let selected: Option | undefined = options.find(
      option => (option.value as number) === targetBackpackID
    );

    return (
      <React.Fragment>
        <SearchSelect
          label={"Save to an existing or new profile"}
          selected={selected}
          options={options}
          changeSelection={(option: Option | null) => changeTargetBackpack(option)}
          loading={loading}
          creatable
        />
        <TextField
          id="deviceProduct"
          label="Device Product"
          select
          value={product}
          onChange={e => setProduct(+e.target.value)}
          margin="normal"
          fullWidth
          variant="outlined">
          {ListDeviceProductDescribers().map(describer => (
            <MenuItem key={describer.product} value={describer.product}>
              {describer.label}
            </MenuItem>
          ))}
        </TextField>
      </React.Fragment>
    );
  };

  const actions = () => {
    return (
      <Grid container justifyContent="space-between" direction="row">
        <Grid size={{ xs: 5 }}>
          <DeleteButton disabled={!isFormValid()} onClick={() => removeSelectedProfile()}>
            Delete
          </DeleteButton>
        </Grid>
        <Grid size={{ xs: 7 }} container justifyItems="flex-end">
          <Button size="small" color="primary" onClick={close}>
            Cancel
          </Button>
          <Button size="small" disabled={!isFormValid()} color="primary" onClick={submit}>
            {isNew ? "Create" : "Update"}
          </Button>
        </Grid>
      </Grid>
    );
  };

  return (
    <ResponsiveDialog
      open={isDialogOpen}
      onClose={close}
      aria-labelledby="save-device-profile"
      maxWidth="sm"
      fullWidth>
      <DialogTitle id="save-device-profile-title">
        Save Device Profile
        <Typography variant="body2" color="textSecondary">
          {device.name()}
        </Typography>
      </DialogTitle>
      <DialogContent className={classes.contentSpacing}>{content()}</DialogContent>
      <DialogActions>{actions()}</DialogActions>
    </ResponsiveDialog>
  );
}
