import { algoliasearch } from 'algoliasearch';
import slugify from '@sindresorhus/slugify';
const INDEX_NAME = 'pubfinder';
const client = algoliasearch('YSWWVAX5RB', '4203de3b08b981c149883b0af830db30');
const $ = document.querySelector.bind(document);

const dataController = (function () {
  const initialRectBounds = {
    north: 51.522899,
    south: 51.484821,
    east: -0.07766,
    west: -0.138968,
  };

  const maxMapSpace = {
    north: 51.74,
    south: 51.27,
    west: -0.51,
    east: 0.23,
  };

  const initialPolygonBounds = [
    { lat: 51.522786, lng: -0.077603 },
    { lat: 51.525376, lng: -0.108023 },
    { lat: 51.520791, lng: -0.138718 },
    { lat: 51.506604, lng: -0.151214 },
    { lat: 51.485288, lng: -0.106803 },
    { lat: 51.493989, lng: -0.085187 },
  ];

  const centralPosition = { lat: 51.508616, lng: -0.125319 };

  return {
    initialRectBounds,
    centralPosition,
    maxMapSpace,
    initialPolygonBounds,
    getSearchResults: async function (query = '', shape = '', bounds = [], hitsToGet) {
      let geoParam;
      if (shape === 'rectangle') {
        geoParam = {
          insideBoundingBox: [bounds],
        };
      } else {
        geoParam = {
          insidePolygon: [bounds],
        };
      }

      const res = await client.searchSingleIndex({
        indexName: INDEX_NAME,
        searchParams: {
          query,
          hitsPerPage: hitsToGet,
          ...geoParam,
          attributesToRetrieve: ['_geoloc', 'name', 'address', 'postcode'],
        },
      });
      return res;
    },
    getCurrentQuery: function () {
      return document.querySelector('#searchbar').value;
    },
  };
})();

const interfaceController = (function () {
  let markers = [];
  let shape;
  let map;
  const DOMStrings = {
    selectRect: '.select--rectangle',
    selectPoly: '.select--polygon',
    hits: '#hits',
    searchBar: '#searchbar',
    pubCount: '.pub-count',
  };

  const generateMap = async function (bounds, position, maxSpace, initialPolygonBounds, currentShape, zoom = 12) {
    const { Map, Polygon, Rectangle } = await google.maps.importLibrary('maps');
    map = new Map(document.getElementById('map'), {
      zoom,
      center: position,
      mapId: 'pub_map',
      colorScheme: 'DARK',
      restriction: {
        latLngBounds: maxSpace,
        strictBounds: true,
      },
    });

    if (currentShape === 'rectangle') {
      shape = new Rectangle({
        bounds: bounds,
        editable: true,
        draggable: true,
        strokeWeight: 2,
        strokeOpacity: 0.8,
        strokeColor: '#D97706',
        fillColor: '#D97706',
        fillOpacity: 0.35,
      });
      shape.setMap(map);
    } else if (currentShape === 'polygon') {
      shape = new Polygon({
        paths: initialPolygonBounds,
        strokeColor: '#D97706',
        strokeOpacity: 0.8,
        strokeWeight: 2,
        fillColor: '#D97706',
        fillOpacity: 0.35,
        editable: true,
        draggable: true,
        geodesic: true,
      });
      shape.setMap(map);
    }
  };

  return {
    handleShapeSelect: function (e) {},
    initMap: generateMap,
    embedSearchResults: async function (hits) {
      const { AdvancedMarkerElement } = await google.maps.importLibrary('marker');
      const { InfoWindow } = await google.maps.importLibrary('maps');

      hits.forEach((hit) => {
        const marker = new AdvancedMarkerElement({
          map: map,
          position: {
            lat: hit._geoloc.lat,
            lng: hit._geoloc.lng,
          },
        });

        const window = new InfoWindow({
          content: `
            <h2 class="text-black font-semibold text-md mb-2">${hit.name}</h2>
            <h3 class="text-zinc-600 text-xs">${hit.address}</h3>
             <a href="https://www.google.co.uk/maps/search/${slugify(
               hit.address
             )}" class="absolute top-2 left-2" target="_blank"><span class="text-xl">📍</span></a>
          `,
        });

        marker.addListener('click', function () {
          window.open({
            anchor: marker,
            map,
          });
        });

        markers.push(marker);
      });
    },
    updateHitsList: function (hits) {
      $(DOMStrings.hits).innerHTML = '';
      let html = ``;
      if (hits.length > 0) {
        const slicedArr = hits.slice(0, 20);
        html += `<ul class="flex flex-col gap-2">`;
        for (const pub of slicedArr) {
          html += `<li class="relative bg-[#373737] rounded-lg p-4">
          <h3 class="font-medium mb-2">${pub.name}</h3>
          <h4 class="text-sm">${pub.address}, ${pub.postcode}</h4>
           <a href="https://www.google.co.uk/maps/search/${slugify(
             pub.address
           )}" class="absolute block top-4 right-4" target="_blank">
          <svg width="20px" height="20px" viewBox="0 0 24 24" fill="none" >
          <path d="M5 12V6C5 5.44772 5.44772 5 6 5H18C18.5523 5 19 5.44772 19 6V18C19 18.5523 18.5523 19 18 19H12M8.11111 12H12M12 12V15.8889M12 12L5 19" stroke="#D97706" stroke-linecap="round" stroke-linejoin="round" />
          </svg>
          </a>
          </li>`;
        }
        html += `</ul>`;
      } else {
        html = `<div class="text-center mt-5">There are no results to show, try refinfing your search</div>`;
      }

      $(DOMStrings.hits).insertAdjacentHTML('beforeend', html);
      $(DOMStrings.pubCount).innerHTML = `${hits.length > 0 ? 'Showing ' + hits.length + ' pubs' : 'No pubs'}`;
    },
    handleBoundChange: function () {
      const { south, west, north, east } = shape.getBounds()?.toJSON();
      return [north, east, south, west];
    },
    getActiveShape: function () {
      return shape;
    },
    getShapeBounds: function () {
      if (shape instanceof google.maps.Polygon) {
        const paths = shape.getPaths();
        let points = [];

        paths.forEach((path) => {
          path.forEach((latLng) => {
            const lat = latLng.lat();
            const lng = latLng.lng();
            points.push(lat, lng);
          });
        });
        return points;
      } else {
        const { south, west, north, east } = shape.getBounds()?.toJSON();
        return [north, east, south, west];
      }
    },
    removeMarkers: function () {
      for (let i = 0; i < markers.length; i++) {
        markers[i].setMap(null);
      }
    },
    getDOMStrings: function () {
      return DOMStrings;
    },
  };
})();

const controller = (function (dataCTRL, uiCTRL) {
  const DOM = uiCTRL.getDOMStrings();
  const rectBounds = dataCTRL.initialRectBounds;
  const centralPoint = dataCTRL.centralPosition;
  const mapBounds = dataCTRL.maxMapSpace; // Rectangle bounds
  const initialPolygonBounds = dataCTRL.initialPolygonBounds; // Polygon bounds

  const initialSetup = async function () {
    await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'rectangle', 12);

    const searchResults = await dataCTRL.getSearchResults(
      '',
      'rectangle',
      [rectBounds.north, rectBounds.east, rectBounds.south, rectBounds.west],
      1000
    );
    await uiCTRL.embedSearchResults(searchResults.hits);
    uiCTRL.updateHitsList(searchResults.hits);
  };

  const setupMapListeners = async function () {
    const shape = uiCTRL.getActiveShape();
    if (shape instanceof google.maps.Rectangle) {
      let debounceTimer;

      shape.addListener('bounds_changed', () => {
        uiCTRL.removeMarkers();
        clearTimeout(debounceTimer);

        debounceTimer = setTimeout(async () => {
          const newAlgoliaBounds = uiCTRL.handleBoundChange();
          const searchResults = await dataCTRL.getSearchResults(
            dataCTRL.getCurrentQuery(),
            'rectangle',
            newAlgoliaBounds,
            1000
          );
          await uiCTRL.embedSearchResults(searchResults.hits);
          uiCTRL.updateHitsList(searchResults.hits);
        }, 200);
      });
    }

    if (shape instanceof google.maps.Polygon) {
      let debounceTimer;
      const paths = shape.getPaths();

      const handlePathDrag = async function () {
        uiCTRL.removeMarkers();
        clearTimeout(debounceTimer);

        debounceTimer = setTimeout(async () => {
          const points = uiCTRL.getShapeBounds();

          const searchResults = await dataCTRL.getSearchResults(dataCTRL.getCurrentQuery(), 'polygon', points, 1000);
          await uiCTRL.embedSearchResults(searchResults.hits);
          uiCTRL.updateHitsList(searchResults.hits);
        }, 300);
      };

      paths.forEach((path) => {
        path.addListener('set_at', handlePathDrag);
        path.addListener('insert_at', handlePathDrag);
      });
    }
  };

  const setupEventListeners = async function () {
    $(DOM.selectRect).addEventListener('click', async () => {
      await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'rectangle', 12);
      const searchResults = await dataCTRL.getSearchResults(
        dataCTRL.getCurrentQuery(),
        'rectangle',
        [rectBounds.north, rectBounds.east, rectBounds.south, rectBounds.west],
        1000
      );
      await uiCTRL.embedSearchResults(searchResults.hits);
      uiCTRL.updateHitsList(searchResults.hits);
      await setupMapListeners();
    });

    $(DOM.selectPoly).addEventListener('click', async () => {
      await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'polygon', 12);
      const translatedPolyBounds = initialPolygonBounds.flatMap(({ lat, lng }) => [lat, lng]);
      const searchResults = await dataCTRL.getSearchResults(
        dataCTRL.getCurrentQuery(),
        'polygon',
        translatedPolyBounds,
        1000
      );
      await uiCTRL.embedSearchResults(searchResults.hits);
      uiCTRL.updateHitsList(searchResults.hits);
      await setupMapListeners();
    });

    $(DOM.searchBar).addEventListener('input', async (e) => {
      const q = dataCTRL.getCurrentQuery();
      if (q > 0 && q < 3) return;
      const bounds = uiCTRL.getShapeBounds();
      const activeShape = uiCTRL.getActiveShape();
      const searchResults = await dataCTRL.getSearchResults(
        q,
        activeShape instanceof google.maps.Polygon ? 'polygon' : 'rectangle',
        bounds,
        1000
      );
      uiCTRL.removeMarkers();
      await uiCTRL.embedSearchResults(searchResults.hits);
      uiCTRL.updateHitsList(searchResults.hits);
    });
  };

  return {
    init: async function () {
      await initialSetup();
      await setupMapListeners();
      await setupEventListeners();
    },
  };
})(dataController, interfaceController);

controller.init();