import { Edit, GppGood, Info, Save } from "@mui/icons-material";
import { Box, Button, darken, DialogActions, IconButton, Table, TableBody, TableCell, TableHead, TableRow, TextField, Theme, Tooltip, Typography, useTheme } 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.3),
        textAlign: "center",
        padding: 10
    },
    mobileHeaderCellStyle: {
        fontWeight: 650,
        backgroundColor: darken(theme.palette.background.paper, 0.3),
        textAlign: "center",
        padding: 2
    },
    targetCellStyle: {
        textAlign: "center",
        backgroundColor: theme.palette.background.paper
    },
    mobileTargetCellStyle: {
        textAlign: "center",
        backgroundColor: theme.palette.background.paper,
        padding: 2
    },
    defaultCellStyle: {
        padding: 2,
        fontSize: 15,
        textAlign: "center",
        backgroundColor: darken(theme.palette.background.paper, 0.1),
        borderTop: "1px solid " + darken(theme.palette.background.paper, 0.4),
        borderBottom: "none"
    },
    colouredCellStyle: {
        padding: 2,
        borderTop: "1px solid " + darken(theme.palette.background.paper, 0.4),
        // fontWeight: 650,
        fontSize: 15,
        textAlign: "center",
        borderBottom: "none"
    }
  })
);

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 theme = useTheme()
    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 isMobile = 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, lastRow?: boolean) => {
        let tempDisplay = "--"
        let moistureDisplay = "--"
        let lvlTemp
        let lvlMoisture
        let tempColour: string = darken(theme.palette.background.paper, 0.1)
        let moistureColour: string = darken(theme.palette.background.paper, 0.1)

        //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() + 10)){
                tempColour = warning
            }else if(lvlTemp > bin.targetTemp() + 5){
                //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)
        }

        //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() + 0.5){
                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 className={classes.colouredCellStyle} padding="none" sx={{backgroundColor: tempColour}}>{tempDisplay}</TableCell>
                <TableCell className={classes.colouredCellStyle} padding="none" sx={{backgroundColor: moistureColour, borderBottomRightRadius: lastRow ? 8 : 0}}>{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 = (hideUnit?: boolean) => {
        if(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT){
            return tempTarget.toFixed(2) + (hideUnit ? "" : " °F")
        }
        return tempTarget.toFixed(2) + (hideUnit ? "" : " °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: isMobile ? "88%" : "100%" }}>
            {expandedTableDialog()}
            {/* Level Table */}
            <Box display="flex" justifyContent="space-between" alignItems="center">
              <Typography fontWeight={650}>Grain Table</Typography>
              <Box display="flex" alignItems="center" gap={1}>
                <Button 
                    variant="contained"
                    sx={{ background: theme.palette.background.default, color: theme.palette.text.primary}}
                    onClick={() => {
                        setOpenExpandedCables(true)
                    }}>
                        Show All Cables
                </Button>
                <Tooltip title="This table shows the bins levels using nodes that are in the grain averaged together. Data comes from the bins status. 
                    Use the button to see all of the cables individually.">
                    <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}>
  {/* "Targets" cell — hide on mobile when either field is being edited */}
  {!(isMobile && (enterTemp || enterMoisture)) && (
    <TableCell className={isMobile ? classes.mobileTargetCellStyle : classes.targetCellStyle} sx={{top: 0, position: "sticky", zIndex: 4, borderBottom: "none" }}>
      <Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
        <GppGood />
        {!isMobile &&
        <Typography fontSize={16} fontWeight={650}>
          Targets
        </Typography>
        }
      </Box>
    </TableCell>
  )}

  {/* Temperature cell */}
  {!(isMobile && enterMoisture) && (
  <TableCell
  className={isMobile ? classes.mobileTargetCellStyle : classes.targetCellStyle}
    colSpan={isMobile && enterTemp ? 3 : 1}
    sx={{ top: 0, position: "sticky", zIndex: 4, borderBottom: "none" }}
  >
    <Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
      <TemperatureIcon heightWidth={isMobile ? 25 : 30} />
      {enterTemp ? updateTempEntry() : (
        <Typography fontSize={isMobile ? 12 : 16}>
          {tempThreshDisplay(isMobile)}
        </Typography>
      )}
      {enterTemp ? (
        <IconButton onClick={() => { setEnterTemp(false); update(); }}>
          <Save />
        </IconButton>
      ) : (
        <IconButton onClick={() => setEnterTemp(true)}>
          <Edit />
        </IconButton>
      )}
    </Box>
  </TableCell>
  )}
  {/* Moisture cell — hide on mobile when temperature is being edited */}
  {!(isMobile && enterTemp) && (
    <TableCell
    className={isMobile ? classes.mobileTargetCellStyle : classes.targetCellStyle}
      colSpan={isMobile && enterMoisture ? 3 : 1}
      sx={{ top: 0, position: "sticky", zIndex: 4, borderBottom: "none" }}
    >
      <Box justifyContent={"center"} sx={{ display: "flex", alignItems: "center", gap: 1 }}>
        <HumidityIcon height={isMobile ? 25 : 30} width={20} />
        {enterMoisture ? updateMoistureEntry() : (
          <Typography fontSize={isMobile ? 12 : 16} sx={{ marginLeft: "10px" }}>
            {bin.targetMoisture()}
          </Typography>
        )}
        {enterMoisture ? (
          <IconButton onClick={() => { setEnterMoisture(false); update(); }}>
            <Save />
          </IconButton>
        ) : (
          <IconButton onClick={() => setEnterMoisture(true)}>
            <Edit />
          </IconButton>
        )}
      </Box>
    </TableCell>
  )}
</TableRow>
                    <TableRow>
                        <TableCell 
                            className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle} 
                            sx={{ 
                                top: firstRowHeight, 
                                position: "sticky", 
                                zIndex: 3,
                                borderBottom: "none",
                                borderTopLeftRadius: 8,
                                boxShadow: `-12px -12px 0px 4px ${theme.palette.background.paper}`
                            }}>
                                Level
                        </TableCell>
                        <TableCell 
                            className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle} 
                            sx={{
                                top: firstRowHeight, 
                                position: "sticky", 
                                zIndex: 3,
                                borderBottom: "none"
                              }}>
                            {isMobile ? "T" : "Temperature"}{(user.tempUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT ? " (°F)" : " (°C)")}
                        </TableCell>
                        <TableCell 
                            className={isMobile ? classes.mobileHeaderCellStyle : classes.headerCellStyle} 
                            sx={{
                                top: firstRowHeight, 
                                position: "sticky", 
                                zIndex: 3,
                                borderBottom: "none",
                                borderTopRightRadius: 8,
                                // fills the transparent corner area with the target row's background colour
                                boxShadow: `16px -16px 8px 12px ${theme.palette.background.paper}`
                            }}>
                                EMC (%)
                        </TableCell>
                    </TableRow>
                </TableHead>

                <TableBody>
                  {binLevels.map((level, i) => (
                      <TableRow key={i}>
                          <TableCell sx={{borderBottomLeftRadius: (i === binLevels.length-1 ? 8 : 0)}} padding="none" className={classes.defaultCellStyle}>{binLevels.length - i}</TableCell>
                          {levelDisplay(level, i === binLevels.length-1)}
                      </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 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"} (0.5%) above</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"} (1.5%) above</Typography>
                </Box>
            </Box>
        </Box>
    )
}