import ResponsiveDialog from "common/ResponsiveDialog";
import { Bin, Field, HarvestYear } from "models";
import React, { useCallback } from "react";
import { useFieldAPI, useBinAPI, useHarvestYearAPI, useGlobalState } from "providers";
import {
  Box,
  Button,
  DialogActions,
  DialogContent,
  DialogTitle,
  FormControl,
  FormControlLabel,
  MenuItem,
  Radio,
  RadioGroup,
  TextField
} from "@mui/material";
import { useState } from "react";
import { useEffect } from "react";
import { pond } from "protobuf-ts/pond";
import { useSnackbar } from "hooks";
import moment from "moment";
import GrainDescriber from "./GrainDescriber";

interface Props {
  activeBinSettings: pond.BinSettings;
  newGrainInventory: number;
  dialogControl: boolean;
  onClose(cancel?: boolean): void;
  afterUpdate?(secondBin?: Bin): void;
}

//this component is going to be deprecated by grain transaction
export default function GrainInventory(props: Props) {
  const { activeBinSettings, newGrainInventory, dialogControl, onClose, afterUpdate } = props;
  const binAPI = useBinAPI();
  const fieldAPI = useFieldAPI();
  const hYearAPI = useHarvestYearAPI();
  const [{ as }] = useGlobalState();
  const [binIndex, setBinIndex] = useState(0);
  const [fieldIndex, setFieldIndex] = useState<number>(-1);
  const [yearIndex, setYearIndex] = useState(0);
  const [bins, setBins] = useState<Bin[]>([]);
  const [fields, setFields] = useState<Field[]>([]);
  const [hYears, setHYears] = useState<Map<number, HarvestYear>>(new Map<number, HarvestYear>());
  const [currentBushels, setCurrentBushels] = useState(0);
  const { openSnack } = useSnackbar();
  const [increaseMode, setIncreaseMode] = useState<"correction" | "purchased" | "grown">(
    "correction"
  );
  const [decreaseMode, setDecreaseMode] = useState<"correction" | "sold" | "moved">("correction");

  useEffect(() => {
    if (activeBinSettings.inventory && dialogControl) {
      setCurrentBushels(activeBinSettings.inventory.grainBushels);
    }
  }, [activeBinSettings, dialogControl]);

  const loadBins = useCallback(() => {
    if (!dialogControl) return;
    binAPI
      .listBins(50, 0, "asc", "name", undefined, as)
      .then(resp => {
        setBins(resp.data.bins.map(b => Bin.any(b)));
      })
      .catch();
  }, [binAPI, as, dialogControl]);

  const loadHYears = useCallback(() => {
    if (fields.length > 0 && fieldIndex > -1) {
      hYearAPI.listHarvestYears(50, 0, "asc", "year", fields[fieldIndex].fieldName()).then(resp => {
        let tempMap = new Map<number, HarvestYear>();
        resp.data.harvestYear.forEach(hy => {
          let tempHY = HarvestYear.any(hy);
          tempMap.set(tempHY.year(), tempHY);
        });
        setHYears(tempMap);
      });
    }
  }, [hYearAPI, fields, fieldIndex]);

  useEffect(() => {
    loadHYears();
  }, [loadHYears]);

  useEffect(() => {
    loadBins();
    fieldAPI
      .listFields(500, 0, "asc", "fieldName", undefined, as)
      .then(resp => {
        setFields(resp.data.fields.map(f => Field.any(f)));
      })
      .catch();
  }, [loadBins, fieldAPI, as]);

  const grainName = (bin: Bin) => {
    return bin.storage() === pond.BinStorage.BIN_STORAGE_SUPPORTED_GRAIN
      ? GrainDescriber(bin.grain()).name
      : bin.customType();
  };

  const dialogContent = () => {
    if (newGrainInventory < currentBushels) {
      return (
        <Box>
          <FormControl>
            <RadioGroup value={decreaseMode} name="radio-buttons-group">
              <FormControlLabel
                value="correction"
                control={<Radio />}
                label="Correction"
                onChange={() => setDecreaseMode("correction")}
              />
              {/* Doesnt Actually Do Anything yet */}
              {/* <FormControlLabel
                value="sold"
                control={<Radio />}
                label="Sold"
                onChange={() => setDecreaseMode("sold")}
              /> */}
              <FormControlLabel
                value="moved"
                control={<Radio />}
                label="Moved"
                onChange={() => setDecreaseMode("moved")}
              />
            </RadioGroup>
          </FormControl>
          {decreaseMode === "moved" && (
            <TextField
              id="bins"
              fullWidth
              margin="normal"
              select
              label="Bin grain moved to"
              value={binIndex}
              onChange={e => setBinIndex(+e.target.value)}>
              {bins.map((option, i) => (
                <MenuItem key={i} value={i} disabled={option.key() === activeBinSettings.key}>
                  {option.name()} - {grainName(option)}
                </MenuItem>
              ))}
            </TextField>
          )}
        </Box>
      );
    } else {
      return (
        <Box>
          <FormControl>
            <RadioGroup value={increaseMode} name="radio-buttons-group">
              <FormControlLabel
                value="correction"
                control={<Radio />}
                label="Correction"
                onChange={() => setIncreaseMode("correction")}
              />
              {/* Doesnt Actually Do Anything yet */}
              {/* <FormControlLabel
                value="purchased"
                control={<Radio />}
                label="Purchased"
                onChange={() => setIncreaseMode("purchased")}
              /> */}
              <FormControlLabel
                value="grown"
                control={<Radio />}
                label="Grown"
                onChange={() => setIncreaseMode("grown")}
              />
            </RadioGroup>
          </FormControl>
          {increaseMode === "grown" && (
            <Box>
              <TextField
                id="fields"
                fullWidth
                margin="normal"
                select
                label="Field"
                helperText="Select the field that provided the grain"
                value={fieldIndex}
                onChange={e => setFieldIndex(+e.target.value)}>
                <MenuItem key="noField" value={-1}>
                  No Field Selected
                </MenuItem>
                {fields.map((option, i) => (
                  <MenuItem key={i} value={i}>
                    {option.fieldName()}
                  </MenuItem>
                ))}
              </TextField>
              <TextField
                id="harvestYear"
                fullWidth
                margin="normal"
                select
                label="Harvest Year"
                helperText="Select which Year to track"
                value={yearIndex}
                onChange={e => setYearIndex(+e.target.value)}
                disabled={fieldIndex < 0}>
                <MenuItem key={0} value={0}>
                  New Harvest Year
                </MenuItem>
                {Array.from(hYears.values()).length > 0 &&
                  Array.from(hYears.values()).map(option => (
                    <MenuItem key={option.year()} value={option.year()}>
                      {option.year()}
                    </MenuItem>
                  ))}
              </TextField>
            </Box>
          )}
        </Box>
      );
    }
  };

  const increaseInv = () => {
    let bin = activeBinSettings;
    let binInv = pond.BinInventory.fromObject({
      ...bin.inventory
    });
    if (binInv.grainBushels === 0 && bin.mode === pond.BinMode.BIN_MODE_NONE) {
      bin.mode = pond.BinMode.BIN_MODE_STORAGE;
    }
    binInv.grainBushels = newGrainInventory;

    if (fields.length > 0 && fieldIndex > -1) {
      if (fields[fieldIndex].crop() !== activeBinSettings.inventory?.grainType) {
        openSnack("Grain type of this bin does not match the Field");
        return;
      } else if (yearIndex === 0 && hYears.has(moment().year())) {
        openSnack("Current year already exists for this field");
        return;
      }
      binInv.grainSubtype = fields[fieldIndex].subtype();
    }

    bin.inventory = binInv;

    binAPI
      .updateBin(activeBinSettings.key, bin)
      .then(() => {
        if (increaseMode === "grown") {
          if (yearIndex === 0) {
            let hy = HarvestYear.create();
            hy.settings.field = fields[fieldIndex].fieldName();
            hy.settings.grain = fields[fieldIndex].crop();
            hy.settings.year = moment().year();
            hy.settings.bushels = newGrainInventory - currentBushels;
            hYearAPI.addHarvestYear(hy.settings).then(resp => {
              loadHYears();
            });
          } else {
            let hy = hYears.get(yearIndex);
            if (hy) {
              hy.settings.bushels = hy.totalBushels() + (newGrainInventory - currentBushels);
              hYearAPI.updateHarvestYear(hy.key(), hy.settings).then(resp => {
                loadHYears();
              });
            }
          }
        } else if (increaseMode === "purchased") {
        }
      })
      .catch(err => {
        openSnack("Failed to update grain inventory");
      })
      .finally(() => {
        if (afterUpdate) {
          loadBins();
          afterUpdate();
        }
        closeDialog();
      });
  };

  const decreaseInv = () => {
    let binFrom = activeBinSettings;
    let binTo: Bin;
    let binFromInv = pond.BinInventory.fromObject({
      ...binFrom.inventory
    });

    if (decreaseMode === "moved") {
      //TODO-CS: re-think the logic for moving between bins since the addition of custom types,
      //possibly remove restrictions and just have a confirmation for the user to change the destination bins grain type
      binTo = bins[binIndex];
      if (binTo.settings.inventory && binTo.settings.specs) {
        let binToInv = binTo.settings.inventory;
        //check to make sure the grain types of both bins are the same
        if (binFromInv.grainType !== binToInv.grainType) {
          if (
            binToInv.grainType === pond.Grain.GRAIN_NONE ||
            binToInv.grainType === pond.Grain.GRAIN_INVALID ||
            binToInv.grainBushels < 1
          ) {
            binTo.settings.inventory.grainType = binFromInv.grainType;
          } else {
            openSnack("Grain types of both bins must match");
            return;
          }
        }
        //check if the other bin has enough space to move the grain
        let space = binTo.settings.specs.bushelCapacity - binTo.settings.inventory.grainBushels;
        if (space < currentBushels - newGrainInventory) {
          openSnack("Bin does not have enough space");
          return;
        }
        binToInv.grainBushels = binToInv.grainBushels + (currentBushels - newGrainInventory);
      }
    }

    binFromInv.grainBushels = newGrainInventory;
    binFrom.inventory = binFromInv;

    binAPI
      .updateBin(activeBinSettings.key, binFrom)
      .then(() => {
        //update inventory of the bin that the grain was moved to if it was a decrease
        if (decreaseMode === "moved") {
          binAPI
            .updateBin(bins[binIndex].key(), binTo.settings)
            .then(() => {
              openSnack("Grain inventory updated");
            })
            .catch(err => {
              openSnack("Failed to update grain inventory");
            })
            .finally(() => {
              if (afterUpdate) {
                afterUpdate(binTo);
              }
            });
        }
      })
      .catch(err => {
        openSnack("Failed to update grain inventory");
      })
      .finally(() => {
        if (decreaseMode !== "moved" && afterUpdate) {
          afterUpdate();
        }
        closeDialog();
      });
  };

  const setNewInventory = () => {
    if (currentBushels > newGrainInventory) {
      decreaseInv();
    } else {
      increaseInv();
    }
  };

  const closeDialog = (useCallback?: boolean) => {
    onClose(useCallback);
  };

  const disableButton = () => {
    if (increaseMode === "grown") {
      if (fields.length === 0 || fieldIndex < 0) {
        return true;
      }
    }
    return false;
  };

  return (
    <ResponsiveDialog open={dialogControl} onClose={() => closeDialog(true)}>
      <DialogTitle>Grain Adjustment for {activeBinSettings.name}</DialogTitle>
      <DialogContent>{dialogContent()}</DialogContent>
      <DialogActions>
        <Button onClick={() => closeDialog(true)} color="primary">
          Cancel
        </Button>
        <Button onClick={setNewInventory} color="primary" disabled={disableButton()}>
          Confirm
        </Button>
      </DialogActions>
    </ResponsiveDialog>
  );
}
