import { Edit, GppGood, Info, Save } from "@mui/icons-material";
import { Box, Button, darken, DialogActions, IconButton, Table, TableBody, TableCell, TableHead, TableRow, TextField, Theme, Tooltip, Typography } from "@mui/material";
import { green, grey, orange, red, yellow } from "@mui/material/colors";
import { makeStyles } from "@mui/styles";
import ResponsiveDialog from "common/ResponsiveDialog";
import HumidityIcon from "component/HumidityIcon";
import TemperatureIcon from "component/TemperatureIcon";
import { cloneDeep } from "lodash";
import { Bin } from "models";
import { pond } from "protobuf-ts/pond";
import { useGlobalState } from "providers";
import React, { useEffect, useRef, useState } from "react";
import { useMemo } from "react";
import { avg, celsiusToFahrenheit } from "utils";
import BinTableExpanded from "./BinTableExpanded";
import { useMobile } from "hooks";

const useStyles = makeStyles((theme: Theme) => ({
    tableStyle: {
        borderCollapse: "separate",
        borderSpacing: 0,  // removes the default gap that "separate" introduces
    },
    headerCellStyle: {
        fontWeight: 650,
        backgroundColor: darken(theme.palette.background.paper, 0.2),
        fontSize: 15,
        textAlign: "center",
        borderTop: "1px solid black",
        borderLeft: "1px solid black",
        borderBottom: "1px solid black",
        "&:last-child": {
            borderRight: "1px solid black",
        }
    },
    cellStyle: {
        padding: 2,
        fontWeight: 650,
        fontSize: 17,
        textAlign: "center",
        border: "1px solid black",

    }
  })
);

interface Props {
    bin: Bin
    updateBinCallback?: (bin: Bin) => void
}

interface BinLevel {
    grainTemps: number[]
    airTemps: number[]
    grainMoistures: number[]
    airMoistures: number[]
}


export default function BinTableView(props: Props) {
    const classes = useStyles()
    const {bin, updateBinCallback} = props
    const [{user}] = useGlobalState()
    const [enterTemp, setEnterTemp] = useState(false)
    const [enterMoisture, setEnterMoisture] = useState(false)
    const [tempTarget, setTempTarget] = useState(0)
    const [tempEntry, setTempEntry] = useState("")
    const [moistureEntry, setMoistureEntry] = useState("")
    const [moistureTarget, setMoistureTarget] = useState(0)
    const [openExpandedCables, setOpenExpandedCables] = useState(false)
    const mobileView = useMobile()
    const inBounds = green[600]
    const caution = yellow[800]
    const warning = red[700]
    const headerRow1Ref = useRef<HTMLTableRowElement>(null)
    const [firstRowHeight, setFirstRowHeight] = useState(0)
    
    useEffect(() => {
        if (headerRow1Ref.current) {
            setFirstRowHeight(headerRow1Ref.current.getBoundingClientRect().height)
        }
    }, [enterTemp, enterMoisture]) // re-measure when the row height might change due to TextField appearing

    useEffect(()=>{
        let val = bin.targetTemp()
        if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){
            val = bin.targetTemp() * (9/5) + 32
        }
        setTempTarget(val)
        setTempEntry(val.toFixed(2))
        setMoistureTarget(bin.targetMoisture())
        setMoistureEntry(bin.targetMoisture().toFixed(2))
    },[bin])
    
    const binLevels = useMemo(() => {
        const cables = bin.status.grainCables
        let levels: BinLevel[] = []
        //note for future self, the first item in the measurement arrays is the lowest node on the cable
        //the zones we are creating here will match with the node number because most cables will either have the same number of nodes or only have a difference of 1 or 2
        //example: cable 1 has 5 nodes cable 2 has 7 nodes, the first five nodes of the cables will match up and average together for each zone 
        //and then zones 6 and 7 will be made using only cable 2 because cable 1 doesn't have them
        cables.forEach(cable => {
            cable.celcius.forEach((c, index) => {
                //remember index will be 0 based but the top node starts at 1 for the lowest cable
                let inGrain = index < cable.topNode
                if (levels[index]){
                    if(cable.excludedNodes.includes(index+1)) return
                    if(inGrain){
                        levels[index].grainTemps.push(c)
                        //need to check that the cable not only has the moisture mutation on it, but also that it reads humidity
                        //cables that dont read humidity return an array of 0 values so if the average of all of the values is 0 then it is a temp only cable
                        if(cable.moisture.length > 0 && avg(cable.moisture) > 0){
                            //based on all the other checks and the fact that the indexes between the array should match this should never be 0 but put a guard here just in case
                            //also note that we are only doing moisture and not humidity, it seems like users dont care about humidity
                            levels[index].grainMoistures.push(cable.moisture[index] ?? 0)
                        }
                    }else{
                        levels[index].airTemps.push(c)
                        //need to check that the cable not only has the moisture mutation on it, but also that it reads humidity
                        //cables that dont read humidity return an array of 0 values so if the average of all of the values is 0 then it is a temp only cable
                        if(cable.moisture.length > 0 && avg(cable.moisture) > 0){
                            //based on all the other checks and the fact that the indexes between the array should match this should never be 0 but put a guard here just in case
                            levels[index].airMoistures.push(cable.moisture[index] ?? 0)
                        }
                    }
                }else{
                    let newlevel: BinLevel = {
                        grainTemps: [],
                        grainMoistures: [],
                        airTemps: [],
                        airMoistures: []
                    }
                    if(!cable.excludedNodes.includes(index)){
                        if(inGrain){
                            newlevel.grainTemps.push(c)
                            if(cable.moisture.length > 0 && avg(cable.moisture) > 0){
                                newlevel.grainMoistures.push(cable.moisture[index] ?? 0)
                            }
                        }else{
                            newlevel.airTemps.push(c)
                            if(cable.moisture.length > 0 && avg(cable.moisture) > 0){
                                newlevel.airMoistures.push(cable.moisture[index] ?? 0)
                            }
                        }
                    }
                    levels.push(newlevel)
                }
                
            })
        })
        //flip the levels so that the highest nodes are on the top
        levels.reverse()
        return levels
    },[bin])

    const levelDisplay = (level: BinLevel) => {
        let tempDisplay = "--"
        let moistureDisplay = "--"
        let lvlTemp
        let lvlMoisture
        let tempColour: string = ""
        let moistureColour: string = ""

        //determine the temp
        if(level.grainTemps.length > 0){
            lvlTemp = avg(level.grainTemps)
            //if the average temp is 5 degrees above the bins threshold
            if(lvlTemp > (bin.targetTemp() + 5)){
                tempColour = warning
            }else if(lvlTemp > bin.targetTemp()){
                //if the average temp is greater than 5 degrees over
                tempColour = caution
            }else {
                tempColour = inBounds
            }
        }else if (level.airTemps.length > 0){
            lvlTemp = avg(level.airTemps)
        }
        if (lvlTemp){
            if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){
                lvlTemp = celsiusToFahrenheit(lvlTemp)
            }
            tempDisplay = lvlTemp.toFixed(2) + (user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? " °F" : " °C")
        }

        //determine the moisture to show
        if(level.grainMoistures.length > 0){
            lvlMoisture = avg(level.grainMoistures)
            if(lvlMoisture > (bin.targetMoisture() + 1.5)){
                moistureColour = warning
            }else if(lvlMoisture > bin.targetMoisture()){
                //if the average temp is greater than 5 degrees over
                moistureColour = caution
            }else {
                moistureColour = inBounds
            }
        }else if (level.airMoistures.length > 0){
            lvlMoisture = avg(level.airMoistures)
        }
        if(lvlMoisture){
            moistureDisplay = lvlMoisture.toFixed(2) + "%"
        }
        return (
            <React.Fragment>
                <TableCell padding="none" className={classes.cellStyle} sx={{backgroundColor: tempColour}}>{tempDisplay}</TableCell>
                <TableCell padding="none" className={classes.cellStyle} sx={{backgroundColor: moistureColour}}>{moistureDisplay}</TableCell>
            </React.Fragment>
        )
    }

    const update = () => {
        let temp = tempTarget
        if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){
            //convert to c
        temp = (temp - 32) * (5 / 9);

        }
        let clone = cloneDeep(bin)
        if(clone.settings.inventory && updateBinCallback){
            clone.settings.inventory.targetTemperature = temp
            clone.settings.inventory.targetMoisture = moistureTarget
            updateBinCallback(clone)
        }
    }

    const tempThreshDisplay = () => {
        if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){
            return tempTarget.toFixed(2) + " °F"
        }
        return tempTarget.toFixed(2) + " °C"
    }

    const LegendSwatch = ({ color }: { color: string }) => (
        <Box sx={{ width: 40, height: 20, backgroundColor: color, borderRadius: 1, flexShrink: 0 }} />
    )


    const updateTempEntry = () => {
        return (
            <TextField 
                value={tempEntry}
                onChange={e => {
                    let val = parseFloat(e.target.value)
                    if(!isNaN(val)){
                        setTempTarget(val)
                    }
                    setTempEntry(e.target.value)
                }}
            />
        )
    }

    const updateMoistureEntry = () => {
        return (
            <TextField 
                value={moistureEntry}
                onChange={e => {
                    let val = parseFloat(e.target.value)
                    if(!isNaN(val)){
                        setMoistureTarget(val)
                    }
                    setMoistureEntry(e.target.value)
                }}
            />
        )
    }

    const expandedTableDialog = () => {
        return (
            <ResponsiveDialog open={openExpandedCables} onClose={()=>{setOpenExpandedCables(false)}}>
                <BinTableExpanded 
                    cables={bin.status.grainCables} 
                    targetMoisture={bin.targetMoisture()} 
                    targetTemp={bin.targetTemp()} 
                    inBounds={inBounds} 
                    caution={caution} 
                    warning={warning}
                />
                <DialogActions>
                    <Button onClick={()=> {setOpenExpandedCables(false)}}>
                        Close
                    </Button>
                </DialogActions>
            </ResponsiveDialog>
        )
    }



    return (
        <Box padding={2} sx={{ display: "flex", flexDirection: "column", height: "100%" }}>
            {expandedTableDialog()}
            {/* Level Table */}
            <Box display="flex" justifyContent="space-between" alignItems="center">
              <Typography>Grain Table</Typography>
              <Box display="flex" alignItems="center" gap={1}>
                <Button 
                    variant="contained"
                    onClick={() => {
                        setOpenExpandedCables(true)
                    }}>
                        Show All Cables
                </Button>
                <Tooltip title="testing">
                    <Info />
                </Tooltip>
              </Box>
            </Box>

            {/* this box is to make the whole table scrollable */}
            <Box sx={{ overflowY: "auto", flex: 1, minHeight: 0 }}>
            <Table className={classes.tableStyle}>
                <TableHead>
                    <TableRow ref={headerRow1Ref}>
                        <TableCell className={classes.headerCellStyle} sx={{ top: 0, position: "sticky", zIndex: 3 }}>
                        <Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
                              <GppGood />
                              <Typography fontWeight={650}>
                                Targets 
                              </Typography>
                            </Box>
                        </TableCell>
                        <TableCell className={classes.headerCellStyle} sx={{ top: 0, position: "sticky", zIndex: 3 }}>
                            <Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
                              <TemperatureIcon heightWidth={30}/>
                              {enterTemp ? 
                                updateTempEntry()
                              :
                                <Typography fontWeight={650}>
                                  {tempThreshDisplay()} 
                                </Typography>
                              }
                              {enterTemp ? 
                                <IconButton 
                                  onClick={() => {
                                    setEnterTemp(false)
                                    //and update the bin with the new temp target
                                    update()
                                  }}
                                >
                                  <Save />
                                </IconButton>
                              :
                                <IconButton 
                                    onClick={() => {
                                        setEnterTemp(true)
                                    }}>
                                  <Edit />
                                </IconButton>
                              }
                            </Box>
                        </TableCell>
                        <TableCell className={classes.headerCellStyle} sx={{ top: 0, position: "sticky", zIndex: 3 }}>
                            <Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
                              <HumidityIcon height={30} width={20}/>
                              {enterMoisture ?
                                updateMoistureEntry()
                              :
                                <Typography fontWeight={650} sx={{marginLeft: "10px"}}>
                                  {bin.targetMoisture()}%
                                </Typography>
                              }
                              {enterMoisture ? 
                                <IconButton 
                                  onClick={() => {
                                    setEnterMoisture(false)
                                    //and update the bin with the new temp target
                                    update()
                                  }}
                                >
                                  <Save />
                                </IconButton>
                              :
                                <IconButton 
                                    onClick={() => {
                                        setEnterMoisture(true)
                                    }}>
                                  <Edit />
                                </IconButton>
                              }
                            </Box>
                        </TableCell>
                    </TableRow>
                    <TableRow>
                        <TableCell className={classes.headerCellStyle} sx={{ top: firstRowHeight, position: "sticky", zIndex: 3 }}>Level</TableCell>
                        <TableCell className={classes.headerCellStyle} sx={{ top: firstRowHeight, position: "sticky", zIndex: 3 }}>Temperature</TableCell>
                        <TableCell className={classes.headerCellStyle} sx={{ top: firstRowHeight, position: "sticky", zIndex: 3 }}>Moisture</TableCell>
                    </TableRow>
                </TableHead>

                <TableBody>
                  {binLevels.map((level, i) => (
                      <TableRow key={i}>
                          <TableCell padding="none" className={classes.cellStyle}>{binLevels.length - i}</TableCell>
                          {levelDisplay(level)}
                      </TableRow>
                  ))}
                  <TableRow>
                    <TableCell>TESTING</TableCell>
                    <TableCell>SCROLLING</TableCell>
                    <TableCell>ROWS</TableCell>
                  </TableRow>
                  <TableRow>
                    <TableCell>TESTING</TableCell>
                    <TableCell>SCROLLING</TableCell>
                    <TableCell>ROWS</TableCell>
                  </TableRow>
                  <TableRow>
                    <TableCell>TESTING</TableCell>
                    <TableCell>SCROLLING</TableCell>
                    <TableCell>ROWS</TableCell>
                  </TableRow>
                  <TableRow>
                    <TableCell>TESTING</TableCell>
                    <TableCell>SCROLLING</TableCell>
                    <TableCell>ROWS</TableCell>
                  </TableRow>
                  <TableRow>
                    <TableCell>TESTING</TableCell>
                    <TableCell>SCROLLING</TableCell>
                    <TableCell>ROWS</TableCell>
                  </TableRow>
                  <TableRow>
                    <TableCell>TESTING</TableCell>
                    <TableCell>SCROLLING</TableCell>
                    <TableCell>ROWS</TableCell>
                  </TableRow>
                  <TableRow>
                    <TableCell>TESTING</TableCell>
                    <TableCell>SCROLLING</TableCell>
                    <TableCell>ROWS</TableCell>
                  </TableRow>
                </TableBody>
            </Table>
            </Box>
            {/* Table Legend */}
            <Box display="flex" alignItems="center" justifyContent="space-between" marginTop={2} sx={{ flexShrink: 0 }}>
                <Box display="flex" alignItems="center" gap={1}>
                    <LegendSwatch color={inBounds} />
                    <Typography sx={{fontWeight: 600, fontSize: 10}}>On or below target</Typography>
                </Box>
                <Box display="flex" alignItems="center" gap={1}>
                    <LegendSwatch color={caution} />
                    <Typography sx={{fontWeight: 600, fontSize: 10}}>{user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "+10 °F" : "+5 °C"} above target</Typography>
                </Box>
                <Box display="flex" alignItems="center" gap={1}>
                    <LegendSwatch color={warning} />
                    <Typography sx={{fontWeight: 600, fontSize: 10}}>{user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? "+20 °F" : "+10 °C"} above target</Typography>
                </Box>
            </Box>

        </Box>
    )
}