import { AppBar, Box, Button, Card, CardActionArea, Checkbox, DialogActions, DialogContent, DialogTitle, FormControlLabel, Grid2, IconButton, Typography } from "@mui/material";
import { green } from "@mui/material/colors";
import { makeStyles } from "@mui/styles";
import { CableData } from "bin/3dView/Data/BuildCableData";
import { NodeData } from "bin/3dView/Data/BuildNodeData";
import ResponsiveDialog from "common/ResponsiveDialog";
import HumidityIcon from "component/HumidityIcon";
import TemperatureIcon from "component/TemperatureIcon";
import { useComponentAPI, useMobile, useSnackbar } from "hooks";
import InteractionsOverview from "interactions/InteractionsOverview";
import { Bin, Component, Device, Interaction } from "models";
import moment from "moment";
import AddIcon from "@mui/icons-material/AddCircle";
import GraphIcon from "products/CommonIcons/graphIcon";
import { pond } from "protobuf-ts/pond";
import { useBinAPI, useGlobalState, useInteractionsAPI } from "providers";
import React from "react";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { canWrite } from "pbHelpers/Permission";
import InteractionSettings from "interactions/InteractionSettings";

const useStyles = makeStyles(() => {
    return ({
      dialog: {
        maxWidth: 350
      },
      appBar: {
        position: "static",
        borderRadius: 30
      },
      readingCard: {
        padding: 10
      },
      greenButton: {
        color: green["600"],
        padding: 0
      }
    });
  });

interface Props {
    open: boolean
    onClose: () => void
    node: NodeData
    cable: CableData
    device: Device
    bin: Bin
    /**
     * the possible options for controllers when creating new interactions, these controllers must be on the same device as the cable for interactions to work
     */
    filteredComponents?: Component[]
    componentMap: Map<string, Component>
    permissions: pond.Permission[]
    /**
     * callback function for updating the cable with a new top node or excluded node
     * @returns void
     */
    updateComponentCallback?: () => void
}

export default function NodeControls(props: Props){
    const {open, onClose, node, cable, device, bin, filteredComponents, permissions, updateComponentCallback, componentMap} = props
    const classes = useStyles()
    const [isTopNode, setIsTopNode] = useState(false)
    const [isExcluded, setIsExcluded] = useState(false)
    const [cableComponent, setCableComponent] = useState<Component>(Component.create())
    const [cableInteractions, setCableInteractions] = useState<Interaction[]>([])
    const isMobile = useMobile()
    const navigate = useNavigate()
    const [{as, user}] = useGlobalState()
    const interactionAPI = useInteractionsAPI()
    const binAPI = useBinAPI()
    const componentAPI = useComponentAPI()
    const {openSnack} = useSnackbar()
    const [newInteraction, setNewInteraction] = useState(false);

    //will need to list the interactions for the cable
    useEffect(() => {
        let component = componentMap.get(cable.grainCable.key) ?? Component.create()
        setCableComponent(component)
        interactionAPI.listInteractionsByComponent(device.id(), cableComponent.location(), undefined, as).then(resp => {
            setCableInteractions(resp);
        });
    },[cable, device, interactionAPI])
    
    //determine if the node is excluded/the top node
    useEffect(()=>{
        if(node.topNode !== undefined){
            setIsTopNode(node.topNode)
        }
        if(cable.grainCable.excludedNodes.includes(node.nodeIndex)){
            setIsExcluded(true)
        }else(
            setIsExcluded(false)
        )
    },[node, cable])
    
    //navigation function to go to the component page
    const goToComponent = () => {
        navigate("/devices/" + device.id() + "/components/" + cable.grainCable.key);
    }

    const close = ()=>{
        onClose()
    }

    const setEMC = () => {
    let settings = cableComponent.settings;
    //make sure the mutation is not there first
    if(!settings.defaultMutations.includes(pond.Mutator.MUTATOR_EMC)){
        settings.defaultMutations.push(pond.Mutator.MUTATOR_EMC)
    }
    settings.grainType = bin.grain();
    settings.customGrain = bin.customGrain();
    componentAPI
      .update(device.id(), settings, [bin.key()], ["bin"], as)
      .then(resp => {
        openSnack("EMC set on cable " + cableComponent.name());
      })
      .catch(err => {});
    }

    const submit = () => {
        //should update the component and then update the bin prefs if successful
        componentAPI.update(device.id(), cableComponent.settings).then(resp => {
            let pref = pond.BinComponentPreferences.create({
                type: pond.BinComponent.BIN_COMPONENT_GRAIN_CABLE,
                node: cableComponent.settings.grainFilledTo
            });
            //update the bins preferences to have the new top node for the cable
            binAPI
              .updateComponentPreferences(bin.key(), cableComponent.key(), pref, as)
              .then(resp => {
                openSnack("Changes will be reflected once the bins status updates")
                if(updateComponentCallback){
                    updateComponentCallback()
                }
              })
              .catch(err => {});
        }).catch(err => {

        })
        close()
    }

    const updateTopNode = (checked: boolean) => {
        if (isTopNode && !checked) {
            cableComponent.settings.grainFilledTo = 0;
            cableInteractions.forEach(interaction => {
              interaction.settings.subtype = 0;
              interaction.settings.nodeOne = 0;
              interaction.settings.nodeTwo = 0;
            });
          } else if (checked) {
            cableComponent.settings.grainFilledTo = node.nodeIndex+1;
            cableInteractions.forEach(interaction => {
              interaction.settings.subtype = Interaction.upToSubtype(node.nodeIndex+1);
              interaction.settings.nodeOne = node.nodeIndex+1;
            });
          }
          setIsTopNode(checked);
    }

    const updateNodeExclusion = (checked: boolean) => {
        if (checked) {
            cableComponent.settings.excludedNodes.push(node.nodeIndex)
        } else {
            cableComponent.settings.excludedNodes.splice(cableComponent.settings.excludedNodes.indexOf(node.nodeIndex), 1)
        }
        setIsExcluded(checked)
    }

    const displayTemp = () => {
        let isF = user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
        let tempFinal = node.celcius ?? 0;
        if (isF) {
          tempFinal = (tempFinal * 1.8 + 32);
        }
        return tempFinal.toFixed(2) + (isF ? " F" : "C");
      };

    const latestReading = () => {
        return (
          <Box>
            <Typography style={{ fontSize: 20, fontWeight: 650 }}>Latest Reading</Typography>
            <Typography style={{ fontSize: 10 }}>{moment(cable.grainCable.lastRead).fromNow()}</Typography>
            <Grid2 container direction="row" spacing={2} style={{ padding: 10 }}>
              <Grid2 size={6}>
                <Card className={classes.readingCard}>
                  <Box display="flex" justifyContent="center" alignItems="center">
                    <TemperatureIcon />
                    <Typography style={{ fontWeight: 650 }}>
                      {displayTemp()}
                    </Typography>
                  </Box>
                  <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                    Temperature
                  </Typography>
                </Card>
              </Grid2>
              <Grid2 size={6}>
                <Card className={classes.readingCard}>
                  <Box display="flex" justifyContent="center" alignItems="center">
                    <HumidityIcon />
                    <Typography style={{ fontWeight: 650 }}>
                      {node.humidity} %
                    </Typography>
                  </Box>
                  <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                    Humidity
                  </Typography>
                </Card>
              </Grid2>
              <Grid2 size={6}>
                {/* only show the emc if the bin grain type and the cable grain type match */}
                {node.moisture ? (
                  <Card className={classes.readingCard}>
                    <Box display="flex" justifyContent="center" alignItems="center">
                      <HumidityIcon />
                      <Typography style={{ fontWeight: 650 }}>
                        {node.moisture} %
                      </Typography>
                    </Box>
                    <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                      Grain EMC
                    </Typography>
                  </Card>
                ) : (
                  <Card>
                    <CardActionArea
                      className={classes.readingCard}
                      onClick={() => {
                        setEMC();
                      }}>
                      <Box display="flex" justifyContent="center" alignItems="center">
                        <HumidityIcon />
                      </Box>
                      <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                        Set EMC
                      </Typography>
                    </CardActionArea>
                  </Card>
                )}
              </Grid2>
              <Grid2 size={6}>
                <Card>
                  <CardActionArea className={classes.readingCard} onClick={() => goToComponent()}>
                    <Box display="flex" justifyContent="center" alignItems="center">
                      <GraphIcon />
                    </Box>
                    <Typography style={{ textAlign: "center", color: "grey", fontWeight: 650 }}>
                      See Graphs
                    </Typography>
                  </CardActionArea>
                </Card>
              </Grid2>
            </Grid2>
          </Box>
        );
      };
    
      const nodeControl = () => {
        return (
          <Box style={{ marginTop: 10 }}>
            <Typography style={{ fontSize: 20, fontWeight: 650 }}>Node Control</Typography>
              <FormControlLabel
              style={{ marginTop: 10 }}
              control={
                <Checkbox
                  checked={isExcluded}
                  value={isExcluded}
                  onChange={e => {
                    updateNodeExclusion(e.target.checked);
                  }}
                  name="excludedNode"
                />
              }
              label={
                <React.Fragment>
                  <Typography style={{ fontSize: 15, fontWeight: 650 }}>
                    Exclude Node
                  </Typography>
                  <Typography style={{ fontSize: 15 }}>
                    If checked this will set the node to be disabled and will not be used for any calculations done for the bin
                  </Typography>
                </React.Fragment>
              }
            />
            <FormControlLabel
              style={{ marginTop: 10 }}
              control={
                <Checkbox
                  checked={isTopNode}
                  value={isTopNode}
                  disabled={isExcluded}
                  onChange={e => {
                    updateTopNode(e.target.checked);
                  }}
                  name="topNode"
                />
              }
              label={
                <React.Fragment>
                  <Typography style={{ fontSize: 15, fontWeight: 650 }}>
                    Set as top node in grain
                  </Typography>
                  <Typography style={{ fontSize: 15 }}>
                    If checked this will set interactions to ignore all nodes above it. Interactions can
                    still be adjusted manually to use all nodes
                  </Typography>
                </React.Fragment>
              }
            />
          </Box>
        );
      };

      const interactionDisplay = () => {
        return (
          <Box marginTop={2}>
            <Typography style={{ fontSize: 20, fontWeight: 650 }}>Interactions</Typography>
            <Box marginTop={1} marginBottom={1}>
              <InteractionsOverview
                component={cableComponent}
                components={filteredComponents ?? []}
                device={device}
                interactions={cableInteractions}
                permissions={permissions}
                refreshCallback={() => {
                  interactionAPI
                    .listInteractionsByComponent(device.id(), cableComponent.location(), undefined, as)
                    .then(resp => {
                      setCableInteractions(resp);
                    });
                }}
              />
            </Box>
            <Box display="flex" justifyContent="center">
              <IconButton
                color="primary"
                disabled={!canWrite(permissions)}
                aria-label="Add Interaction"
                onClick={() => {
                  setNewInteraction(true);
                }}
                className={classes.greenButton}>
                <AddIcon />
              </IconButton>
            </Box>
          </Box>
        );
      };

    return (
      <React.Fragment>
        <ResponsiveDialog open={open} onClose={close} fullWidth={isMobile}>
          <DialogTitle style={{ padding: 10 }}>
            <Box display="flex" justifyContent="center" marginBottom={2}>
              <Typography style={{ fontSize: 30, fontWeight: 650 }}>{cable.grainCable.name}</Typography>
            </Box>
            <AppBar 
              className={classes.appBar} 
              color="secondary">
              <Box>
                <Typography align="center" variant="h5">
                  Node {node.nodeIndex+1}
                </Typography>
              </Box>
            </AppBar>
          </DialogTitle>
          <DialogContent>
            {latestReading()}
            {nodeControl()}
            {interactionDisplay()} 
            {/* 
            */}
          </DialogContent>
          <DialogActions>
            <Button onClick={close} color="primary">
              Cancel
            </Button>
            <Button onClick={submit} color="primary">
              Submit
            </Button>
          </DialogActions>
        </ResponsiveDialog>
        <InteractionSettings
          isDialogOpen={newInteraction}
          initialComponent={cableComponent}
          mode="add"
          device={device}
          components={filteredComponents ?? []}
          closeDialogCallback={() => {
            setNewInteraction(false);
          }}
          refreshCallback={() => {
            interactionAPI.listInteractionsByComponent(device.id(), cableComponent.location(), undefined, as).then(resp => {
              setCableInteractions(resp);
            });
          }}
          canEdit={canWrite(permissions)}
        />
      </React.Fragment>
    )
}