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

import GrayMarkerIcon from "../../assets/gray-marker.png";
import GreenMarkerIcon from "../../assets/green-marker.png";
import { logger } from "../../services/monitoring";
import { ValidateLocationData, ValidateLocationVehiclePathsData } from "../../utils/locations";
import { useUserContext } from "../Contexts/UserContext";
import { useVehicleContext, VehicleProvider } from "../Contexts/VehicleContext";
import { VehiclePopUp } from "../VehicleMap/popup";

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

  const REQUEST_INTERVAL = 10000;

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

  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) => {
    let vinsToShowOnMap = [...props.vinsToShowOnMapColors.keys()];
    let param = "lookback_hours=" + props.lookbackHours

    return getLocationsVehiclePaths(accessToken, param, vinsToShowOnMap)
      .then(async (result) => {
        let resultArray = Object.entries(result)
        const points = []

        // validate each location
        for (let vinLocations of resultArray) {
          if (vinLocations[0]) {
            let path = [];
            path[0] = vinLocations[0];
            path[1] = [];
            if (vinLocations[1] && vinLocations[1][0]) {
              for (let location of vinLocations[1]) {
                if (ValidateLocationVehiclePathsData(location) !== false) {
                  path[1].push(location);
                }
              }
            } else {
              await getState(accessToken, vinLocations[0])
                .then(async (stateResult) => {
                  if (stateResult.data?.location) {
                    if (ValidateLocationData(stateResult.data.location) !== false) {
                      path[1].push([stateResult.data.location.latitude, stateResult.data.location.longitude]);
                    }
                  }
                });
            }
            points.push(path);
          }
        }

        setMarkers(points);
        return points;
      })
      .catch((error) => logger.warn(error.stack));
  };

  const centerAroundMarkers = (points) => {
    // default center
    let center = [37.0902, -95.7129]

    // center is the very first geographical point
    if (points && points[0] && points[0][1] && points[0][1][0]) {
      center = points[0][1][0]
    }

    setCenter(center);
    setZoom(4.5);
  };

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

    const vins = []

    for (let vinLocations of markers) {
      vins.push(vinLocations[0])
    }

    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 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 (
    <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} />
      {markers && markers.map((vinLocations) => (
        <div key={vinLocations[0]}>
          <Polyline
            key={'line' + vinLocations[0]}
            positions={vinLocations[1]}
            pathOptions={{
              color: props.vinsToShowOnMapColors && props.vinsToShowOnMapColors.get(vinLocations[0]),
            }}
          />
          {vinLocations[1][0] &&
            <Marker
              icon={getCarIcon(vinLocations[0])}
              key={'marker' + vinLocations[0]}
              position={vinLocations[1][0]}
              title={vinLocations[0]}
              opacity={0.9}
              zIndexOffset={getZIndex(vinLocations[0])}
              eventHandlers={{
                click: () => {
                  setCenter(vinLocations[1][0]);
                  setZoom(16);
                },
              }}
            >
              <Popup>
                <div align="center">
                  <p className={classes.markerTitle}>
                    <b>{vinLocations[0]}</b>
                  </p>
                  <Button
                    type="submit"
                    variant="contained"
                    color="primary"
                    onClick={(e) => selectCar(e, vinLocations[0])}
                  >
                    View Stats
                  </Button>
                </div>
              </Popup>
            </Marker>
          }
        </div>
      ))}

      {carState ? (
        <VehiclePopUp
          {...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 VehiclePathsMap = (props) => (
  <VehicleProvider>
    <ComponentVehiclePathsMap vinsToShowOnMapColors={props.vinsToShowOnMapColors} lookbackHours={props.lookbackHours} />
  </VehicleProvider>
);

export default VehiclePathsMap;
