import { Box, Button, DialogActions, DialogContent, DialogTitle } from "@mui/material";
import ResponsiveDialog from "common/ResponsiveDialog";
import { Bin, Component, Device, Interaction } from "models";
import { pond } from "protobuf-ts/pond";
import React, { useEffect, useState } from "react";
import StorageMode from "./StorageMode";
import DryingMode from "./DryingMode";
import HydratingMode from "./HydratingMode";
import CooldownMode from "./CooldownMode";
import { PromiseProgress, Stage, Step } from "common/PromiseProgress";
import { useBinAPI, useComponentAPI, useInteractionsAPI } from "providers";
import { ComponentSet } from "bin/conditioning/pickComponentSet";
import { cloneDeep } from "lodash";

export interface DeviceChangeData {
    deviceId: number
    toRemove: Interaction[]
    toAdd?: pond.MultiInteractionSettings
    componentSets: ComponentSet[]
}

interface Props {
    newMode: pond.BinMode //used to control which dialog to open
    open: boolean
    onClose: () => void
    bin: Bin
    binPrefs?: Map<string, pond.BinComponentPreferences>
    devices: Device[]
    componentDevices: Map<string, number>
    componentMap: Map<string, Component>
    modeUpdated?: (mode: pond.BinMode) => void
}

export default function BinModeController(props: Props) {
    const {open, onClose, newMode, devices, componentDevices, componentMap, binPrefs, bin, modeUpdated} = props
    const [showProgress, setShowProgress] = useState(false)
    const [deviceComponents, setDeviceComponents] = useState<Map<number, Component[]>>(new Map())
    const [promiseStages, setPromiseStages] = useState<Stage[]>([])
    const interactionAPI = useInteractionsAPI()
    const componentAPI = useComponentAPI()
    const binAPI = useBinAPI();

    //the use effects for the initial setup of what will be needed
    useEffect(()=>{
        let newMap:Map<number, Component[]> = new Map()
        componentMap.forEach((comp, key) => {
            //first we need to get the device id this component belongs to
            let dev = componentDevices.get(key)
            if(dev){
                //check if the key exists in the new map yet
                if(newMap.has(dev)){
                    newMap.get(dev)?.push(comp)
                }else{
                    newMap.set(dev, [comp])
                }
            }
        })
        setDeviceComponents(newMap)
    },[componentDevices, componentMap])

    //steps involved for devices when changing the bin mode:
    //remove conflicting interactions
    //add new interactions
    //updating the controllers

    const closeDialog = () => {
        setShowProgress(false)
        onClose()
    }

    const buildStages = (deviceData: DeviceChangeData[]) => {
      //build the stages to pass into the PromiseProgress
      //stage one is to remove conflicting interactions
      let stages: Stage[] = []
      let stage1: Stage = {
        title: "Remove Interactions",
        steps: []
      }
      let stage2: Stage = {
        title: "Add New Interactions",
        steps: []
      }
      let stage3: Stage = {
        title: "Update Controllers",
        steps: []
      }
      deviceData.forEach(data => {
        data.toRemove.forEach(interaction => {
            let newStep: Step = {
                title: "Removing Interaction",
                promise: () => interactionAPI.removeInteraction(data.deviceId, interaction.key())
              }
              stage1.steps.push(newStep);
        })
        let newInteractions = data.toAdd
        if(newInteractions){
            stage2.steps.push({
                title: "Adding Interactions",
                promise: () => interactionAPI.addMultiInteractions(data.deviceId, newInteractions)
            })
        }
        data.componentSets.forEach(set => {
            set.controllers.forEach(controller => {
              let newStep: Step = {
                title: "Updating " + controller.name(),
                promise: () => componentAPI.update(data.deviceId, controller.settings)
              }
              stage3.steps.push(newStep)
            })
          })
      })
      if(stage1.steps.length > 0){
        stages.push(stage1)
      }
      if(stage2.steps.length > 0){
        stages.push(stage2)
      }
      if(stage3.steps.length > 0){
        stages.push(stage3)
      }
      //stage 4 is to update the bin with the new mode
      let stage4: Stage = {
        title: "Update Bin With New Mode",
        steps: [
            {
                title: "Update " + bin.name(), 
                promise: () => {
                  let clone = cloneDeep(bin.settings)
                  clone.mode = newMode
                  //this will set the bin to use the auto top nodes for cables when the bin is going into storage mode, and turn them off if the bin is being conditioned
                  if(newMode === pond.BinMode.BIN_MODE_STORAGE){
                    clone.autoGrainNode = true
                  }else{
                    clone.autoGrainNode = false
                  }
                  return binAPI.updateBin(bin.key(), clone)
                },
                onComplete: () => modeUpdated?.(newMode)
            }
        ]
      }
      stages.push(stage4)
      
      //set those stages to a state variable
      setPromiseStages(stages)
    }

    /**
     * the progress step is the last step of each mode change that shows what is being done and what succeeded/failed
     * initially i was going to put it here because it would effectively be the same thing for each mode, however depending
     * on how i decide to handle the api calls, and since i am thinking about passing the functions into the children it may go into each mode component
     * 
     */
    const progressContent = () => {
        return (
            <React.Fragment>
                <DialogTitle>Change Mode</DialogTitle>
                <DialogContent>
                  <PromiseProgress 
                    stages={promiseStages}
                    description="These are the changes that will occur in order to change your bin mode with the given options. Press start to begin."
                    failFast 
                    onStart={() => {
                      //this is just a function that is rin as soo as the start button is clicked it can be used to disable things while the change is in progress if we want to
                    }}
                    onComplete={()=>{
                      closeDialog()
                    }}
                  /> 
                </DialogContent>
                <DialogActions>
                    <Button onClick={closeDialog}>Close</Button>
                </DialogActions>
            </React.Fragment>
        )
    }

    const modeContent = () => {
        switch(newMode){
            case pond.BinMode.BIN_MODE_DRYING:
                //setNumSteps(DryingStepCount)
                return <DryingMode 
                    grain={bin.settings.inventory?.grainType}
                    devices={devices}
                    binPrefs={binPrefs}
                    deviceComponents={deviceComponents}
                    cancel={closeDialog}
                    confirm={(deviceData) => {
                        buildStages(deviceData)
                        setShowProgress(true)
                    }}
                />
            case pond.BinMode.BIN_MODE_HYDRATING:
                return <HydratingMode />
            case pond.BinMode.BIN_MODE_COOLDOWN:
                return <CooldownMode />
            default:
                return <StorageMode devices={devices} confirm={()=>{}}/>
        }
    }

    return (
        <ResponsiveDialog open={open} onClose={closeDialog}>
            {showProgress ? progressContent() : modeContent()}
        </ResponsiveDialog>
    )
}