import type { EditableGeoJsonLayerProps } from '@deck.gl-community/editable-layers'
import type { Color } from '@deck.gl/core'
import type { GeoJsonLayerProps } from '@deck.gl/layers'
import type {
  CustomLassoToolsMode,
  LassoToolsMode,
  LassoToolsModes,
  MapComponentProps,
} from '../../types'
import type { GeoJsonEditMode } from '@deck.gl-community/editable-layers'
import type { GeoJsonProperties, Geometry, FeatureCollection } from 'geojson'
import type { ReactNode } from 'react'

export type DataType = 'mask' | 'draw'

/** Mirrors MUI's `AlertColor` without depending on `@mui/material`. */
export type LassoToolsMessageSeverity = 'success' | 'info' | 'warning' | 'error'

/**
 * A message surfaced by the lasso tools (e.g. a self-intersection repair
 * notice), rendered by `LassoTools.Message`.
 */
export interface LassoToolsMessage {
  /**
   * Id of the associated entity (for self-repair notices, the lasso geometry
   * id). `addMessage` dedups by id, so there is at most one message per
   * entity. When it matches a lasso geometry, the message is anchored over
   * that geometry; otherwise it renders in the top-center stack of the map.
   */
  id: string
  title: string
  /** Body rendered under the title. */
  content?: ReactNode
  /** @default 'info' (applied at render, not stored) */
  severity?: LassoToolsMessageSeverity
  action?: ReactNode
}

export interface LassoToolsData {
  id: string
  properties: GeoJsonProperties & {
    label: string
    visible: boolean
    /** When locked, the geometry is read-only: it can be selected (to unlock)
     * but not reshaped, renamed, retyped, or deleted. */
    locked?: boolean
  }
  geometry: Geometry
  type: DataType
  groupId?: number
  layerProps?: GeoJsonLayerProps
}

export interface LassoToolsState<M extends string = LassoToolsMode> {
  mapId: MapComponentProps['id']
  enabled: boolean
  values: Record<LassoToolsData['id'], LassoToolsData>
  modeSelected: LassoToolsMode | null
  modeInstance: CustomLassoToolsMode<GeoJsonEditMode>
  modes: LassoToolsModes<M>
  /** Id of the geometry currently selected for the contextual toolbar. */
  selectedId: LassoToolsData['id'] | null
  /** True while an edit gesture (drag) is in progress, to hide the toolbar. */
  editing: boolean
  /**
   * Layer ids the mask clips. `null` (default) = all eligible layers, preserving
   * the original behavior. When set, only these layers receive the GPU mask.
   */
  maskLayerIds: string[] | null
  /**
   * Messages to surface over the map (e.g. a self-intersection auto-repair
   * notice, see {@link repairSelfIntersection}). Rendered by
   * `LassoTools.Message`; each message is removed via its `onDismiss`.
   */
  messages: LassoToolsMessage[]
  // sources?: string[]
}

export interface LassoToolsLayerProps {
  layerProps: LassoToolsLayersProps<FeatureCollection>
  onEdit: (args: LassoToolsLayerEditProps) => void
  getLineColor: Color | ((d: LassoToolsData) => Color)
  getFillColor: Color | ((d: LassoToolsData) => Color)
  pointColor: {
    fill: Color
    outline: Color
  }
  mode: CustomLassoToolsMode<GeoJsonEditMode>
}

export type LassoToolsLayersProps<D = unknown> = Omit<
  EditableGeoJsonLayerProps<D>,
  'mode' | 'id'
>

export interface LassoToolsLayerEditProps {
  editType: string
  updatedData: FeatureCollection<Geometry, LassoToolsData['properties']>
}

export interface DefaultLassoToolsProps {
  styles: {
    colors: {
      editing: {
        fill: Color
        border: Color
        point: Color
      }
      finished: {
        fill: Color
        border: Color
      }
    }
  }
  labels: {
    mask: string
    draw: string
    /** Warning shown when a self-intersecting geometry was auto-repaired. */
    selfIntersectionRepaired: string
  }
}

export interface LassoToolsProps {
  mapId: string
  defaultProps?: Partial<DefaultLassoToolsProps>
  layerProps?: Partial<Record<DataType, LassoToolsLayerProps['layerProps']>>
  defaultType?: DataType
  /**
   * Layer IDs that should NOT receive the GPU render mask when a lasso
   * polygon is drawn. Useful when a layer must remain visible outside the
   * polygon (e.g. selected boundary outlines, base reference layers).
   * The data-side spatial filter from `useLassoToolsSpatialFilter` is
   * unaffected.
   * @default []
   */
  excludeLayerIds?: string[]
}

export interface LassoToolsControlProps {
  mapId: LassoToolsProps['mapId']
  children: (props: Required<LassoToolsChildrenProps>) => React.ReactNode
}

export type LassoToolsChildrenProps = Partial<
  Omit<
    LassoToolsState,
    | 'values'
    | 'modeInstance'
    | 'selectedId'
    | 'editing'
    | 'maskLayerIds'
    | 'messages'
  >
> & {
  values: Record<
    DataType,
    {
      id: LassoToolsState['values'][number]['id']
      visible: LassoToolsState['values'][number]['properties']['visible']
      label: LassoToolsState['values'][number]['properties']['label']
    }[]
  >
  setValue: (
    id: LassoToolsState['values'][number]['id'],
    data?: Partial<LassoToolsState['values'][number]>,
  ) => void
  setEnabled: (enabled: LassoToolsState['enabled']) => void
  setVisibility: (
    valueId: LassoToolsState['values'][number]['id'],
    visible: LassoToolsState['values'][number]['properties']['visible'],
  ) => void
  setModeSelected: (mode: LassoToolsState['modeSelected']) => void
}
