<template>
  <!-- CONTENT -->
  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Sub View Heading -->
    <div id="SubViewHeading" class="flex gap-4 items-start mb-8">
      <h1 class="grow text-xl text-center font-semibold leading-tight">
        Area for Nearby Search
      </h1>

      <!-- Back -->
      <a
        class="order-first text-lg text-center leading-none p-1"
        @click="goBack()"
      >
        <font-awesome icon="chevron-left" class="block text-center w-[1em]" />
      </a>

      <!-- Help button -->
      <router-link
        :to="{ name: 'help' }"
        class="block ms-auto text-sm text-center text-white bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] p-1.5 rounded-full"
      >
        <font-awesome icon="question" class="block text-center w-[1em]" />
      </router-link>
    </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="actionButtonClass"
        @click="storeSearchBox"
      >
        <font-awesome icon="save" class="fa-fw" />
        Store This Location for Nearby Search
      </button>
      <button
        v-if="searchBox"
        :class="actionButtonClass"
        @click="forgetSearchBox"
      >
        <font-awesome icon="trash-can" class="fa-fw" />
        Delete Stored Location
      </button>
      <button v-if="searchBox" :class="actionButtonClass" @click="resetLatLong">
        <font-awesome icon="rotate" class="fa-fw" />
        Reset To Original
      </button>
      <button
        v-if="isNewMarkerSet"
        :class="actionButtonClass"
        @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">
/**
 * @fileoverview SearchAreaView - Geographic Search Area Management Component
 *
 * This component provides an interactive map interface for users to set and manage
 * geographic search areas for location-based content filtering. It uses Leaflet maps
 * to allow users to select, store, and delete geographic bounding boxes that define
 * their preferred search areas.
 *
 * Key Features:
 * - Interactive Leaflet map with marker and rectangle overlays
 * - Geographic bounding box calculation and storage
 * - Privacy-preserving local storage (no server transmission)
 * - Real-time map interaction with visual feedback
 * - Automatic bounding box size estimation based on map zoom
 *
 * Enhanced Triple Migration Pattern Status:
 * ✅ Phase 1: Database Migration - PlatformServiceMixin integration
 * ⏳ Phase 2: SQL Abstraction - No raw SQL queries to migrate
 * ⏳ Phase 3: Notification Migration - Helper system integration
 * ⏳ Phase 4: Template Streamlining - Method extraction and styling
 *
 * Security: All location data is stored locally on the user's device and is never
 * transmitted to external servers except for explicit search operations.
 *
 * @component SearchAreaView
 * @requires PlatformServiceMixin - Database operations
 * @requires Leaflet - Map rendering and interaction
 * @requires Vue-Leaflet - Vue.js Leaflet integration
 * @author TimeSafari Development Team
 * @since 2024-01-01
 * @version 1.0.0
 * @migrated 2025-07-09 (Enhanced Triple Migration Pattern)
 */

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 } from "../constants/app";
import { BoundingBox } from "../db/tables/settings";
import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
import {
  NOTIFY_SEARCH_AREA_SAVED,
  NOTIFY_SEARCH_AREA_ERROR,
  NOTIFY_SEARCH_AREA_NO_LOCATION,
  NOTIFY_SEARCH_AREA_DELETED,
} from "../constants/notifications";
import { createNotifyHelpers, TIMEOUTS } from "../utils/notify";

// Geographic constants for map rendering and bounding box calculations
const DEFAULT_LAT_LONG_DIFF = 0.01;
const WORLD_ZOOM = 2;
const DEFAULT_ZOOM = 2;

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

  // Notification helper system - will be initialized in mounted()
  private notify: ReturnType<typeof createNotifyHelpers> | null = null;

  // User interface state management
  isChoosingSearchBox = false;
  isNewMarkerSet = false;

  // Local geographic coordinates for current map selection
  // These represent the currently selected area before storage
  localCenterLat = 0;
  localCenterLong = 0;
  localLatDiff = DEFAULT_LAT_LONG_DIFF;
  localLongDiff = DEFAULT_LAT_LONG_DIFF;
  localZoom = DEFAULT_ZOOM;

  // Stored search box configuration loaded from database
  // This represents the persisted geographic search area
  searchBox: { name: string; bbox: BoundingBox } | null = null;

  /**
   * Component lifecycle hook - Initialize geographic search area
   * Loads existing search box configuration from local database
   * and initializes map display with stored or default coordinates
   */
  async mounted() {
    try {
      // Initialize notification helper system
      this.notify = createNotifyHelpers(this.$notify);

      const settings = await this.$accountSettings();
      this.searchBox = settings.searchBoxes?.[0] || null;
      this.resetLatLong();

      logger.debug("[SearchAreaView] Component mounted", {
        hasStoredSearchBox: !!this.searchBox,
        searchBoxName: this.searchBox?.name,
        coordinates: this.searchBox?.bbox,
      });
    } catch (error) {
      logger.error(
        "[SearchAreaView] Failed to load search area settings",
        error,
      );
      // Continue with default behavior if settings load fails
      this.resetLatLong();
    }
  }

  /**
   * Handle map click events for geographic area selection
   * Supports two modes: initial marker placement and bounding box sizing
   *
   * @param event - Leaflet mouse event containing geographic coordinates
   */
  setMapPoint(event: LeafletMouseEvent) {
    if (this.isNewMarkerSet) {
      // Existing marker - calculate bounding box size based on click distance
      this.localLatDiff = Math.abs(event.latlng.lat - this.localCenterLat);
      this.localLongDiff = Math.abs(event.latlng.lng - this.localCenterLong);
    } else {
      // New marker - set center point and estimate initial bounding box size
      this.localCenterLat = event.latlng.lat;
      this.localCenterLong = event.latlng.lng;

      let latDiff = DEFAULT_LAT_LONG_DIFF;
      let longDiff = DEFAULT_LAT_LONG_DIFF;

      // Estimate reasonable bounding box size based on current map 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;
    }
  }

  /**
   * Reset map coordinates to stored search box or default values
   * Used when component loads or when user wants to restore original area
   */
  public resetLatLong() {
    if (this.searchBox?.bbox) {
      // Restore coordinates from stored search box
      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 {
      // No stored search box - reset to default state
      this.isNewMarkerSet = false;
    }
  }

  /**
   * Computed property for consistent action button styling
   * Provides standardized classes for all geographic action buttons
   */
  get actionButtonClass() {
    return "m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500";
  }

  /**
   * Navigate back to previous page
   * Extracted from template for better maintainability
   */
  goBack() {
    this.$router.back();
  }

  /**
   * Store the currently selected geographic area as search box preference
   * Validates coordinates and persists bounding box to local database
   * Provides user feedback and navigates back on success
   */
  public async storeSearchBox() {
    if (this.localCenterLong || this.localCenterLat) {
      try {
        // Create search box configuration with calculated bounding box
        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,
          },
        };

        // Store search box configuration using platform service
        // searchBoxes will be automatically converted to JSON string by $updateSettings
        await this.$updateSettings({ searchBoxes: [newSearchBox] });

        this.searchBox = newSearchBox;
        this.isChoosingSearchBox = false;

        logger.debug("[SearchAreaView] Search box stored successfully", {
          searchBox: newSearchBox,
          coordinates: newSearchBox.bbox,
        });

        // Enhanced notification system with proper timeout
        this.notify?.success(NOTIFY_SEARCH_AREA_SAVED.text, TIMEOUTS.VERY_LONG);
        this.$router.back();
      } catch (err) {
        logger.error("[SearchAreaView] Failed to store search box", err);

        // Enhanced notification system with proper timeout
        this.notify?.error(NOTIFY_SEARCH_AREA_ERROR.text, TIMEOUTS.LONG);
      }
    } else {
      // Invalid coordinates - show validation warning
      this.notify?.warning(NOTIFY_SEARCH_AREA_NO_LOCATION.text, TIMEOUTS.LONG);
    }
  }

  /**
   * Delete stored search box preference and reset component state
   * Removes geographic search area from database and UI
   * Provides success feedback on completion
   */
  public async forgetSearchBox() {
    try {
      // Clear search box settings and disable nearby filtering
      await this.$updateSettings({
        searchBoxes: [],
        filterFeedByNearby: false,
      });

      // Reset component state to default values
      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;

      logger.debug("[SearchAreaView] Search box deleted successfully");

      // Enhanced notification system with proper timeout
      this.notify?.success(NOTIFY_SEARCH_AREA_DELETED.text, TIMEOUTS.STANDARD);
    } catch (err) {
      logger.error("[SearchAreaView] Failed to delete search box", err);

      // Enhanced notification system with proper timeout
      this.notify?.error(NOTIFY_SEARCH_AREA_ERROR.text, TIMEOUTS.LONG);
    }
  }

  /**
   * Cancel search box selection and reset zoom level
   * Used when user wants to abort the selection process
   */
  public cancelSearchBoxSelect() {
    this.isChoosingSearchBox = false;
    this.localZoom = WORLD_ZOOM;
  }
}
</script>
