import { forwardRef, useImperativeHandle, useState, useEffect } from "react";
import {
  Box,
  FormControl,
  FormControlLabel,
  Grid,
  TextField,
  InputLabel,
  Select,
  Switch,
} from "@material-ui/core";
import api from "../../../services/vehiclesAPI";
import TaskRunner from "../../../utils/taskRunner";
import { AllECUsCommand } from "../../Controls/SendDiagnosticCommand";
import useStyles from "../../useStyles";
import { useStatusContext } from "../../Contexts/StatusContext";
import { useUserContext } from "../../Contexts/UserContext";
import unionIntersect from "../../../utils/unionIntersect";

const DIAGNOSTIC_COMMANDS = [
  { displayname: "Reset", val: "remote_reset", ecu_name: true },
  { displayname: "Set CAN Network State", val: "can_network", state: "can_net_action" },
  { displayname: "Set Remote Ignition", val: "remote_ignition", state: "can_net_action" },
  { displayname: "Read ECU versions", val: "read_ecu_versions", allowAll: true, ecu_name: true },
];

const DEFAULT_SECONDS = 180;

async function getECUsByVINs(vins, token) {
  return new Promise((resolve, reject) => {
    const taskRunner = new TaskRunner(30, vins.length);

    const task = (vin) => {
      return async () => api.getECUs({ vin, unique: true }, token)
        .then((result) => {
          if (result.total === 0) {
            reject([]);
          }
          return result.data.map(({ ecu }) => ecu);
        })
        .catch(() => reject([]));
    }

    vins.forEach((vin) => {
      taskRunner.push(task(vin));
    });

    taskRunner.onComplete().then((results) => {
      const ecus = unionIntersect(...results);
      resolve(ecus.map(ecu => ({ ecu })));
    });
  });
}

export default forwardRef(({
  ids,
  idCSV,
}, ref) => {
  const [ecus, setECUs] = useState([{ ecu: "TBOX" }]);
  const [currentECU, setCurrentECU] = useState("");
  const [validateECUs, setValidateECUs] = useState(false);
  const [command, setCommand] = useState(DIAGNOSTIC_COMMANDS[0]);
  const [checked, setChecked] = useState(false);
  const [seconds, setSeconds] = useState(DEFAULT_SECONDS);
  const classes = useStyles();
  const { setMessage } = useStatusContext();
  const { token: { idToken: { jwtToken: token } } } = useUserContext();

  useImperativeHandle(ref, () => ({
    async submit() {
      if (!validateECUs) {
        return Promise.reject("Invalid ECUs found, cannot submit");
      }

      return api.sendDiagnosticCommand(ids, createDiagnosticPayload(command, checked, currentECU, seconds), token)
        .then(() => {
          setMessage(`Sent ${command.val} command to ${ids.length} vehicles.`);
        })
        .catch(() => {
          setMessage(`Failed to send ${command.val} command.`);
        });
    }
  }));

  const handleSelectCommand = (e) => {
    setCommand(DIAGNOSTIC_COMMANDS[e.target.value]);
  };

  const handleSwitch = (e) => {
    setChecked(e.target.checked);
  }

  const handleSecondsChange = (e) => {
    const value = Number(e.target.value);
    if (value > 0) {
      setSeconds();
    }
  }

  useEffect(() => {
    async function fetchData() {
      setValidateECUs(false);
      const ecus = await getECUsByVINs(ids, token);
      setECUs(() => [{ ecu: "TBOX" }, ...ecus]); // TBOX is a hardcoded ECU
    }
    fetchData();
  }, [ids, token]);

  useEffect(() => {
    if (!checked) {
      setSeconds(DEFAULT_SECONDS);
    }
  }, [checked, setSeconds]);

  useEffect(() => {
    setChecked(false);
  }, [command, setChecked]);

  useEffect(() => {
    setValidateECUs(true);
    setCurrentECU(ecus[0].ecu);
  }, [ecus]);

  return (
    <Box minHeight="185px">
      <p>
        Attempt to send a vehicle diagnostic command to the following VINs: {idCSV}.
      </p>

      <FormControl className={classes.formControl} variant="outlined" size="small" margin="normal">
        <InputLabel htmlFor="send-command" className={classes.whiteBackground}>
          Diagnostic Command
        </InputLabel>
        <Select native variant="outlined"
          inputProps={{
            name: "send-command",
            id: "send-command",
          }}
          onChange={handleSelectCommand}
        >
          {DIAGNOSTIC_COMMANDS.map((command, index) => (
            <option key={command.val} value={index}>
              {command.displayname}
            </option>
          ))}
        </Select>
      </FormControl>

      {["remote_reset", "read_ecu_versions"].includes(command.val) && <AllECUsCommand
        classes={classes}
        ecus={command.allowAll ? [...ecus, { ecu: "*" }] : ecus}
        currentECU={currentECU}
        setCurrentECU={setCurrentECU}
      />}

      {["can_network", "remote_ignition"].includes(command.val) && (
        <Grid container direction="row" alignItems="flex-end" >
          <div>
            <FormControlLabel
              label="State"
              labelPlacement="end"
              control={<Switch checked={checked} onChange={handleSwitch} />}
            />
          </div>
          <TextField
            label="Seconds"
            type="number"
            onChange={handleSecondsChange}
            value={seconds}
            disabled={!checked}
            inputProps={{
              min: "0",
              step: "10",
            }}
          />
        </Grid>
      )}
    </Box>
  );
});

const hasOwnProperty = (obj, key) => {
  return Object.prototype.hasOwnProperty.call(obj, key);
}

const createDiagnosticPayload = (command = {}, checked, ecu, seconds) => {
  const payload = {
    command: command.val,
  };

  if (hasOwnProperty(command, "ecu_name")) {
    payload.ecu_name = ecu;
  }

  if (hasOwnProperty(command, "state")) {
    payload[command.state] = checked ? "on" : "off";
    payload.timeout = seconds;
  }

  return payload;
}