import type {
  VectorQuerySourceOptions,
  VectorTableSourceOptions,
  VectorTilesetSourceOptions,
  TileFormat,
  BoundaryQuerySourceOptions,
  BoundaryTableSourceOptions,
  H3QuerySourceOptions,
  QuadbinQuerySourceOptions,
  H3TilesetSourceOptions,
  QuadbinTilesetSourceOptions,
  H3TableSourceOptions,
  QuadbinTableSourceOptions,
  RasterSourceOptions,
  RasterMetadata,
  Filters,
  AddFilterOptions,
  RemoveFilterOptions,
  ApiVersion,
  WidgetQuerySource,
  WidgetTilesetSource,
  WidgetTableSource,
  WidgetRasterSource,
} from '@carto/api-client'
import type { BBox, Feature } from 'geojson'
import type { WidgetGeoJsonSource } from './widgets/widget-geojson-source'

interface ExternalData {
  data: string
  apiVersion?: ApiVersion
  apiBaseUrl?: string
}

interface WithWidgets<T> {
  widgets?: T | null
}

type _Omit<T, P extends string = ''> = Omit<T, 'accessToken' | P>
type OmitQuery<T, P extends string = ''> = _Omit<T, 'sqlQuery' | P>
type OmitTable<T, P extends string = ''> = _Omit<T, 'tableName' | P>

export type VectorQuerySource = {
  type: 'query'
} & OmitQuery<VectorQuerySourceOptions>

export type H3QuerySource = {
  type: 'query+h3'
} & OmitQuery<H3QuerySourceOptions>

export type QuadbinQuerySource = {
  type: 'query+quadbin'
} & OmitQuery<QuadbinQuerySourceOptions>

export type BoundaryQuerySource = {
  type: 'query+boundary'
} & _Omit<BoundaryQuerySourceOptions, 'propertiesSqlQuery'>

export type VectorQueryTilesetSource = {
  type: 'query+tileset'
} & OmitQuery<VectorQuerySourceOptions>

export type H3QueryTilesetSource = {
  type: 'query+tileset+h3'
} & OmitQuery<H3QuerySourceOptions>

export type QuadbinQueryTilesetSource = {
  type: 'query+tileset+quadbin'
} & OmitQuery<QuadbinQuerySourceOptions>

export type QueryTilesetSource = (
  | VectorQueryTilesetSource
  | H3QueryTilesetSource
  | QuadbinQueryTilesetSource
) &
  ExternalData &
  WithWidgets<WidgetQuerySource> &
  Omit<TilesetSource, 'type'> & {
    tileset: string
  }

export type QuerySource = (
  | VectorQuerySource
  | H3QuerySource
  | QuadbinQuerySource
  | BoundaryQuerySource
) &
  ExternalData &
  WithWidgets<WidgetQuerySource>

export type VectorTilesetSource = {
  type: 'tileset'
} & OmitTable<VectorTilesetSourceOptions>

export type H3TilesetSource = {
  type: 'tileset+h3'
} & OmitTable<H3TilesetSourceOptions>

export type QuadbinTilesetSource = {
  type: 'tileset+quadbin'
} & OmitTable<QuadbinTilesetSourceOptions>

export type TilesetSource = (
  | VectorTilesetSource
  | H3TilesetSource
  | QuadbinTilesetSource
) &
  ExternalData &
  WithWidgets<WidgetTilesetSource> & {
    tileFormat: TileFormat
    spatialDataBounds: BBox
  }

export type VectorTableSource = {
  type: 'table'
} & OmitTable<VectorTableSourceOptions>

export type H3TableSource = {
  type: 'table+h3'
} & OmitTable<H3TableSourceOptions>

export type QuadbinTableSource = {
  type: 'table+quadbin'
} & OmitTable<QuadbinTableSourceOptions>

export type BoundaryTableSource = {
  type: 'table+boundary'
} & _Omit<BoundaryTableSourceOptions, 'propertiesTableName'>

export type VectorTableTilesetSource = {
  type: 'table+tileset'
} & OmitTable<VectorTableSourceOptions>

export type H3TableTilesetSource = {
  type: 'table+tileset+h3'
} & OmitTable<H3TableSourceOptions>

export type QuadbinTableTilesetSource = {
  type: 'table+tileset+quadbin'
} & OmitTable<QuadbinTableSourceOptions>

export type TableTilesetSource = (
  | VectorTableTilesetSource
  | H3TableTilesetSource
  | QuadbinTableTilesetSource
) &
  ExternalData &
  WithWidgets<WidgetTableSource> &
  Omit<TilesetSource, 'type'> & {
    tileset: string
  }

export type TableSource = (
  | VectorTableSource
  | H3TableSource
  | QuadbinTableSource
  | BoundaryTableSource
) &
  ExternalData &
  WithWidgets<WidgetTableSource>

export type RasterQuadbinTilesetSource = {
  type: 'raster'
  tileFormat: TileFormat
  rasterMetadata?: RasterMetadata
  spatialDataBounds: BBox
} & OmitTable<RasterSourceOptions>

export type RasterSource = RasterQuadbinTilesetSource &
  ExternalData &
  WithWidgets<WidgetRasterSource>

export type GeoJSONSource = {
  type: 'geojson'
  data: Feature[]
} & WithWidgets<WidgetGeoJsonSource>

export type AllExternalSources =
  | QuerySource
  | TilesetSource
  | TableSource
  | TableTilesetSource
  | QueryTilesetSource
  | RasterSource

export type AllSources = AllExternalSources | GeoJSONSource

export type Source<T extends AllSources = AllSources> = Omit<T, 'filters'> & {
  id: string
}

export interface SourceStore {
  sources: Record<string, Source>
  filters: Record<string, Filters>
  setSource: <T extends AllSources>(
    id: string,
    source: Partial<Source<T>>,
  ) => void
  removeSource: (id: string) => void
  getSource: (id: string) => Source | null
  setFilters: (id: string, filters: AddFilterOptions[]) => void
  removeFilters: (
    id: string,
    column?: RemoveFilterOptions['column'],
    owner?: RemoveFilterOptions['owner'],
  ) => void
  getFilters: (id: string) => Filters | null
  invalidateSourcesCache: (
    ids: string[],
    options?: {
      timeout?: number
      maxAge?: number
    },
  ) => void
}
