import { connectorFetch } from "std::data/connector"

/** @module
  @summary Read economic time series from DBnomics.
  ## DBnomics — world macroeconomic time-series

  Fetch economic data from [DBnomics](https://db.nomics.world/) using a
  provider, dataset, and series code. Browse the DBnomics directory to find
  codes for national or cross-country statistics. No API key is required.
  For U.S. macro data, prefer `std::data/finance/fred`.

  Returns a series as a list of `{ period, value }` observations (period like "2025-01"; a
  missing value is null).

  ### Usage

  ```ts
  import { dbnomicsSeries } from "std::data/finance/dbnomics"

  node main() {
    const series = dbnomicsSeries("BLS", "cu", "CUUR0000SA0") catch { providerCode: "", datasetCode: "", seriesCode: "", seriesName: "", frequency: "", observations: [] }
    print("${series.seriesName}: ${series.observations.length} observations")
  }
  ```
*/

effect std::dbnomics { provider: string, dataset: string, series: string }

/** A single DBnomics observation. `period` is a label like "2025-01"; `value` may be null. */
export type DbnomicsObservation = {
  period: string;
  value: number | null
}

/** A DBnomics data series with its provider/dataset/series identity and observations. */
export type DbnomicsSeries = {
  providerCode: string;
  datasetCode: string;
  seriesCode: string;
  seriesName: string;
  frequency: string;
  observations: DbnomicsObservation[]
}

// The std::dbnomics interrupt gates the call; the fetch is approved at the call site
// (an outer handler can still reject std::http::fetchJSON).
const DBNOMICS_BASE = "https://api.db.nomics.world/v22/"
const DBNOMICS_DOMAINS = ["api.db.nomics.world"]

/* Build the DBnomics series path (codes are URL-encoded). */
def buildDbnomicsPath(provider: string, dataset: string, series: string): string {
  const p = encodeURIComponent(provider)
  const d = encodeURIComponent(dataset)
  const s = encodeURIComponent(series)
  return "series/${p}/${d}/${s}?observations=1"
}

/* Reshape a DBnomics series response (zips parallel period/value arrays). */
def parseDbnomics(raw: any): DbnomicsSeries {
  const r: any = raw ?? {}
  const series: any = r.series ?? {}
  const docs = series.docs ?? []
  const doc: any = docs[0] ?? {}
  const periods = doc.period ?? []
  const values = doc.value ?? []
  const observations = map(range(periods.length)) as i {
    return { period: periods[i] ?? "", value: values[i] ?? null }
  }
  return {
    providerCode: doc.provider_code ?? "",
    datasetCode: doc.dataset_code ?? "",
    seriesCode: doc.series_code ?? "",
    seriesName: doc.series_name ?? "",
    frequency: doc["@frequency"] ?? "",
    observations: observations
  }
}

/* Finalize a DBnomics fetch into a Result. */
def dbnomicsFinalize(fetchResult: any): Result {
  return match (fetchResult) {
    success(body) => {
      const b: any = body ?? {}
      const series: any = b.series ?? {}
      const docs = series.docs ?? []
      if (docs.length == 0) {
        return failure("DBnomics returned no series (check the provider/dataset/series codes)")
      }
      return success(parseDbnomics(body))
    }
    failure(err) => failure("DBnomics request failed: ${err}")
  }
}

@capabilities(network)
export def dbnomicsSeries(provider: string, dataset: string, series: string): Result<DbnomicsSeries> raises <std::dbnomics, std::http::fetchJSON> {
  """
  Fetch an economic time series from DBnomics. Use it for non-U.S. or
  cross-country data; prefer fredSeries for U.S. macro data.

  @param provider - The DBnomics provider code, such as "BLS" or "IMF".
  @param dataset - The dataset code within that provider, such as "cu".
  @param series - The series code within that dataset, such as "CUUR0000SA0".
  """
  return interrupt std::dbnomics("Fetch this DBnomics series?", {
    provider: provider,
    dataset: dataset,
    series: series
  })
  const path = buildDbnomicsPath(provider, dataset, series)
  // Bind the fetch to its own statement so std::http::fetchJSON fires at statement level.
  const result = connectorFetch(DBNOMICS_BASE, DBNOMICS_DOMAINS, path) with approve
  return dbnomicsFinalize(result)
}
