import { Button } from "@material-ui/core";
import L from "leaflet";
import React, { useEffect, useState } from "react";
import { MapContainer, Marker, Popup, TileLayer, useMap } from "react-leaflet";
import MarkerClusterGroup from 'react-leaflet-cluster'
import useStyles from "../useStyles";

import GrayMarkerIcon from "../../assets/gray-marker.png";
import GreenMarkerIcon from "../../assets/green-marker.png";
import { DropDownList } from "../Controls/DropDownList";
import { logger } from "../../services/monitoring";
import { ValidateLocationData } from "../../utils/locations";
import { useUserContext } from "../Contexts/UserContext";
import { useVehicleContext, VehicleProvider } from "../Contexts/VehicleContext";
import { VehiclePopUp } from "./popup";
import { useLocalStorage } from "../useLocalStorage";
import zoomLocations from './zoomLocations.json';

const Component = (props) => {
  const classes = useStyles();
  const {
    token: {
      idToken: { jwtToken: token },
    },
  } = useUserContext();
  const { getConnections, getLocations, getState } = useVehicleContext();

  const REQUEST_INTERVAL = 30000;

  const [center, setCenter] = useState([0, 0]);
  const [zoom, setZoom] = useState(2);
  const [markers, setMarkers] = useState([]);
  const [connections, setConnections] = useState({});
  const [lastMapCenter, setLastMapCenter] = useLocalStorage("MAP_CENTER", "");

  useEffect(() => {
    if (!token) return;
    retrieveAndStoreLocations(token).then((points) => {
      centerAroundMarkers(points);
    });
    const id = setInterval(function () {
      retrieveAndStoreLocations(token);
    }, REQUEST_INTERVAL);
    return () => {
      clearInterval(id);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [token]);

  const retrieveAndStoreLocations = (accessToken) => {
    if (props.vin) {
      // if a vin is specified, get its location from vehicle state
      return getState(accessToken, props.vin)
        .then(async (stateResult) => {
          if (stateResult.data?.location) {
            if (ValidateLocationData(stateResult.data.location) !== false) {
              const points = [[
                stateResult.data.location.latitude,
                stateResult.data.location.longitude,
                props.vin,
              ]];
              setMarkers(points);
              return points;
            }
          }
          return [];
        })
        .catch((error) => logger.warn(error.stack));
    } else {
      // if no vin is specified, use getLocations
      return getLocations(accessToken)
        .then(async (result) => {
          if (result.data != null && ValidateLocationData(result.data) !== false) {
            const points = result.data.map((point) => [
              point.latitude,
              point.longitude,
              point.vin,
            ]);
            setMarkers(points);
            return points;
          }
          return [];
        })
        .catch((error) => logger.warn(error.stack));
    }
  };

  const centerAroundMarkers = (points) => {
    let defaultLocationJSON = process.env.REACT_APP_HOME_MAP_DEFAULT_LOCATION;
    let defaultLocation = JSON.parse(defaultLocationJSON)

    if (props.vin) {
      // center is the location of the specified vin
      if (points && points[0]) {
        setCenter([points[0][0], points[0][1]]);
        setZoom(defaultLocation.zoom);
        return
      }
    }

    if (lastMapCenter) {
      let zoomLocation = JSON.parse(lastMapCenter)
      setCenter(zoomLocation.center);
      setZoom(zoomLocation.zoom)
    } else {
      setCenter([defaultLocation.lat, defaultLocation.lng]);
      setZoom(defaultLocation.zoom);
    }
  };

  useEffect(() => {
    if (!token) return;

    const vins = markers
      .filter((marker) => marker[2].length > 0)
      .map((marker) => marker[2]);

    if (vins.length === 0) return;

    getConnections(vins, token).then((conns) => {
      setConnections(conns);
    });
    // eslint-disable-next-line
  }, [markers, token]);

  const [selectedVIN, setSelectedVIN] = useState(null);
  const [carState, setCarState] = useState(null);

  useEffect(() => {
    if (selectedVIN != null) {
      retrieveAndStoreCarState(selectedVIN);
      const id = setInterval(function () {
        retrieveAndStoreCarState(selectedVIN);
      }, REQUEST_INTERVAL);
      return () => {
        clearInterval(id);
      };
    }
    // eslint-disable-next-line
  }, [selectedVIN]);

  const selectCar = (e, vin) => {
    e.preventDefault();
    setSelectedVIN(vin);
  };

  const focusMap = (e) => {
    setLastMapCenter(e.target.value)
    let zoomLocation = JSON.parse(e.target.value)
    setCenter(zoomLocation.center);
    setZoom(zoomLocation.zoom)
  }

  const retrieveAndStoreCarState = (vin) => {
    getState(token, vin).then((results) => {
      setCarState({ ...results.data, vin: vin });
    });
  };

  const handleClose = () => {
    setSelectedVIN(null);
    setCarState(null);
  };

  const isOnline = (vin) => {
    return connections[vin];
  };

  const getZIndex = (vin) => {
    if (isOnline(vin)) return 1000;
    return 0;
  };

  function getCarIcon(vin) {
    let icon = GrayMarkerIcon;

    if (isOnline(vin)) {
      icon = GreenMarkerIcon;
    }

    return new L.Icon({
      iconUrl: icon,
      iconAnchor: [24, 42],
    });
  }

  return (
    <>
      {!props.vin && <DropDownList label="Zoom To" data={zoomLocations} classes={classes} onChange={focusMap} value={lastMapCenter} />}
      <MapContainer
        center={center}
        zoom={zoom}
        style={{
          width: "100%",
          height: "900px",
        }}
      >
        <TileLayer
          attribution='&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <CenterFocus center={center} zoom={zoom} />
        <MarkerClusterGroup
          chunkedLoading
        >
          {markers.map((marker) => (
            <Marker
              icon={getCarIcon(marker[2])}
              key={marker[2]}
              position={[marker[0], marker[1]]}
              title={marker[2]}
              opacity={0.9}
              zIndexOffset={getZIndex(marker[2])}
            >
              <Popup>
                <div align="center">
                  <p className={classes.markerTitle}>
                    <b>{marker[2]}</b>
                  </p>
                  <Button
                    type="submit"
                    variant="contained"
                    color="primary"
                    onClick={(e) => selectCar(e, marker[2])}
                  >
                    View Stats
                  </Button>
                </div>
              </Popup>
            </Marker>
          ))}
        </MarkerClusterGroup>

        {carState ? (
          <VehiclePopUp
            key={carState.vin}
            {...carState}
            className={classes.popup}
            onClose={handleClose}
          />
        ) : null}
      </MapContainer>
    </>
  );
};

const CenterFocus = ({ center, zoom }) => {
  const map = useMap();

  useEffect(() => {
    if (center[0] === 0 && center[1] === 0) {
      map.flyTo([0, 0], 2, { duration: 1.5 });
    } else {
      map.flyTo(center, zoom, { duration: 1.5 });
    }
  }, [center, zoom, map]);

  return null;
};

const VehicleMap = (props) => (
  <VehicleProvider>
    <Component vin={props.vin} />
  </VehicleProvider>
);

export default VehicleMap;