import { useCallback, useEffect, useState } from "react";
import PageContainer from "./PageContainer";
import CustomGrainForm from "grain/CustomGrainForm";
import { Accordion, AccordionDetails, AccordionSummary, Box, Button, DialogActions, DialogContent, DialogTitle, Divider, Grid2, IconButton, Typography } from "@mui/material";
import { pond } from "protobuf-ts/pond";
import ButtonGroup from "common/ButtonGroup";
import { useMobile, useSnackbar, useUserAPI } from "hooks";
import { useGrainAPI } from "providers/pond/grainAPI";
import ResponsiveTable from "common/ResponsiveTable";
import { GetGrainExtensionMap, GetValidGrainExtensions, GrainExtension } from "grain";
import ResponsiveDialog from "common/ResponsiveDialog";
import { useGlobalState } from "providers";
import { Scope } from "models";
import React from "react";
import { AddCircle, ExpandMore } from "@mui/icons-material";

export default function Grains() {
  const [currentCustomGrain, setCurrentCustomGrain] = useState<pond.GrainSettings>()
  const [formValid, setFormValid] = useState(false)
  const [selectedGrain, setSelectedGrain] = useState<pond.GrainObject | undefined>()
  const [displayCustom, setDisplayCustom] = useState(false)
  const { openSnack } = useSnackbar()
  const [tableSize, setTableSize] = useState(10)
  const [tablePage, setTablePage] = useState(0)
  const [tableTotal, setTableTotal] = useState(0)
  const grainAPI = useGrainAPI()
  const userAPI = useUserAPI()
  const [{as, user}] = useGlobalState();
  const [permissions, setPermissions] = useState<pond.Permission[]>([])
  const [openRemoveConfirmation, setOpenRemoveConfirmation] = useState(false)
  const [openUpdateConfirmation, setOpenUpdateConfirmation] = useState(false)
  const [grainObjectTableData, setGrainObjectTableData] = useState<pond.GrainObject[]>([])
  const [supportedGrainTableData, setSupportedGrainTableData] = useState<GrainExtension[]>([])
  // const [selectedGrainExtension, setSelectedGrainExtension] = useState<GrainExtension | undefined>()
  const [accordionKey, setAccordionKey] = useState("allClosed")
  const [openNewGrainDialog, setOpenNewGrainDialog] = useState(false)
  const isMobile = useMobile()
  const extraLoad = 10 //the amount of more to load on the mobile view when load more is clicked

  //function to load the first set of custom grains
  const loadCustomGrain = useCallback(() => {
    grainAPI.listGrains(tableSize, tablePage * tableSize, "asc", "name").then(resp => {
      setGrainObjectTableData(resp.data.grains)
      setTableTotal(resp.data.total)
    }).catch(err => {
      openSnack("There was an issue loading your custom grains")
    })
  },[tableSize, tablePage])

  useEffect(() => {
    //load the custom grains
    if(displayCustom){
      loadCustomGrain()
    }else{
      let displayedGrains: GrainExtension[] = []
      let offset = tablePage * tableSize
      let limit = tableSize
      let grainList = GetValidGrainExtensions()
      let sortedGrain = grainList.sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1))
      sortedGrain.forEach((ext, i) => {
        if(i >= offset && displayedGrains.length < limit){
          displayedGrains.push(ext)
        }
      })
      setSupportedGrainTableData(displayedGrains)
      setTableTotal(sortedGrain.length)
    }
  },[loadCustomGrain, tableSize, tablePage, displayCustom, as])
  
  useEffect(()=>{
    //if the user is viewing as a team get their permissions to the team, if they are not then we will get their permission to the grain type individually when a type is selected
    if(as){
      userAPI.getUser(user.id(), { key: as, kind: "team" } as Scope).then(resp => {
        setPermissions(resp.permissions);
      });
    }
  },[as, userAPI])

  const equationName = (enumVal: number) => {
    switch(enumVal){
      case pond.MoistureEquation.MOISTURE_EQUATION_CHUNG_PFOST:
        return "Chung-Pfost"
      case pond.MoistureEquation.MOISTURE_EQUATION_HALSEY:
        return "Halsey"
      case pond.MoistureEquation.MOISTURE_EQUATION_HENDERSON:
        return "Henderson"
      case pond.MoistureEquation.MOISTURE_EQUATION_OSWIN:
        return "Oswin"
      default:
        return "None"
    }
  }

  //function to update

  //table that will display the custom grains created by the user
  const grainObjectTable = () => {
    return (
      <ResponsiveTable<pond.GrainObject>
        rows={grainObjectTableData}
        noDataMessage="No Grain Types Found. Add a new custom grain to see them here"
        loadMore={() => {
          setTableSize(tableSize + extraLoad)
        }}
        renderMobile={(row) => <Accordion expanded={accordionKey === row.key} onChange={() => {
          //close it if it is already open
          if(accordionKey === row.key){
            setAccordionKey("allClosed")
          }else{
            setAccordionKey(row.key)
          }
          setCurrentCustomGrain(row.settings ?? pond.GrainSettings.create())
          }}>
          <AccordionSummary expandIcon={<ExpandMore />}>{row.name}</AccordionSummary>
          <AccordionDetails>
            {customGrainDisplay()}
          </AccordionDetails>
        </Accordion>}
        onRowClick={(grainObj) => {
          setSelectedGrain(grainObj)
          setFormValid(true)
          /** if they are viewin as a team leave the permissions alone, if not then use the users permissions to the grain type,
           * for the moment grain types are not shareable so this will be unused, but this will cover our bases if we want to implement sharing in the future
           * */
          if (!as) {
            userAPI.getUser(user.id(), { key: grainObj.key, kind: "grain" } as Scope).then(resp => {
              setPermissions(resp.permissions);
            });
          }
        }}
        columns={[
          {
            title: "Name",
            render: row => <Box padding={2}>
              <Typography>
                {row.name}
              </Typography>
            </Box>
          },          
          {
            title: "Group",
            render: row => <Box padding={2}>
              <Typography>
                {row.settings?.group}
              </Typography>
            </Box>
          },          
          {
            title: "Equation",
            render: row => <Box padding={2}>
              <Typography>
                {equationName(row.settings?.equation ?? 0)}
              </Typography>
            </Box>
          }
        ]}
        page={tablePage}
        pageSize={tableSize}
        handleRowsPerPageChange={(e) => {
          setTableSize(e.target.value)
        }}
        setPage={(page) => {
          setTablePage(page)
        }}
        total={tableTotal}
      />
    )
  }

  //table that will display the grain extensions provided by us
  const supportedGrainTable = () => {
    return (
      <ResponsiveTable<GrainExtension>
        rows={supportedGrainTableData}
        page={tablePage}
        // onRowClick={(ext) => {setSelectedGrainExtension(ext)}}
        loadMore={() => {
          setTableSize(tableSize + extraLoad)
        }}
        renderMobile={(row) => <Accordion>
          <AccordionSummary expandIcon={<ExpandMore />}>{row.name}</AccordionSummary>
          <AccordionDetails>
            {supportedDisplay(row)}
          </AccordionDetails>
        </Accordion>}
        pageSize={tableSize}
        columns={[
          {
            title: "Name",
            render: row => <Box padding={2}>
              <Typography>
                {row.name}
              </Typography>
            </Box>
          },          
          {
            title: "Group",
            render: row => <Box padding={2}>
              <Typography>
                {row.group}
              </Typography>
            </Box>
          },          
          {
            title: "Equation",
            render: row => <Box padding={2}>
              <Typography>
                {equationName(row.equation ?? 0)}
              </Typography>
            </Box>
          },
          {
            title: "Kg Per Bushel",
            render: row => <Box padding={2}>
              <Typography>
                {row.weightConversionKg.toFixed(2)}
              </Typography>
            </Box>
          },
          {
            title: "Bushels Per Tonne",
            render: row => <Box padding={2}>
              <Typography>
                {row.bushelsPerTonne.toFixed(2)}
              </Typography>
            </Box>
          }
        ]}
        handleRowsPerPageChange={(e) => {
          setTableSize(e.target.value)
        }}
        setPage={(page) => {
          setTablePage(page)
        }}
        total={tableTotal}
      />
    )
  }

  //the grain display for supported grain type to show the rest of the data such as what constants are used in the formula
  const supportedDisplay = (grain?: GrainExtension) => {
    return (
      <Box>
      {grain ?
        <React.Fragment>
          <Typography>
            Info
          </Typography>
          <Divider />
          <Box padding={2}>
            <Typography>Name: {grain.name}</Typography>
            <Typography>Group: {grain.group}</Typography>
          </Box>
          <Typography>
            Formula
          </Typography>
          <Divider />
          <Box padding={2}>
            <Typography>Equation: {equationName(grain.equation)}</Typography>
            {/* <Typography>Constant A: {grain.a}</Typography>
            <Typography>Constant B: {grain.b}</Typography>
            <Typography>Constant C: {grain.c}</Typography> */}
          </Box>
          <Typography>
            Conversions
          </Typography>
          <Divider />
          <Box padding={2}>
            <Typography>Kg per Bushel: {grain.weightConversionKg.toFixed(2)}</Typography>
            <Typography>Bushels per Tonne: {grain.bushelsPerTonne.toFixed(2)}</Typography>
          </Box>
        </React.Fragment>
        :
        <Typography>Select a Grain from the table to view its details</Typography>
      }
      </Box>
    )
  }

  const updateGrain = () => {
    if(selectedGrain && currentCustomGrain){
      grainAPI.updateGrain(selectedGrain.key, currentCustomGrain).then(resp => {
        openSnack("Grain type updated")
      }).catch(err => {
        openSnack("There was an issue updating the selected grain type")
      }).finally(() => {
        setOpenUpdateConfirmation(false)
        loadCustomGrain()
      })
    }
  }
  const updateGrainConfirmation = () => {
    return (
      <ResponsiveDialog open={openUpdateConfirmation} onClose={() => {setOpenUpdateConfirmation(false)}}>
        <DialogTitle>Update Custom Grain</DialogTitle>
        <DialogContent>
          Update existing custom grain type?
          <br/> 
          Note that updating the grain here will not change any bins or components this type was used for, those will retain the old settings.
        </DialogContent>
        <DialogActions>
          <Button variant="contained" color="primary" onClick={()=>{setOpenUpdateConfirmation(false)}}>Cancel</Button>
          <Button variant="contained" color="primary" onClick={()=>{updateGrain()}}>Confirm</Button>
        </DialogActions>
      </ResponsiveDialog>
    )
  }

  //because the form is embedded on desktop this dialog is specifically for mobile view
  const newGrainDialog = () => {
    return (
      <ResponsiveDialog open={openNewGrainDialog} onClose={() => setOpenNewGrainDialog(false)}>
        <DialogTitle>Add New Grain</DialogTitle>
        <DialogActions>
          {customGrainDisplay()}

        </DialogActions>
      </ResponsiveDialog>
    )
  }

  const addNewGrain = () => {
    if(currentCustomGrain){
      grainAPI.addGrain(currentCustomGrain).then(resp => {
        //new grain added
        openSnack("New grain type added")
      }).catch(err => {
        openSnack("There was a problem creating the new grain type")
      }).finally(() => {
        loadCustomGrain()
        setOpenNewGrainDialog(false)//only for the mobile view since the desktop has it embedded
      })
    }
  }

  const removeGrain = () => {
    if(selectedGrain){
      grainAPI.removeGrain(selectedGrain.key).then(resp => {
        openSnack("Grain type removed")
      }).catch(err => {
        openSnack("There was a problem removing the selected grain type")
      }).finally(() => {
        setOpenRemoveConfirmation(false)
        loadCustomGrain()
      })
    }
  }


  const removeConfirmation = () => {
    return (
      <ResponsiveDialog open={openRemoveConfirmation} onClose={() => {setOpenRemoveConfirmation(false)}}>
        <DialogTitle>Remove Custom Grain</DialogTitle>
        <DialogContent>
          Remove custom grain type?
          <br />
          Note that removing the grain here will not change any bins or components this type was used for, those will retain the old settings.
        </DialogContent>
        <DialogActions>
          <Button variant="contained" color="primary" onClick={()=>{setOpenRemoveConfirmation(false)}}>Cancel</Button>
          <Button variant="contained" color="primary" onClick={()=>{removeGrain()}}>Confirm</Button>
        </DialogActions>
      </ResponsiveDialog>
    )
  }

  const customGrainActions = () => {
    return (
        <Box display="flex" justifyContent="space-between">
          <Box>
            {selectedGrain &&
              <Button disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)} variant="contained" color="error" onClick={() => {setOpenRemoveConfirmation(true)}}>Remove Grain</Button>
            }
          </Box>
          <Box>
            {selectedGrain &&
              <Button sx={{marginRight: 1}} onClick={() => setOpenUpdateConfirmation(true)} disabled={!formValid || !permissions.includes(pond.Permission.PERMISSION_WRITE)} variant="contained" color="primary">Update</Button>
            }
            {isMobile && !selectedGrain && <Button onClick={() => {setOpenNewGrainDialog(false)}}>Cancel</Button>}
            <Button onClick={() => addNewGrain()} disabled={!formValid || (as !== "" && !permissions.includes(pond.Permission.PERMISSION_WRITE))} variant="contained" color="primary">Save New</Button>
          </Box>
        </Box>
    )
  }

  //the grain display for custom grain the user has defined
  const customGrainDisplay = () => {
    return (
      <Box>
        <CustomGrainForm 
          grainSettings={selectedGrain?.settings || undefined}
          onGrainSettingsChange={(newSettings, formValid)=>{
            setCurrentCustomGrain(newSettings)
            setFormValid(formValid)
          }}
          />
        {customGrainActions()}
      </Box>
    )
  }

  const desktopView = () => {
    if(displayCustom){
      return (
      <Grid2 container>
        <Grid2 size={7} padding={2}>
          {grainObjectTable()}
        </Grid2>
        <Grid2 size={5} padding={2}>
          {customGrainDisplay()}
        </Grid2>
      </Grid2>
      )
    }else{
      return (
        <Box marginTop={2}>
          {supportedGrainTable()}
        </Box>
      )
    }
  }

  const mobileView = () => {
    return (
      <Box>
        {displayCustom ? grainObjectTable() : supportedGrainTable()}
      </Box>
    )
  }

  return (
    <PageContainer>
      <Box padding={2}>
      <Typography variant="h4">
        Adaptive Grains
      </Typography>
      <Box display="flex" marginTop={2} justifyContent="space-between">
        <ButtonGroup 
          toggle
          buttons={[
            {
              title: "Supported",
              function: () => {
                setDisplayCustom(false)
                setTablePage(0)
              }
            },
            {
              title: "Custom",
              function: () => {
                setDisplayCustom(true)
                setTablePage(0)
              }
            }
          ]}
        />
        {isMobile && displayCustom &&
          <IconButton sx={{padding: 0}} color="primary" onClick={()=>{
            setSelectedGrain(undefined)
            setCurrentCustomGrain(undefined)
            setOpenNewGrainDialog(true)
          }}>
            <AddCircle />
          </IconButton>
        }
      </Box>
      {!isMobile ? desktopView() : mobileView()}
      </Box>
      {updateGrainConfirmation()}
      {removeConfirmation()}
      {newGrainDialog()}
    </PageContainer>
  )
}