import React, { useContext, useState } from "react";
import { logger } from "../../services/monitoring";
import api from "../../services/vehiclesAPI";

const VehicleContext = React.createContext();

const validateAdd = (vehicle) => {
  if (vehicle === null) {
    throw new Error("No vehicle data");
  }

  if (!vehicle.vin || vehicle.vin.length === 0) {
    throw new Error("VIN required");
  }

  if (vehicle.vin.length > 17) {
    throw new Error("VIN cannot be larger than 17 characters");
  }

  if (!vehicle.model || vehicle.model.length === 0) {
    throw new Error("model required");
  }

  if (!vehicle.year || vehicle.year < 2000 || vehicle.year > 9999) {
    throw new Error("year required");
  }
};

export const VehicleProvider = ({ children }) => {
  const [busy, setBusy] = useState(false);
  const [vehicles, setVehicles] = useState([]);
  const [totalVehicles, setTotalVehicles] = useState(0);
  const [models, setModels] = useState([]);
  const [years, setYears] = useState([]);

  const addConnections = async (cars, token) => {
    try {
      const vins = cars.map((car) => car.vin);
      const result = await api.getConnections(vins, token);

      if (result.error) {
        throw new Error(`Add connections error. ${result.message}`);
      }
      cars.forEach((car) => {
        car.connected = result[car.vin] || false;
      });
    } catch (e) {
      logger.error(e.stack);
    }
  };

  const addVehicle = async (vehicle, token) => {
    try {
      setBusy(true);
      validateAdd(vehicle);
      const result = await api.addVehicle(vehicle, token);
      if (result.error) throw new Error(`Add vehicle error. ${result.message}`);
      return result;
    } finally {
      setBusy(false);
    }
  };

  const getConnections = async (vins, token) => {
    try {
      setBusy(true);
      const result = await api.getConnections(vins, token);
      if (result.error)
        throw new Error(`Get connections error. ${result.message}`);
      return result;
    } finally {
      setBusy(false);
    }
  };

  const getECUs = async (search, token) => {
    try {
      setBusy(true);
      const result = await api.getECUs(search, token);
      if (result.error) throw new Error(`Get ECUs error. ${result.message}`);
      return result;
    } finally {
      setBusy(false);
    }
  };

  const getLocations = async (token) => {
    try {
      setBusy(true);
      const result = await api.getLocations(token);
      if (result.error)
        throw new Error(`Get locations error. ${result.message}`);
      return result;
    } finally {
      setBusy(false);
    }
  };

  const getModels = async (token) => {
    try {
      setBusy(true);
      const result = await api.getModels(token);
      if (result.error) throw new Error(`Get models error. ${result.message}`);
      setModels(result.data);
    } finally {
      setBusy(false);
    }
  };

  const getState = async (token, vin) => {
    try {
      setBusy(true);
      const result = await api.getState(token, vin);
      if (result.error) throw new Error(`Get state error. ${result.message}`);
      return result;
    } finally {
      setBusy(false);
    }
  };

  const getVehicles = async (search, token) => {
    try {
      setBusy(true);
      const result = await api.getVehicles(search, token);
      if (result.error) {
        setVehicles([]);
        throw new Error(`Get vehicles error. ${result.message}`);
      }
      await addConnections(result.data, token);
      setVehicles(result.data);
      if (result.total) {
        setTotalVehicles(result.total);
      }
    } finally {
      setBusy(false);
    }
  };

  const getYears = async (token) => {
    try {
      setBusy(true);
      const result = await api.getYears(token);
      if (result.error) throw new Error(`Get years error. ${result.message}`);
      setYears(result.data);
    } finally {
      setBusy(false);
    }
  };

  const sendCommand = async (vins, command, parameters, token) => {
    try {
      setBusy(true);
      const result = await api.sendCommand(vins, command, parameters, token);
      if (result.error)
        throw new Error(`Send command error. ${result.message}`);
      return result;
    } finally {
      setBusy(false);
    }
  };

  return (
    <VehicleContext.Provider
      value={{
        busy,
        models,
        totalVehicles,
        vehicles,
        years,
        addVehicle,
        getConnections,
        getECUs,
        getLocations,
        getModels,
        getState,
        getYears,
        getVehicles,
        sendCommand,
      }}
    >
      {children}
    </VehicleContext.Provider>
  );
};

export const useVehicleContext = () => useContext(VehicleContext);
