<template>
  <QuickNav />

  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Breadcrumb -->
    <div class="mb-8">
      <!-- Back -->
      <div class="text-lg text-center font-light relative px-7">
        <h1
          class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
          @click="$router.back()"
        >
          <font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
        </h1>
      </div>

      <!-- Heading -->
      <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
        Area for Nearby Search
      </h1>
    </div>

    <div class="px-2 py-4">
      This location is only stored on your device. It is sometimes sent from
      your device to run searches but it is not stored on our servers.
    </div>

    <div class="text-center">
      <button v-if="!searchBox && !isNewMarkerSet" class="m-4 px-4 py-2">
        Click to Choose a Location for Nearby Search
      </button>
      <button
        v-if="isNewMarkerSet"
        class="m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500"
        @click="storeSearchBox"
      >
        <font-awesome icon="save" class="fa-fw" />
        Store This Location for Nearby Search
      </button>
      <button
        v-if="searchBox"
        class="m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500"
        @click="forgetSearchBox"
      >
        <font-awesome icon="trash-can" class="fa-fw" />
        Delete Stored Location
      </button>
      <button
        v-if="searchBox"
        class="m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500"
        @click="resetLatLong"
      >
        <font-awesome icon="rotate" class="fa-fw" />
        Reset To Original
      </button>
      <button
        v-if="isNewMarkerSet"
        class="m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500"
        @click="isNewMarkerSet = false"
      >
        <font-awesome icon="eraser" class="fa-fw" />
        Erase Marker
      </button>
      <div v-if="isNewMarkerSet">
        Click on the pin to erase it. Click anywhere else to set a different
        different corner.
      </div>
    </div>

    <div class="aspect-video">
      <l-map
        ref="map"
        v-model:zoom="localZoom"
        :center="[localCenterLat, localCenterLong]"
        class="!z-40 rounded-md"
        @click="setMapPoint"
      >
        <l-tile-layer
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          layer-type="base"
          name="OpenStreetMap"
        />
        <l-marker
          v-if="isNewMarkerSet"
          :lat-lng="[localCenterLat, localCenterLong]"
          @click="isNewMarkerSet = false"
        />
        <l-rectangle
          v-if="isNewMarkerSet"
          :bounds="[
            [localCenterLat - localLatDiff, localCenterLong - localLongDiff],
            [localCenterLat + localLatDiff, localCenterLong + localLongDiff],
          ]"
          :weight="1"
        />
      </l-map>
    </div>
  </section>
</template>

<script lang="ts">
import { LeafletMouseEvent } from "leaflet";
import "leaflet/dist/leaflet.css";
import { Component, Vue } from "vue-facing-decorator";
import {
  LMap,
  LMarker,
  LRectangle,
  LTileLayer,
} from "@vue-leaflet/vue-leaflet";
import { Router } from "vue-router";

import QuickNav from "../components/QuickNav.vue";
import { NotificationIface, USE_DEXIE_DB } from "../constants/app";
import * as databaseUtil from "../db/databaseUtil";
import { db, retrieveSettingsForActiveAccount } from "../db/index";
import { BoundingBox, MASTER_SETTINGS_KEY } from "../db/tables/settings";
import { logger } from "../utils/logger";

const DEFAULT_LAT_LONG_DIFF = 0.01;
const WORLD_ZOOM = 2;
const DEFAULT_ZOOM = 2;

@Component({
  components: {
    QuickNav,
    LRectangle,
    LMap,
    LMarker,
    LTileLayer,
  },
})
export default class SearchAreaView extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  $router!: Router;

  isChoosingSearchBox = false;
  isNewMarkerSet = false;

  // "local" vars are for the currently selected map box
  localCenterLat = 0;
  localCenterLong = 0;
  localLatDiff = DEFAULT_LAT_LONG_DIFF;
  localLongDiff = DEFAULT_LAT_LONG_DIFF;
  localZoom = DEFAULT_ZOOM;

  // searchBox reflects what is stored in the database
  searchBox: { name: string; bbox: BoundingBox } | null = null;

  async mounted() {
    let settings = await databaseUtil.retrieveSettingsForActiveAccount();
    if (USE_DEXIE_DB) {
      settings = await retrieveSettingsForActiveAccount();
    }
    this.searchBox = settings.searchBoxes?.[0] || null;
    this.resetLatLong();
  }

  setMapPoint(event: LeafletMouseEvent) {
    if (this.isNewMarkerSet) {
      this.localLatDiff = Math.abs(event.latlng.lat - this.localCenterLat);
      this.localLongDiff = Math.abs(event.latlng.lng - this.localCenterLong);
    } else {
      // marker is not set
      this.localCenterLat = event.latlng.lat;
      this.localCenterLong = event.latlng.lng;

      let latDiff = DEFAULT_LAT_LONG_DIFF;
      let longDiff = DEFAULT_LAT_LONG_DIFF;
      // Guess at a size for the bounding box.
      // This doesn't seem like the right approach but it's the only way I can find to get the screen bounds.
      const bounds = event.target.boxZoom?._map?.getBounds();
      if (bounds) {
        latDiff =
          Math.abs(bounds.getNorthEast().lat - bounds.getSouthWest().lat) / 8;
        longDiff =
          Math.abs(bounds.getNorthEast().lng - bounds.getSouthWest().lng) / 8;
      }
      this.localLatDiff = latDiff;
      this.localLongDiff = longDiff;
      this.isNewMarkerSet = true;
    }
  }

  public resetLatLong() {
    if (this.searchBox?.bbox) {
      const bbox = this.searchBox.bbox;
      this.localCenterLat = (bbox.maxLat + bbox.minLat) / 2;
      this.localCenterLong = (bbox.eastLong + bbox.westLong) / 2;
      this.localLatDiff = (bbox.maxLat - bbox.minLat) / 2;
      this.localLongDiff = (bbox.eastLong - bbox.westLong) / 2;
      this.localZoom = WORLD_ZOOM;
      this.isNewMarkerSet = true;
    } else {
      this.isNewMarkerSet = false;
    }
  }

  public async storeSearchBox() {
    if (this.localCenterLong || this.localCenterLat) {
      try {
        const newSearchBox = {
          name: "Local",
          bbox: {
            eastLong: this.localCenterLong + this.localLongDiff,
            maxLat: this.localCenterLat + this.localLatDiff,
            minLat: this.localCenterLat - this.localLatDiff,
            westLong: this.localCenterLong - this.localLongDiff,
          },
        };
        const searchBoxes = JSON.stringify([newSearchBox]);
        // @ts-expect-error - the DB requires searchBoxes to be a string
        databaseUtil.updateDefaultSettings({ searchBoxes });
        if (USE_DEXIE_DB) {
          await db.open();
          await db.settings.update(MASTER_SETTINGS_KEY, {
            searchBoxes: searchBoxes as unknown, // Type assertion for Dexie compatibility
          });
        }
        this.searchBox = newSearchBox;
        this.isChoosingSearchBox = false;

        this.$notify(
          {
            group: "alert",
            type: "success",
            title: "Saved",
            text: "That has been saved in your preferences. You can now filter by it on your home screen feed.",
          },
          7000,
        );
        this.$router.back();
      } catch (err) {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error Updating Search Settings",
            text: "Try going to a different page and then coming back.",
          },
          5000,
        );
        logger.error(
          "Telling user to retry the location search setting because:",
          err,
        );
      }
    } else {
      this.$notify(
        {
          group: "alert",
          type: "warning",
          title: "No Location Selected",
          text: "Select a location on the map.",
        },
        5000,
      );
    }
  }

  public async forgetSearchBox() {
    try {
      // @ts-expect-error - the DB requires searchBoxes to be a string
      await databaseUtil.updateDefaultSettings({
        searchBoxes: "[]",
        filterFeedByNearby: false,
      });
      if (USE_DEXIE_DB) {
        await db.open();
        await db.settings.update(MASTER_SETTINGS_KEY, {
          searchBoxes: "[]" as unknown as string, // Type assertion for Dexie compatibility
          filterFeedByNearby: false,
        });
      }
      this.searchBox = null;
      this.localCenterLat = 0;
      this.localCenterLong = 0;
      this.localLatDiff = DEFAULT_LAT_LONG_DIFF;
      this.localLongDiff = DEFAULT_LAT_LONG_DIFF;
      this.localZoom = DEFAULT_ZOOM;
      this.isChoosingSearchBox = false;
      this.isNewMarkerSet = false;
    } catch (err) {
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error Updating Search Settings",
          text: "Try going to a different page and then coming back.",
        },
        5000,
      );
      logger.error(
        "Telling user to retry the location search setting because:",
        err,
      );
    }
  }

  public cancelSearchBoxSelect() {
    this.isChoosingSearchBox = false;
    this.localZoom = WORLD_ZOOM;
  }
}
</script>
