import { Controller } from '@hotwired/stimulus'
import { ApiClient } from 'maps_maplibre/services/api_client'
import { SettingsManager } from 'maps_maplibre/utils/settings_manager'
import { SearchManager } from 'maps_maplibre/utils/search_manager'
import { Toast } from 'maps_maplibre/components/toast'
import { performanceMonitor } from 'maps_maplibre/utils/performance_monitor'
import { CleanupHelper } from 'maps_maplibre/utils/cleanup_helper'
import { MapInitializer } from './maplibre/map_initializer'
import { MapDataManager } from './maplibre/map_data_manager'
import { LayerManager } from './maplibre/layer_manager'
import { DataLoader } from './maplibre/data_loader'
import { EventHandlers } from './maplibre/event_handlers'
import { FilterManager } from './maplibre/filter_manager'
import { DateManager } from './maplibre/date_manager'
import { SettingsController } from './maplibre/settings_manager'
import { AreaSelectionManager } from './maplibre/area_selection_manager'
import { VisitsManager } from './maplibre/visits_manager'
import { PlacesManager } from './maplibre/places_manager'
import { RoutesManager } from './maplibre/routes_manager'

/**
 * Main map controller for Maps V2
 * Coordinates between different managers and handles UI interactions
 */
export default class extends Controller {
  static values = {
    apiKey: String,
    startDate: String,
    endDate: String,
    timezone: String
  }

  static targets = [
    'container',
    'loading',
    'loadingText',
    'monthSelect',
    'clusterToggle',
    'settingsPanel',
    'visitsSearch',
    'routeOpacityRange',
    'placesFilters',
    'enableAllPlaceTagsToggle',
    'fogRadiusValue',
    'fogThresholdValue',
    'metersBetweenValue',
    'minutesBetweenValue',
    // Search
    'searchInput',
    'searchResults',
    // Layer toggles
    'pointsToggle',
    'routesToggle',
    'heatmapToggle',
    'visitsToggle',
    'photosToggle',
    'areasToggle',
    'placesToggle',
    'fogToggle',
    'scratchToggle',
    'familyToggle',
    // Speed-colored routes
    'routesOptions',
    'speedColoredToggle',
    'speedColorScaleContainer',
    'speedColorScaleInput',
    // Family members
    'familyMembersList',
    'familyMembersContainer',
    // Area selection
    'selectAreaButton',
    'selectionActions',
    'deleteButtonText',
    'selectedVisitsContainer',
    'selectedVisitsBulkActions',
    // Info display
    'infoDisplay',
    'infoTitle',
    'infoContent',
    'infoActions'
  ]

  async connect() {
    this.cleanup = new CleanupHelper()

    // Initialize API and settings
    SettingsManager.initialize(this.apiKeyValue)
    this.settingsController = new SettingsController(this)
    await this.settingsController.loadSettings()
    this.settings = this.settingsController.settings

    // Sync toggle states with loaded settings
    this.settingsController.syncToggleStates()

    await this.initializeMap()
    this.initializeAPI()

    // Initialize managers
    this.layerManager = new LayerManager(this.map, this.settings, this.api)
    this.dataLoader = new DataLoader(this.api, this.apiKeyValue, this.settings)
    this.eventHandlers = new EventHandlers(this.map, this)
    this.filterManager = new FilterManager(this.dataLoader)
    this.mapDataManager = new MapDataManager(this)

    // Initialize feature managers
    this.areaSelectionManager = new AreaSelectionManager(this)
    this.visitsManager = new VisitsManager(this)
    this.placesManager = new PlacesManager(this)
    this.routesManager = new RoutesManager(this)

    // Initialize search manager
    this.initializeSearch()

    // Listen for visit and place creation/update events
    this.boundHandleVisitCreated = this.visitsManager.handleVisitCreated.bind(this.visitsManager)
    this.cleanup.addEventListener(document, 'visit:created', this.boundHandleVisitCreated)

    this.boundHandleVisitUpdated = this.visitsManager.handleVisitUpdated.bind(this.visitsManager)
    this.cleanup.addEventListener(document, 'visit:updated', this.boundHandleVisitUpdated)

    this.boundHandlePlaceCreated = this.placesManager.handlePlaceCreated.bind(this.placesManager)
    this.cleanup.addEventListener(document, 'place:created', this.boundHandlePlaceCreated)

    this.boundHandlePlaceUpdated = this.placesManager.handlePlaceUpdated.bind(this.placesManager)
    this.cleanup.addEventListener(document, 'place:updated', this.boundHandlePlaceUpdated)

    this.boundHandleAreaCreated = this.handleAreaCreated.bind(this)
    this.cleanup.addEventListener(document, 'area:created', this.boundHandleAreaCreated)

    // Format initial dates
    this.startDateValue = DateManager.formatDateForAPI(new Date(this.startDateValue))
    this.endDateValue = DateManager.formatDateForAPI(new Date(this.endDateValue))
    console.log('[Maps V2] Initial dates:', this.startDateValue, 'to', this.endDateValue)

    this.loadMapData()
  }

  disconnect() {
    this.searchManager?.destroy()
    this.cleanup.cleanup()
    this.map?.remove()
    performanceMonitor.logReport()
  }

  /**
   * Initialize MapLibre map
   */
  async initializeMap() {
    this.map = await MapInitializer.initialize(this.containerTarget, {
      mapStyle: this.settings.mapStyle
    })
  }

  /**
   * Initialize API client
   */
  initializeAPI() {
    this.api = new ApiClient(this.apiKeyValue)
  }

  /**
   * Initialize location search
   */
  initializeSearch() {
    if (!this.hasSearchInputTarget || !this.hasSearchResultsTarget) {
      console.warn('[Maps V2] Search targets not found, search functionality disabled')
      return
    }

    this.searchManager = new SearchManager(this.map, this.apiKeyValue)
    this.searchManager.initialize(this.searchInputTarget, this.searchResultsTarget)

    console.log('[Maps V2] Search manager initialized')
  }

  /**
   * Load map data from API
   */
  async loadMapData(options = {}) {
    return this.mapDataManager.loadMapData(
      this.startDateValue,
      this.endDateValue,
      {
        ...options,
        onProgress: this.updateLoadingProgress.bind(this)
      }
    )
  }

  /**
   * Month selector changed
   */
  monthChanged(event) {
    const { startDate, endDate } = DateManager.parseMonthSelector(event.target.value)
    this.startDateValue = startDate
    this.endDateValue = endDate

    console.log('[Maps V2] Date range changed:', this.startDateValue, 'to', this.endDateValue)
    this.loadMapData()
  }

  /**
   * Show loading indicator
   */
  showLoading() {
    this.loadingTarget.classList.remove('hidden')
  }

  /**
   * Hide loading indicator
   */
  hideLoading() {
    this.loadingTarget.classList.add('hidden')
  }

  /**
   * Update loading progress
   */
  updateLoadingProgress({ loaded, totalPages, progress }) {
    if (this.hasLoadingTextTarget) {
      const percentage = Math.round(progress * 100)
      this.loadingTextTarget.textContent = `Loading... ${percentage}%`
    }
  }

  /**
   * Toggle settings panel
   */
  toggleSettings() {
    if (this.hasSettingsPanelTarget) {
      this.settingsPanelTarget.classList.toggle('open')
    }
  }

  // ===== Delegated Methods to Managers =====

  // Settings Controller methods
  updateMapStyle(event) { return this.settingsController.updateMapStyle(event) }
  resetSettings() { return this.settingsController.resetSettings() }
  updateRouteOpacity(event) { return this.settingsController.updateRouteOpacity(event) }
  updateAdvancedSettings(event) { return this.settingsController.updateAdvancedSettings(event) }
  updateFogRadiusDisplay(event) { return this.settingsController.updateFogRadiusDisplay(event) }
  updateFogThresholdDisplay(event) { return this.settingsController.updateFogThresholdDisplay(event) }
  updateMetersBetweenDisplay(event) { return this.settingsController.updateMetersBetweenDisplay(event) }
  updateMinutesBetweenDisplay(event) { return this.settingsController.updateMinutesBetweenDisplay(event) }

  // Area Selection Manager methods
  startSelectArea() { return this.areaSelectionManager.startSelectArea() }
  cancelAreaSelection() { return this.areaSelectionManager.cancelAreaSelection() }
  deleteSelectedPoints() { return this.areaSelectionManager.deleteSelectedPoints() }

  // Visits Manager methods
  toggleVisits(event) { return this.visitsManager.toggleVisits(event) }
  searchVisits(event) { return this.visitsManager.searchVisits(event) }
  filterVisits(event) { return this.visitsManager.filterVisits(event) }
  startCreateVisit() { return this.visitsManager.startCreateVisit() }

  // Places Manager methods
  togglePlaces(event) { return this.placesManager.togglePlaces(event) }
  filterPlacesByTags(event) { return this.placesManager.filterPlacesByTags(event) }
  toggleAllPlaceTags(event) { return this.placesManager.toggleAllPlaceTags(event) }
  startCreatePlace() { return this.placesManager.startCreatePlace() }

  // Area creation
  startCreateArea() {
    console.log('[Maps V2] Starting create area mode')

    if (this.hasSettingsPanelTarget && this.settingsPanelTarget.classList.contains('open')) {
      this.toggleSettings()
    }

    // Find area drawer controller on the same element
    const drawerController = this.application.getControllerForElementAndIdentifier(
      this.element,
      'area-drawer'
    )

    if (drawerController) {
      console.log('[Maps V2] Area drawer controller found, starting drawing with map:', this.map)
      drawerController.startDrawing(this.map)
    } else {
      console.error('[Maps V2] Area drawer controller not found')
      Toast.error('Area drawer controller not available')
    }
  }

  async handleAreaCreated(event) {
    console.log('[Maps V2] Area created:', event.detail.area)

    try {
      // Fetch all areas from API
      const areas = await this.api.fetchAreas()
      console.log('[Maps V2] Fetched areas:', areas.length)

      // Convert to GeoJSON
      const areasGeoJSON = this.dataLoader.areasToGeoJSON(areas)
      console.log('[Maps V2] Converted to GeoJSON:', areasGeoJSON.features.length, 'features')
      if (areasGeoJSON.features.length > 0) {
        console.log('[Maps V2] First area GeoJSON:', JSON.stringify(areasGeoJSON.features[0], null, 2))
      }

      // Get or create the areas layer
      let areasLayer = this.layerManager.getLayer('areas')
      console.log('[Maps V2] Areas layer exists?', !!areasLayer, 'visible?', areasLayer?.visible)

      if (areasLayer) {
        // Update existing layer
        areasLayer.update(areasGeoJSON)
        console.log('[Maps V2] Areas layer updated')
      } else {
        // Create the layer if it doesn't exist yet
        console.log('[Maps V2] Creating areas layer')
        this.layerManager._addAreasLayer(areasGeoJSON)
        areasLayer = this.layerManager.getLayer('areas')
        console.log('[Maps V2] Areas layer created, visible?', areasLayer?.visible)
      }

      // Enable the layer if it wasn't already
      if (areasLayer) {
        if (!areasLayer.visible) {
          console.log('[Maps V2] Showing areas layer')
          areasLayer.show()
          this.settings.layers.areas = true
          this.settingsController.saveSetting('layers.areas', true)

          // Update toggle state
          if (this.hasAreasToggleTarget) {
            this.areasToggleTarget.checked = true
          }
        } else {
          console.log('[Maps V2] Areas layer already visible')
        }
      }

      Toast.success('Area created successfully!')
    } catch (error) {
      console.error('[Maps V2] Failed to reload areas:', error)
      Toast.error('Failed to reload areas')
    }
  }

  // Routes Manager methods
  togglePoints(event) { return this.routesManager.togglePoints(event) }
  toggleRoutes(event) { return this.routesManager.toggleRoutes(event) }
  toggleHeatmap(event) { return this.routesManager.toggleHeatmap(event) }
  toggleFog(event) { return this.routesManager.toggleFog(event) }
  toggleScratch(event) { return this.routesManager.toggleScratch(event) }
  togglePhotos(event) { return this.routesManager.togglePhotos(event) }
  toggleAreas(event) { return this.routesManager.toggleAreas(event) }
  toggleTracks(event) { return this.routesManager.toggleTracks(event) }
  toggleSpeedColoredRoutes(event) { return this.routesManager.toggleSpeedColoredRoutes(event) }
  openSpeedColorEditor() { return this.routesManager.openSpeedColorEditor() }
  handleSpeedColorSave(event) { return this.routesManager.handleSpeedColorSave(event) }
  toggleFamily(event) { return this.routesManager.toggleFamily(event) }

  // Family Members methods
  async loadFamilyMembers() {
    try {
      const response = await fetch(`/api/v1/families/locations?api_key=${this.apiKeyValue}`, {
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        }
      })

      if (!response.ok) {
        if (response.status === 403) {
          console.warn('[Maps V2] Family feature not enabled or user not in family')
          Toast.info('Family feature not available')
          return
        }
        throw new Error(`HTTP error! status: ${response.status}`)
      }

      const data = await response.json()
      const locations = data.locations || []

      // Update family layer with locations
      const familyLayer = this.layerManager.getLayer('family')
      if (familyLayer) {
        familyLayer.loadMembers(locations)
      }

      // Render family members list
      this.renderFamilyMembersList(locations)

      Toast.success(`Loaded ${locations.length} family member(s)`)
    } catch (error) {
      console.error('[Maps V2] Failed to load family members:', error)
      Toast.error('Failed to load family members')
    }
  }

  renderFamilyMembersList(locations) {
    if (!this.hasFamilyMembersContainerTarget) return

    const container = this.familyMembersContainerTarget

    if (locations.length === 0) {
      container.innerHTML = '<p class="text-xs text-base-content/60">No family members sharing location</p>'
      return
    }

    container.innerHTML = locations.map(location => {
      const emailInitial = location.email?.charAt(0)?.toUpperCase() || '?'
      const color = this.getFamilyMemberColor(location.user_id)
      const lastSeen = new Date(location.updated_at).toLocaleString('en-US', {
        timeZone: this.timezoneValue || 'UTC',
        month: 'short',
        day: 'numeric',
        hour: 'numeric',
        minute: '2-digit'
      })

      return `
        <div class="flex items-center gap-2 p-2 hover:bg-base-200 rounded-lg cursor-pointer transition-colors"
             data-action="click->maps--maplibre#centerOnFamilyMember"
             data-member-id="${location.user_id}">
          <div style="background-color: ${color}; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; flex-shrink: 0;">
            ${emailInitial}
          </div>
          <div class="flex-1 min-w-0">
            <div class="text-sm font-medium truncate">${location.email || 'Unknown'}</div>
            <div class="text-xs text-base-content/60">${lastSeen}</div>
          </div>
        </div>
      `
    }).join('')
  }

  getFamilyMemberColor(userId) {
    const colors = [
      '#3b82f6', '#10b981', '#f59e0b',
      '#ef4444', '#8b5cf6', '#ec4899'
    ]
    // Use user ID to get consistent color
    const hash = userId.toString().split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
    return colors[hash % colors.length]
  }

  centerOnFamilyMember(event) {
    const memberId = event.currentTarget.dataset.memberId
    if (!memberId) return

    const familyLayer = this.layerManager.getLayer('family')
    if (familyLayer) {
      familyLayer.centerOnMember(parseInt(memberId))
      Toast.success('Centered on family member')
    }
  }

  // Info Display methods
  showInfo(title, content, actions = []) {
    if (!this.hasInfoDisplayTarget) return

    // Set title
    this.infoTitleTarget.textContent = title

    // Set content
    this.infoContentTarget.innerHTML = content

    // Set actions
    if (actions.length > 0) {
      this.infoActionsTarget.innerHTML = actions.map(action => {
        if (action.type === 'button') {
          // For button actions (modals, etc.), create a button with data-action
          // Use error styling for delete buttons
          const buttonClass = action.label === 'Delete' ? 'btn btn-sm btn-error' : 'btn btn-sm btn-primary'
          return `<button class="${buttonClass}" data-action="click->maps--maplibre#${action.handler}" data-id="${action.id}" data-entity-type="${action.entityType}">${action.label}</button>`
        } else {
          // For link actions, keep the original behavior
          return `<a href="${action.url}" class="btn btn-sm btn-primary">${action.label}</a>`
        }
      }).join('')
    } else {
      this.infoActionsTarget.innerHTML = ''
    }

    // Show info display
    this.infoDisplayTarget.classList.remove('hidden')

    // Switch to tools tab and open panel
    this.switchToToolsTab()
  }

  closeInfo() {
    if (!this.hasInfoDisplayTarget) return
    this.infoDisplayTarget.classList.add('hidden')
  }

  /**
   * Handle edit action from info display
   */
  handleEdit(event) {
    const button = event.currentTarget
    const id = button.dataset.id
    const entityType = button.dataset.entityType

    console.log('[Maps V2] Opening edit for', entityType, id)

    switch (entityType) {
      case 'visit':
        this.openVisitModal(id)
        break
      case 'place':
        this.openPlaceEditModal(id)
        break
      default:
        console.warn('[Maps V2] Unknown entity type:', entityType)
    }
  }

  /**
   * Handle delete action from info display
   */
  handleDelete(event) {
    const button = event.currentTarget
    const id = button.dataset.id
    const entityType = button.dataset.entityType

    console.log('[Maps V2] Deleting', entityType, id)

    switch (entityType) {
      case 'area':
        this.deleteArea(id)
        break
      default:
        console.warn('[Maps V2] Unknown entity type for delete:', entityType)
    }
  }

  /**
   * Open visit edit modal
   */
  async openVisitModal(visitId) {
    try {
      // Fetch visit details
      const response = await fetch(`/api/v1/visits/${visitId}`, {
        headers: {
          'Authorization': `Bearer ${this.apiKeyValue}`,
          'Content-Type': 'application/json'
        }
      })

      if (!response.ok) {
        throw new Error(`Failed to fetch visit: ${response.status}`)
      }

      const visit = await response.json()

      // Trigger visit edit event
      const event = new CustomEvent('visit:edit', {
        detail: { visit },
        bubbles: true
      })
      document.dispatchEvent(event)
    } catch (error) {
      console.error('[Maps V2] Failed to load visit:', error)
      Toast.error('Failed to load visit details')
    }
  }

  /**
   * Delete area with confirmation
   */
  async deleteArea(areaId) {
    try {
      // Fetch area details
      const area = await this.api.fetchArea(areaId)

      // Show delete confirmation
      const confirmed = confirm(`Delete area "${area.name}"?\n\nThis action cannot be undone.`)

      if (!confirmed) return

      Toast.info('Deleting area...')

      // Delete the area
      await this.api.deleteArea(areaId)

      // Reload areas
      const areas = await this.api.fetchAreas()
      const areasGeoJSON = this.dataLoader.areasToGeoJSON(areas)

      const areasLayer = this.layerManager.getLayer('areas')
      if (areasLayer) {
        areasLayer.update(areasGeoJSON)
      }

      // Close info display
      this.closeInfo()

      Toast.success('Area deleted successfully')
    } catch (error) {
      console.error('[Maps V2] Failed to delete area:', error)
      Toast.error('Failed to delete area')
    }
  }

  /**
   * Open place edit modal
   */
  async openPlaceEditModal(placeId) {
    try {
      // Fetch place details
      const response = await fetch(`/api/v1/places/${placeId}`, {
        headers: {
          'Authorization': `Bearer ${this.apiKeyValue}`,
          'Content-Type': 'application/json'
        }
      })

      if (!response.ok) {
        throw new Error(`Failed to fetch place: ${response.status}`)
      }

      const place = await response.json()

      // Trigger place edit event
      const event = new CustomEvent('place:edit', {
        detail: { place },
        bubbles: true
      })
      document.dispatchEvent(event)
    } catch (error) {
      console.error('[Maps V2] Failed to load place:', error)
      Toast.error('Failed to load place details')
    }
  }

  switchToToolsTab() {
    // Open the panel if it's not already open
    if (!this.settingsPanelTarget.classList.contains('open')) {
      this.toggleSettings()
    }

    // Find the map-panel controller and switch to tools tab
    const panelElement = this.settingsPanelTarget
    const panelController = this.application.getControllerForElementAndIdentifier(panelElement, 'map-panel')

    if (panelController && panelController.switchToTab) {
      panelController.switchToTab('tools')
    }
  }
}
