import type {
  MapViewState,
  PickingInfo,
  WebMercatorViewport,
} from '@deck.gl/core'
import type {
  DeckGLComponentProps,
  InstanceRef,
  MapComponentProps,
  OverlayRef,
} from '../../types'
import type { SpatialFilter } from '@carto/api-client'
import type { Position } from 'geojson'

export type SpatialFilterWithGroup = SpatialFilter & {
  groupId?: number
}

interface ValueProps {
  id: MapComponentProps['id']
  /**
   * The view state of the map.
   * @deprecated Use `initialViewState` instead.
   */
  viewState?: MapComponentProps['viewState']
  initialViewState: MapComponentProps['viewState']
  isLoaded: boolean
  basemap: NonNullable<MapComponentProps['basemap']>
  layers?: string[]
  spatialFilter?: Record<string, SpatialFilterWithGroup | null>
  controller?: DeckGLComponentProps['controller']
}

export interface MapProviderProps {
  values: Omit<ValueProps, 'isLoaded'>[]
}

export interface MapSlice {
  maps: Record<
    ValueProps['id'],
    Pick<ValueProps, 'basemap' | 'isLoaded' | 'controller'> & {
      instanceRef?: MapComponentProps['instanceRef']
      overlayRef?: MapComponentProps['overlayRef']
    } & {
      layers: NonNullable<ValueProps['layers']>
    }
  >
  setBasemap: (id: ValueProps['id'], basemap: ValueProps['basemap']) => void
  setLoad: (id: ValueProps['id'], isLoaded: boolean) => void
  getController: (id: ValueProps['id']) => DeckGLComponentProps['controller']
  setController: (
    id: ValueProps['id'],
    controller: DeckGLComponentProps['controller'],
  ) => void
  getDeckInstance: (id: ValueProps['id']) => InstanceRef['deck']
  getOverlayInstance: (id: ValueProps['id']) => OverlayRef
  getViewState: (id: ValueProps['id']) => ValueProps['viewState']
  getViewports: (
    id: ValueProps['id'],
    rect?: ViewportRect,
  ) => WebMercatorViewport[] | undefined
  getViewport: (
    id: ValueProps['id'],
    index?: number,
    rect?: ViewportRect,
  ) => WebMercatorViewport | undefined
  getBounds: (
    id: ValueProps['id'],
    bounds: Parameters<WebMercatorViewport['fitBounds']>[0],
    options?: {
      boundsOptions?: Parameters<WebMercatorViewport['fitBounds']>[1]
      viewStateOptions?: Omit<MapViewState, 'latitude' | 'longitude' | 'zoom'>
    },
  ) => MapViewState | undefined
  addLayers: (
    id: ValueProps['id'],
    layers: NonNullable<ValueProps['layers']>,
  ) => void
  removeLayers: (
    id: ValueProps['id'],
    layer: NonNullable<ValueProps['layers']>,
  ) => void
}

// export type AreLayerLoadedSlice = Record<ValueProps['id'], boolean>
export interface AreLayerLoadedSlice {
  areLayersLoaded: Record<ValueProps['id'], boolean>
  setLayersLoad: (id: ValueProps['id'], isLoaded: boolean) => void
}

export interface AreMapsIdleSlice {
  areMapsIdle: Record<ValueProps['id'], boolean>
  setIdle: (id: ValueProps['id'], isIdle: boolean) => void
}

export interface ViewStateSlice {
  viewports: Record<ValueProps['id'], WebMercatorViewport | null>
  updateViewPort: (
    id: ValueProps['id'],
    viewState?: ValueProps['initialViewState'],
  ) => void
  viewStates: Record<ValueProps['id'], ValueProps['initialViewState']>
  initialViewStates: Record<ValueProps['id'], ValueProps['initialViewState']>
  setInitialViewState: (
    id: MapComponentProps['id'],
    viewState: MapViewState,
  ) => void
  setViewState: (id: MapComponentProps['id'], viewState: MapViewState) => void
  setZoom: (id: MapComponentProps['id'], zoom: MapViewState['zoom']) => void
  setMinZoom: (
    id: MapComponentProps['id'],
    minZoom: MapViewState['minZoom'],
  ) => void
  setMaxZoom: (
    id: MapComponentProps['id'],
    maxZoom: MapViewState['maxPitch'],
  ) => void
}

export interface CommonSlice {
  fitBounds: (
    id: ValueProps['id'],
    bounds: Parameters<WebMercatorViewport['fitBounds']>[0],
    options?: {
      boundsOptions?: Parameters<WebMercatorViewport['fitBounds']>[1]
      viewStateOptions?: Omit<MapViewState, 'latitude' | 'longitude' | 'zoom'>
    },
  ) => void
  resetViewState: (id: ValueProps['id']) => void
  loadInitialViewState: (
    id: ValueProps['id'],
    initialViewState: MapViewState,
  ) => void
  flyTo: (
    id: ValueProps['id'],
    target: {
      longitude?: MapViewState['longitude']
      latitude?: MapViewState['latitude']
      zoom?: MapViewState['zoom']
    },
    options?: {
      viewStateOptions?: Omit<MapViewState, 'latitude' | 'longitude' | 'zoom'>
    },
  ) => void
}

export interface SpatialFilterSlice {
  spatialFilter: Record<
    ValueProps['id'],
    Record<string, SpatialFilterWithGroup | null>
  >
  setSpatialFilter: (
    mapId: ValueProps['id'],
    id: string,
    value: SpatialFilterWithGroup,
  ) => void
  removeSpatialFilter: (mapId: ValueProps['id'], id: string) => void
}

export interface LayerActionsSlice {
  setLayerHover: (
    mapId: ValueProps['id'],
    layerId: string,
    position: Position,
    callback?: (pickedObjects: PickingInfo[]) => PickingInfo | undefined,
  ) => void
  clearLayersHover: (mapId: ValueProps['id']) => void
}

export type MapStore = MapSlice &
  ViewStateSlice &
  CommonSlice &
  AreLayerLoadedSlice &
  AreMapsIdleSlice &
  SpatialFilterSlice &
  LayerActionsSlice

export interface ViewportRect {
  x: number
  y: number
  width?: number
  height?: number
}
