import airQualityLookup from './air-quality.ts'
import moonLookup from './moon.ts'
import sunLookup from './sun.ts'
import weatherLookup from './weather.ts'
import { resolveWeatherLocation } from './weather-geocode.ts'
export type EnvironmentSnapshotParams = {
location?: string
latitude?: number
longitude?: number
label?: string
date?: string
units?: 'metric' | 'imperial'
/** Set false to skip a section and save the upstream calls. All default to true. */
include?: {
weather?: boolean
airQuality?: boolean
sun?: boolean
moon?: boolean
}
}
function sectionError(error: unknown): { error: string } {
return { error: error instanceof Error ? error.message : String(error) }
}
/**
* One-call outdoor snapshot combining weather, air quality, sun times, and
* moon phase for a single location. Sections degrade independently: a failed
* upstream lookup yields `{ error }` for that section instead of throwing.
*/
export async function environmentSnapshot(
params: EnvironmentSnapshotParams = {},
) {
const input = typeof params === 'object' && params ? params : {}
const include = input.include || {}
// Resolve the location once so all sections agree on coordinates and we
// only hit the geocoder a single time.
const resolvedLocation = await resolveWeatherLocation(input)
const shared = {
latitude: resolvedLocation.latitude,
longitude: resolvedLocation.longitude,
label: resolvedLocation.displayName,
date: input.date,
}
const [weather, airQuality, sun, moon] = await Promise.all([
include.weather === false
? Promise.resolve(null)
: weatherLookup({ ...shared, units: input.units }).catch(sectionError),
include.airQuality === false
? Promise.resolve(null)
: airQualityLookup(shared).catch(sectionError),
include.sun === false
? Promise.resolve(null)
: sunLookup(shared).catch(sectionError),
include.moon === false
? Promise.resolve(null)
: moonLookup({ date: input.date }).catch(sectionError),
])
const summaryLines: string[] = [
'Environment snapshot for ' + resolvedLocation.displayName,
]
for (const section of [weather, airQuality, sun, moon]) {
if (!section) continue
if ('error' in section) continue
if (typeof (section as { summary?: unknown }).summary === 'string') {
summaryLines.push((section as { summary: string }).summary)
}
}
const failed = []
if (weather && 'error' in weather) failed.push('weather: ' + weather.error)
if (airQuality && 'error' in airQuality) {
failed.push('airQuality: ' + airQuality.error)
}
if (sun && 'error' in sun) failed.push('sun: ' + sun.error)
if (moon && 'error' in moon) failed.push('moon: ' + moon.error)
if (failed.length > 0) {
summaryLines.push('Failed sections: ' + failed.join('; '))
}
return {
location: {
displayName: resolvedLocation.displayName,
latitude: resolvedLocation.latitude,
longitude: resolvedLocation.longitude,
source: resolvedLocation.source,
},
weather,
airQuality,
sun,
moon,
summary: summaryLines.join('\n\n'),
}
}
export default environmentSnapshot