import { useEffect, useRef, useState } from 'react' import { formatError } from '../lib/formatError' import { datasetsApi } from '../services/api/datasets' import type { DatasetCreateResponse, VectorSelectionBBox, VectorSelectionResponse } from '../types' import { terrainSelectionToMapSelection } from '../lib/terrainSelection' import { floodHazardSelectionToMapSelection } from '../lib/floodHazardSelection' import { thematicRasterSelectionToMapSelection } from '../lib/thematicRaster' export type MapThemeAcquisitionKind = 'thematic_raster' | 'dhmv' | 'flood_hazard' | 'grb' | 'official_vector' export interface MapThemeAcquisition { kind: MapThemeAcquisitionKind productKey: string displayName: string } export interface MapThemeQuery { themeId: TThemeId dataset?: DatasetCreateResponse partitioned?: boolean acquisition?: MapThemeAcquisition } export interface MapThemeInsight { themeId: TThemeId dataset: DatasetCreateResponse partitioned?: boolean acquisition?: MapThemeAcquisition result: VectorSelectionResponse } export function useMapThemeSelectionInsights( selectedProjectId: string | null, onDatasetsChanged?: () => Promise, ) { const [themeInsights, setThemeInsights] = useState>>([]) const [themeInsightsLoading, setThemeInsightsLoading] = useState(false) const [themeInsightsError, setThemeInsightsError] = useState(null) const requestSequence = useRef(0) useEffect(() => { requestSequence.current += 1 setThemeInsights([]) setThemeInsightsError(null) setThemeInsightsLoading(false) }, [selectedProjectId]) const clearThemeInsights = () => { requestSequence.current += 1 setThemeInsights([]) setThemeInsightsError(null) setThemeInsightsLoading(false) } const loadThemeInsights = async ( bbox: VectorSelectionBBox, queries: Array>, areaId?: string, ): Promise>> => { if (!selectedProjectId) { setThemeInsights([]) setThemeInsightsError('Open eerst een project om de selectie te analyseren.') return [] } const sequence = requestSequence.current + 1 requestSequence.current = sequence setThemeInsightsLoading(true) setThemeInsightsError(null) try { const settled = await Promise.allSettled( queries.map(async ({ themeId, dataset: existingDataset, partitioned, acquisition }) => { let dataset = existingDataset if (acquisition) { const commonPayload = { bbox, area_id: areaId, force_refresh: false, } const acquisitionJob = acquisition.kind === 'thematic_raster' ? await datasetsApi.acquireThematicRaster(selectedProjectId, { ...commonPayload, product_key: acquisition.productKey, }) : acquisition.kind === 'dhmv' ? await datasetsApi.acquireDhmv(selectedProjectId, { ...commonPayload, product_key: acquisition.productKey as 'dtm_1m' | 'dsm_1m', }) : acquisition.kind === 'flood_hazard' ? await datasetsApi.acquireFloodHazard(selectedProjectId, { ...commonPayload, product_key: acquisition.productKey, }) : acquisition.kind === 'grb' ? await datasetsApi.acquireGrb(selectedProjectId, { ...commonPayload, product_key: acquisition.productKey as 'buildings' | 'roads' | 'water' | 'parcels', }) : await datasetsApi.acquireOfficialVector(selectedProjectId, { ...commonPayload, product_key: acquisition.productKey, }) if (acquisitionJob.status !== 'success' || !acquisitionJob.output_dataset_id) { throw new Error( acquisitionJob.error_message || `De officiële kaartbron ${acquisition.displayName} kon niet worden ingeladen.`, ) } dataset = await datasetsApi.get(selectedProjectId, acquisitionJob.output_dataset_id) } if (!dataset) { throw new Error(`Geen persistente databron beschikbaar voor thema ${themeId}.`) } return { themeId, dataset, partitioned, acquisition, result: dataset.dataset_type === 'raster' && dataset.source_name === 'digitaal_vlaanderen_dhmv' ? terrainSelectionToMapSelection( partitioned ? await datasetsApi.selectTerrainPartitions(selectedProjectId, { bbox, area_id: areaId, product_key: String(dataset.source_metadata?.['product_key'] ?? 'dtm_1m'), }) : await datasetsApi.selectTerrain(selectedProjectId, dataset.id, { bbox, area_id: areaId, }), ) : dataset.dataset_type === 'raster' && dataset.source_name === 'vmm_flood_hazard' ? floodHazardSelectionToMapSelection( partitioned ? await datasetsApi.selectFloodHazardPartitions(selectedProjectId, { bbox, area_id: areaId, product_key: String(dataset.source_metadata?.['product_key'] ?? 'pluviaal_current_t100'), }) : await datasetsApi.selectFloodHazard(selectedProjectId, dataset.id, { bbox, area_id: areaId, }), ) : dataset.dataset_type === 'raster' && dataset.source_name === 'department_omgeving_thematic_raster' ? thematicRasterSelectionToMapSelection(await datasetsApi.selectThematicRaster(selectedProjectId, dataset.id, { bbox, area_id: areaId, })) : dataset.source_name === 'vmm_vha_bathymetry_profiles' && partitioned ? await datasetsApi.selectBathymetryProfilePartitions(selectedProjectId, { bbox, area_id: areaId, limit: 1000, }) : await datasetsApi.selectVectorFeatures(selectedProjectId, dataset.id, { bbox, area_id: areaId, limit: 1000, }), } }), ) const successful = settled.flatMap((item) => (item.status === 'fulfilled' ? [item.value] : [])) const failures = settled.flatMap((item, index) => ( item.status === 'rejected' ? [{ dataset: queries[index]?.dataset?.name ?? queries[index]?.acquisition?.displayName ?? queries[index]?.themeId ?? 'Onbekende bron', reason: formatError(item.reason, 'Bron kon niet worden bevraagd.'), }] : [] )) const failureCount = failures.length if (requestSequence.current !== sequence) { return [] } setThemeInsights(successful) let refreshFailure: string | null = null if (successful.some((item) => item.acquisition) && onDatasetsChanged) { try { await onDatasetsChanged() } catch (error) { refreshFailure = formatError(error, 'De datasetlijst kon na de analyse niet worden vernieuwd.') } } if (failureCount > 0) { const details = failures .slice(0, 4) .map((failure) => `${failure.dataset}: ${failure.reason}`) .join(' · ') const remainder = failureCount > 4 ? ` · en ${failureCount - 4} andere` : '' setThemeInsightsError( `${failureCount} beschikbare databron${failureCount === 1 ? '' : 'nen'} kon niet worden bevraagd. ${details}${remainder}`, ) } else if (refreshFailure) { setThemeInsightsError(refreshFailure) } return successful } catch (error) { if (requestSequence.current !== sequence) { return [] } setThemeInsights([]) setThemeInsightsError(formatError(error, 'De gebiedsanalyse is mislukt.')) return [] } finally { if (requestSequence.current === sequence) { setThemeInsightsLoading(false) } } } return { themeInsights, themeInsightsLoading, themeInsightsError, loadThemeInsights, clearThemeInsights, } }