import { useEffect, useState } from 'react' import { externalApi } from '../services/api' import type { CoverageResolveResponse, VectorSelectionBBox } from '../types' import { COVERAGE_RESPONSE_BUDGET_MS, exceedsPerformanceBudget, } from '../lib/performanceBudget' interface CoverageResolverOptions { projectId: string | null bbox: VectorSelectionBBox | null } export function useCoverageResolver({ projectId, bbox }: CoverageResolverOptions) { const [coverage, setCoverage] = useState(null) const [loadingCoverage, setLoadingCoverage] = useState(false) const [coverageError, setCoverageError] = useState(null) const [coverageDurationMs, setCoverageDurationMs] = useState(null) useEffect(() => { if (!projectId || !bbox) { setCoverage(null) setCoverageError(null) setLoadingCoverage(false) setCoverageDurationMs(null) return } let cancelled = false // A new AOI must never temporarily display the previous AOI's coverage. // Clear immediately; the debounce only postpones the network request. setCoverage(null) setCoverageError(null) setLoadingCoverage(true) setCoverageDurationMs(null) const timer = window.setTimeout(() => { const startedAt = Date.now() externalApi.resolveCoverage({ projectId, bbox: { minx: bbox.min_x, miny: bbox.min_y, maxx: bbox.max_x, maxy: bbox.max_y, }, }) .then((result) => { if (!cancelled) { setCoverage(result) } }) .catch((error) => { if (!cancelled) { setCoverage(null) setCoverageError(error instanceof Error ? error.message : 'Dekking kon niet worden bepaald') } }) .finally(() => { if (!cancelled) { setCoverageDurationMs(Date.now() - startedAt) setLoadingCoverage(false) } }) }, 250) return () => { cancelled = true window.clearTimeout(timer) } }, [projectId, bbox?.min_x, bbox?.min_y, bbox?.max_x, bbox?.max_y]) return { coverage, loadingCoverage, coverageError, coverageDurationMs, coverageBudgetExceeded: exceedsPerformanceBudget( coverageDurationMs, COVERAGE_RESPONSE_BUDGET_MS, ), } }