// Code generated by protoc-gen-ts-client. DO NOT EDIT.
// source: worldmonitor/economic/v1/service.proto

export interface GetFredSeriesRequest {
  seriesId: string;
  limit: number;
}

export interface GetFredSeriesResponse {
  series?: FredSeries;
}

export interface FredSeries {
  seriesId: string;
  title: string;
  units: string;
  frequency: string;
  observations: FredObservation[];
}

export interface FredObservation {
  date: string;
  value: number;
}

export interface ListWorldBankIndicatorsRequest {
  indicatorCode: string;
  countryCode: string;
  year: number;
  pageSize: number;
  cursor: string;
}

export interface ListWorldBankIndicatorsResponse {
  data: WorldBankCountryData[];
  pagination?: PaginationResponse;
}

export interface WorldBankCountryData {
  countryCode: string;
  countryName: string;
  indicatorCode: string;
  indicatorName: string;
  year: number;
  value: number;
}

export interface PaginationResponse {
  nextCursor: string;
  totalCount: number;
}

export interface GetEnergyPricesRequest {
  commodities: string[];
}

export interface GetEnergyPricesResponse {
  prices: EnergyPrice[];
}

export interface EnergyPrice {
  commodity: string;
  name: string;
  price: number;
  unit: string;
  change: number;
  priceAt: number;
}

export interface GetMacroSignalsRequest {
}

export interface GetMacroSignalsResponse {
  timestamp: string;
  verdict: string;
  bullishCount: number;
  totalCount: number;
  signals?: MacroSignals;
  meta?: MacroMeta;
  unavailable: boolean;
}

export interface MacroSignals {
  liquidity?: LiquiditySignal;
  flowStructure?: FlowStructureSignal;
  macroRegime?: MacroRegimeSignal;
  technicalTrend?: TechnicalTrendSignal;
  hashRate?: HashRateSignal;
  priceMomentum?: PriceMomentumSignal;
  fearGreed?: FearGreedSignal;
}

export interface LiquiditySignal {
  status: string;
  value?: number;
  sparkline: number[];
}

export interface FlowStructureSignal {
  status: string;
  btcReturn5?: number;
  qqqReturn5?: number;
}

export interface MacroRegimeSignal {
  status: string;
  qqqRoc20?: number;
  xlpRoc20?: number;
}

export interface TechnicalTrendSignal {
  status: string;
  btcPrice?: number;
  sma50?: number;
  sma200?: number;
  vwap30d?: number;
  mayerMultiple?: number;
  sparkline: number[];
}

export interface HashRateSignal {
  status: string;
  change30d?: number;
}

export interface PriceMomentumSignal {
  status: string;
}

export interface FearGreedSignal {
  status: string;
  value?: number;
  history: FearGreedHistoryEntry[];
}

export interface FearGreedHistoryEntry {
  value: number;
  date: string;
}

export interface MacroMeta {
  qqqSparkline: number[];
}

export interface GetEnergyCapacityRequest {
  energySources: string[];
  years: number;
}

export interface GetEnergyCapacityResponse {
  series: EnergyCapacitySeries[];
}

export interface EnergyCapacitySeries {
  energySource: string;
  name: string;
  data: EnergyCapacityYear[];
}

export interface EnergyCapacityYear {
  year: number;
  capacityMw: number;
}

export interface GetBisPolicyRatesRequest {
}

export interface GetBisPolicyRatesResponse {
  rates: BisPolicyRate[];
}

export interface BisPolicyRate {
  countryCode: string;
  countryName: string;
  rate: number;
  previousRate: number;
  date: string;
  centralBank: string;
}

export interface GetBisExchangeRatesRequest {
}

export interface GetBisExchangeRatesResponse {
  rates: BisExchangeRate[];
}

export interface BisExchangeRate {
  countryCode: string;
  countryName: string;
  realEer: number;
  nominalEer: number;
  realChange: number;
  date: string;
}

export interface GetBisCreditRequest {
}

export interface GetBisCreditResponse {
  entries: BisCreditToGdp[];
}

export interface BisCreditToGdp {
  countryCode: string;
  countryName: string;
  creditGdpRatio: number;
  previousRatio: number;
  date: string;
}

export interface GetFredSeriesBatchRequest {
  seriesIds: string[];
  limit: number;
}

export interface GetFredSeriesBatchResponse {
  results: Record<string, FredSeries>;
  fetched: number;
  requested: number;
}

export interface ListGroceryBasketPricesRequest {
}

export interface ListGroceryBasketPricesResponse {
  countries: CountryBasket[];
  fetchedAt: string;
  cheapestCountry: string;
  mostExpensiveCountry: string;
  upstreamUnavailable: boolean;
  wowAvgPct: number;
  wowAvailable: boolean;
  prevFetchedAt: string;
}

export interface CountryBasket {
  code: string;
  name: string;
  currency: string;
  flag: string;
  totalUsd: number;
  fxRate: number;
  items: GroceryItemPrice[];
  wowPct: number;
}

export interface GroceryItemPrice {
  itemId: string;
  itemName: string;
  unit: string;
  localPrice: number;
  usdPrice: number;
  currency: string;
  sourceSite: string;
  available: boolean;
}

export interface ListBigMacPricesRequest {
}

export interface ListBigMacPricesResponse {
  countries: BigMacCountryPrice[];
  fetchedAt: string;
  cheapestCountry: string;
  mostExpensiveCountry: string;
  wowAvgPct: number;
  wowAvailable: boolean;
  prevFetchedAt: string;
}

export interface BigMacCountryPrice {
  code: string;
  name: string;
  currency: string;
  flag: string;
  localPrice: number;
  usdPrice: number;
  fxRate: number;
  sourceSite: string;
  available: boolean;
  wowPct: number;
}

export interface GetNationalDebtRequest {
}

export interface GetNationalDebtResponse {
  entries: NationalDebtEntry[];
  seededAt: string;
  unavailable: boolean;
}

export interface NationalDebtEntry {
  iso3: string;
  debtUsd: number;
  gdpUsd: number;
  debtToGdp: number;
  annualGrowth: number;
  perSecondRate: number;
  perDayRate: number;
  baselineTs: string;
  source: string;
}

export interface ListFuelPricesRequest {
}

export interface ListFuelPricesResponse {
  countries: FuelCountryPrice[];
  fetchedAt: string;
  cheapestGasoline: string;
  cheapestDiesel: string;
  mostExpensiveGasoline: string;
  mostExpensiveDiesel: string;
  wowAvailable: boolean;
  prevFetchedAt: string;
  sourceCount: number;
  countryCount: number;
}

export interface FuelCountryPrice {
  code: string;
  name: string;
  currency: string;
  flag: string;
  gasoline?: FuelPrice;
  diesel?: FuelPrice;
  fxRate: number;
}

export interface FuelPrice {
  usdPrice: number;
  localPrice: number;
  grade: string;
  source: string;
  available: boolean;
  wowPct: number;
  observedAt: string;
}

export interface GetBlsSeriesRequest {
  seriesId: string;
  limit: number;
}

export interface GetBlsSeriesResponse {
  series?: BlsSeries;
}

export interface BlsSeries {
  seriesId: string;
  title: string;
  units: string;
  observations: BlsObservation[];
}

export interface BlsObservation {
  year: string;
  period: string;
  periodName: string;
  value: string;
}

export interface GetEconomicCalendarRequest {
  fromDate: string;
  toDate: string;
}

export interface GetEconomicCalendarResponse {
  events: EconomicEvent[];
  fromDate: string;
  toDate: string;
  total: number;
  unavailable: boolean;
}

export interface EconomicEvent {
  event: string;
  country: string;
  date: string;
  impact: string;
  actual: string;
  estimate: string;
  previous: string;
  unit: string;
}

export interface GetCrudeInventoriesRequest {
}

export interface GetCrudeInventoriesResponse {
  weeks: CrudeInventoryWeek[];
  latestPeriod: string;
}

export interface CrudeInventoryWeek {
  period: string;
  stocksMb: number;
  weeklyChangeMb?: number;
}

export interface GetNatGasStorageRequest {
}

export interface GetNatGasStorageResponse {
  weeks: NatGasStorageWeek[];
  latestPeriod: string;
}

export interface NatGasStorageWeek {
  period: string;
  storBcf: number;
  weeklyChangeBcf?: number;
}

export interface GetEcbFxRatesRequest {
}

export interface GetEcbFxRatesResponse {
  rates: EcbFxRate[];
  updatedAt: string;
  seededAt: string;
  unavailable: boolean;
}

export interface EcbFxRate {
  pair: string;
  rate: number;
  date: string;
  change1d: number;
}

export interface GetEurostatCountryDataRequest {
}

export interface GetEurostatCountryDataResponse {
  countries: Record<string, EurostatCountryEntry>;
  seededAt: string;
  unavailable: boolean;
}

export interface EurostatCountryEntry {
  cpi?: EurostatMetric;
  unemployment?: EurostatMetric;
  gdpGrowth?: EurostatMetric;
}

export interface EurostatMetric {
  value: number;
  date: string;
  unit: string;
  priorValue: number;
  hasPrior: boolean;
}

export interface GetEuGasStorageRequest {
}

export interface GetEuGasStorageResponse {
  fillPct: number;
  fillPctChange1d: number;
  gasDaysConsumption: number;
  trend: string;
  history: EuGasStorageHistoryEntry[];
  seededAt: string;
  updatedAt: string;
  unavailable: boolean;
}

export interface EuGasStorageHistoryEntry {
  date: string;
  fillPct: number;
  gasTwh: number;
}

export interface GetEuYieldCurveRequest {
}

export interface GetEuYieldCurveResponse {
  data?: EuYieldCurveData;
  unavailable: boolean;
}

export interface EuYieldCurveData {
  date: string;
  rates: Record<string, number>;
  source: string;
  updatedAt: string;
}

export interface GetEuFsiRequest {
}

export interface GetEuFsiResponse {
  latestValue: number;
  latestDate: string;
  label: string;
  history: EuFsiObservation[];
  seededAt: string;
  unavailable: boolean;
  stale: boolean;
}

export interface EuFsiObservation {
  date: string;
  value: number;
}

export interface GetEconomicStressRequest {
}

export interface GetEconomicStressResponse {
  compositeScore: number;
  label: string;
  components: EconomicStressComponent[];
  seededAt: string;
  unavailable: boolean;
}

export interface EconomicStressComponent {
  id: string;
  label: string;
  rawValue: number;
  score: number;
  weight: number;
  missing: boolean;
}

export interface GetFaoFoodPriceIndexRequest {
}

export interface GetFaoFoodPriceIndexResponse {
  points: FaoFoodPricePoint[];
  fetchedAt: string;
  currentFfpi: number;
  momPct: number;
  yoyPct: number;
}

export interface FaoFoodPricePoint {
  date: string;
  ffpi: number;
  meat: number;
  dairy: number;
  cereals: number;
  oils: number;
  sugar: number;
}

export interface GetOilStocksAnalysisRequest {
}

export interface GetOilStocksAnalysisResponse {
  updatedAt: string;
  dataMonth: string;
  ieaMembers: OilStocksAnalysisMember[];
  belowObligation: string[];
  regionalSummary?: OilStocksRegionalSummary;
  unavailable: boolean;
}

export interface OilStocksAnalysisMember {
  iso2: string;
  daysOfCover?: number;
  netExporter: boolean;
  belowObligation: boolean;
  obligationMet: boolean;
  rank: number;
  vsObligation?: number;
}

export interface OilStocksRegionalSummary {
  europe?: OilStocksRegionalSummaryEurope;
  asiaPacific?: OilStocksRegionalSummaryAsiaPacific;
  northAmerica?: OilStocksRegionalSummaryNorthAmerica;
}

export interface OilStocksRegionalSummaryEurope {
  avgDays?: number;
  minDays?: number;
  countBelowObligation: number;
}

export interface OilStocksRegionalSummaryAsiaPacific {
  avgDays?: number;
  minDays?: number;
  countBelowObligation: number;
}

export interface OilStocksRegionalSummaryNorthAmerica {
  netExporters: number;
  avgDays?: number;
}

export interface GetOilInventoriesRequest {
}

export interface GetOilInventoriesResponse {
  crudeWeeks: CrudeInventoryWeekRef[];
  spr?: OilInventoriesSprSnapshot;
  natGasWeeks: NatGasWeekRef[];
  euGas?: OilInventoriesEuGas;
  ieaStocks?: OilInventoriesIeaStocks;
  refinery?: OilInventoriesRefinery;
  updatedAt: string;
}

export interface CrudeInventoryWeekRef {
  period: string;
  stocksMb: number;
  weeklyChangeMb?: number;
}

export interface OilInventoriesSprSnapshot {
  latestStocksMb: number;
  changeWow: number;
  weeks: OilInventoriesSprWeek[];
}

export interface OilInventoriesSprWeek {
  period: string;
  stocksMb: number;
}

export interface NatGasWeekRef {
  period: string;
  storBcf: number;
  weeklyChangeBcf?: number;
}

export interface OilInventoriesEuGas {
  fillPct: number;
  fillPctChange1d: number;
  trend: string;
  history: OilInventoriesEuGasDay[];
}

export interface OilInventoriesEuGasDay {
  date: string;
  fillPct: number;
}

export interface OilInventoriesIeaStocks {
  dataMonth: string;
  members: OilInventoriesIeaMember[];
  europe?: OilInventoriesRegionStats;
  asiaPacific?: OilInventoriesRegionStats;
  northAmerica?: OilInventoriesRegionStats;
}

export interface OilInventoriesIeaMember {
  iso2: string;
  daysOfCover?: number;
  netExporter: boolean;
  belowObligation: boolean;
}

export interface OilInventoriesRegionStats {
  avgDays?: number;
  minDays?: number;
  countBelowObligation?: number;
}

export interface OilInventoriesRefinery {
  inputsMbpd: number;
  period: string;
}

export interface GetEnergyCrisisPoliciesRequest {
  countryCode: string;
  category: string;
}

export interface GetEnergyCrisisPoliciesResponse {
  source: string;
  sourceUrl: string;
  context: string;
  policies: EnergyCrisisPolicy[];
  updatedAt: string;
  unavailable: boolean;
}

export interface EnergyCrisisPolicy {
  country: string;
  countryCode: string;
  category: string;
  sector: string;
  measure: string;
  dateAnnounced: string;
  status: string;
}

export interface FieldViolation {
  field: string;
  description: string;
}

export class ValidationError extends Error {
  violations: FieldViolation[];

  constructor(violations: FieldViolation[]) {
    super("Validation failed");
    this.name = "ValidationError";
    this.violations = violations;
  }
}

export class ApiError extends Error {
  statusCode: number;
  body: string;

  constructor(statusCode: number, message: string, body: string) {
    super(message);
    this.name = "ApiError";
    this.statusCode = statusCode;
    this.body = body;
  }
}

export interface EconomicServiceClientOptions {
  fetch?: typeof fetch;
  defaultHeaders?: Record<string, string>;
}

export interface EconomicServiceCallOptions {
  headers?: Record<string, string>;
  signal?: AbortSignal;
}

export class EconomicServiceClient {
  private baseURL: string;
  private fetchFn: typeof fetch;
  private defaultHeaders: Record<string, string>;

  constructor(baseURL: string, options?: EconomicServiceClientOptions) {
    this.baseURL = baseURL.replace(/\/+$/, "");
    this.fetchFn = options?.fetch ?? globalThis.fetch;
    this.defaultHeaders = { ...options?.defaultHeaders };
  }

  async getFredSeries(req: GetFredSeriesRequest, options?: EconomicServiceCallOptions): Promise<GetFredSeriesResponse> {
    let path = "/api/economic/v1/get-fred-series";
    const params = new URLSearchParams();
    if (req.seriesId != null && req.seriesId !== "") params.set("series_id", String(req.seriesId));
    if (req.limit != null && req.limit !== 0) params.set("limit", String(req.limit));
    const url = this.baseURL + path + (params.toString() ? "?" + params.toString() : "");

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetFredSeriesResponse;
  }

  async listWorldBankIndicators(req: ListWorldBankIndicatorsRequest, options?: EconomicServiceCallOptions): Promise<ListWorldBankIndicatorsResponse> {
    let path = "/api/economic/v1/list-world-bank-indicators";
    const params = new URLSearchParams();
    if (req.indicatorCode != null && req.indicatorCode !== "") params.set("indicator_code", String(req.indicatorCode));
    if (req.countryCode != null && req.countryCode !== "") params.set("country_code", String(req.countryCode));
    if (req.year != null && req.year !== 0) params.set("year", String(req.year));
    if (req.pageSize != null && req.pageSize !== 0) params.set("page_size", String(req.pageSize));
    if (req.cursor != null && req.cursor !== "") params.set("cursor", String(req.cursor));
    const url = this.baseURL + path + (params.toString() ? "?" + params.toString() : "");

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as ListWorldBankIndicatorsResponse;
  }

  async getEnergyPrices(req: GetEnergyPricesRequest, options?: EconomicServiceCallOptions): Promise<GetEnergyPricesResponse> {
    let path = "/api/economic/v1/get-energy-prices";
    const params = new URLSearchParams();
    if (req.commodities && req.commodities.length > 0) req.commodities.forEach(v => params.append("commodities", v));
    const url = this.baseURL + path + (params.toString() ? "?" + params.toString() : "");

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEnergyPricesResponse;
  }

  async getMacroSignals(_req: GetMacroSignalsRequest, options?: EconomicServiceCallOptions): Promise<GetMacroSignalsResponse> {
    let path = "/api/economic/v1/get-macro-signals";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetMacroSignalsResponse;
  }

  async getEnergyCapacity(req: GetEnergyCapacityRequest, options?: EconomicServiceCallOptions): Promise<GetEnergyCapacityResponse> {
    let path = "/api/economic/v1/get-energy-capacity";
    const params = new URLSearchParams();
    if (req.energySources && req.energySources.length > 0) req.energySources.forEach(v => params.append("energy_sources", v));
    if (req.years != null && req.years !== 0) params.set("years", String(req.years));
    const url = this.baseURL + path + (params.toString() ? "?" + params.toString() : "");

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEnergyCapacityResponse;
  }

  async getBisPolicyRates(_req: GetBisPolicyRatesRequest, options?: EconomicServiceCallOptions): Promise<GetBisPolicyRatesResponse> {
    let path = "/api/economic/v1/get-bis-policy-rates";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetBisPolicyRatesResponse;
  }

  async getBisExchangeRates(_req: GetBisExchangeRatesRequest, options?: EconomicServiceCallOptions): Promise<GetBisExchangeRatesResponse> {
    let path = "/api/economic/v1/get-bis-exchange-rates";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetBisExchangeRatesResponse;
  }

  async getBisCredit(_req: GetBisCreditRequest, options?: EconomicServiceCallOptions): Promise<GetBisCreditResponse> {
    let path = "/api/economic/v1/get-bis-credit";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetBisCreditResponse;
  }

  async getFredSeriesBatch(req: GetFredSeriesBatchRequest, options?: EconomicServiceCallOptions): Promise<GetFredSeriesBatchResponse> {
    let path = "/api/economic/v1/get-fred-series-batch";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "POST",
      headers,
      body: JSON.stringify(req),
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetFredSeriesBatchResponse;
  }

  async listGroceryBasketPrices(_req: ListGroceryBasketPricesRequest, options?: EconomicServiceCallOptions): Promise<ListGroceryBasketPricesResponse> {
    let path = "/api/economic/v1/list-grocery-basket-prices";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as ListGroceryBasketPricesResponse;
  }

  async listBigMacPrices(_req: ListBigMacPricesRequest, options?: EconomicServiceCallOptions): Promise<ListBigMacPricesResponse> {
    let path = "/api/economic/v1/list-bigmac-prices";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as ListBigMacPricesResponse;
  }

  async getNationalDebt(_req: GetNationalDebtRequest, options?: EconomicServiceCallOptions): Promise<GetNationalDebtResponse> {
    let path = "/api/economic/v1/get-national-debt";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetNationalDebtResponse;
  }

  async listFuelPrices(_req: ListFuelPricesRequest, options?: EconomicServiceCallOptions): Promise<ListFuelPricesResponse> {
    let path = "/api/economic/v1/list-fuel-prices";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as ListFuelPricesResponse;
  }

  async getBlsSeries(req: GetBlsSeriesRequest, options?: EconomicServiceCallOptions): Promise<GetBlsSeriesResponse> {
    let path = "/api/economic/v1/get-bls-series";
    const params = new URLSearchParams();
    if (req.seriesId != null && req.seriesId !== "") params.set("series_id", String(req.seriesId));
    if (req.limit != null && req.limit !== 0) params.set("limit", String(req.limit));
    const url = this.baseURL + path + (params.toString() ? "?" + params.toString() : "");

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetBlsSeriesResponse;
  }

  async getEconomicCalendar(req: GetEconomicCalendarRequest, options?: EconomicServiceCallOptions): Promise<GetEconomicCalendarResponse> {
    let path = "/api/economic/v1/get-economic-calendar";
    const params = new URLSearchParams();
    if (req.fromDate != null && req.fromDate !== "") params.set("fromDate", String(req.fromDate));
    if (req.toDate != null && req.toDate !== "") params.set("toDate", String(req.toDate));
    const url = this.baseURL + path + (params.toString() ? "?" + params.toString() : "");

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEconomicCalendarResponse;
  }

  async getCrudeInventories(_req: GetCrudeInventoriesRequest, options?: EconomicServiceCallOptions): Promise<GetCrudeInventoriesResponse> {
    let path = "/api/economic/v1/get-crude-inventories";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetCrudeInventoriesResponse;
  }

  async getNatGasStorage(_req: GetNatGasStorageRequest, options?: EconomicServiceCallOptions): Promise<GetNatGasStorageResponse> {
    let path = "/api/economic/v1/get-nat-gas-storage";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetNatGasStorageResponse;
  }

  async getEcbFxRates(_req: GetEcbFxRatesRequest, options?: EconomicServiceCallOptions): Promise<GetEcbFxRatesResponse> {
    let path = "/api/economic/v1/get-ecb-fx-rates";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEcbFxRatesResponse;
  }

  async getEurostatCountryData(_req: GetEurostatCountryDataRequest, options?: EconomicServiceCallOptions): Promise<GetEurostatCountryDataResponse> {
    let path = "/api/economic/v1/get-eurostat-country-data";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEurostatCountryDataResponse;
  }

  async getEuGasStorage(_req: GetEuGasStorageRequest, options?: EconomicServiceCallOptions): Promise<GetEuGasStorageResponse> {
    let path = "/api/economic/v1/get-eu-gas-storage";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEuGasStorageResponse;
  }

  async getEuYieldCurve(_req: GetEuYieldCurveRequest, options?: EconomicServiceCallOptions): Promise<GetEuYieldCurveResponse> {
    let path = "/api/economic/v1/get-eu-yield-curve";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEuYieldCurveResponse;
  }

  async getEuFsi(_req: GetEuFsiRequest, options?: EconomicServiceCallOptions): Promise<GetEuFsiResponse> {
    let path = "/api/economic/v1/get-eu-fsi";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEuFsiResponse;
  }

  async getEconomicStress(_req: GetEconomicStressRequest, options?: EconomicServiceCallOptions): Promise<GetEconomicStressResponse> {
    let path = "/api/economic/v1/get-economic-stress";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEconomicStressResponse;
  }

  async getFaoFoodPriceIndex(_req: GetFaoFoodPriceIndexRequest, options?: EconomicServiceCallOptions): Promise<GetFaoFoodPriceIndexResponse> {
    let path = "/api/economic/v1/get-fao-food-price-index";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetFaoFoodPriceIndexResponse;
  }

  async getOilStocksAnalysis(_req: GetOilStocksAnalysisRequest, options?: EconomicServiceCallOptions): Promise<GetOilStocksAnalysisResponse> {
    let path = "/api/economic/v1/get-oil-stocks-analysis";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetOilStocksAnalysisResponse;
  }

  async getOilInventories(_req: GetOilInventoriesRequest, options?: EconomicServiceCallOptions): Promise<GetOilInventoriesResponse> {
    let path = "/api/economic/v1/get-oil-inventories";
    const url = this.baseURL + path;

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetOilInventoriesResponse;
  }

  async getEnergyCrisisPolicies(req: GetEnergyCrisisPoliciesRequest, options?: EconomicServiceCallOptions): Promise<GetEnergyCrisisPoliciesResponse> {
    let path = "/api/economic/v1/get-energy-crisis-policies";
    const params = new URLSearchParams();
    if (req.countryCode != null && req.countryCode !== "") params.set("country_code", String(req.countryCode));
    if (req.category != null && req.category !== "") params.set("category", String(req.category));
    const url = this.baseURL + path + (params.toString() ? "?" + params.toString() : "");

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...this.defaultHeaders,
      ...options?.headers,
    };

    const resp = await this.fetchFn(url, {
      method: "GET",
      headers,
      signal: options?.signal,
    });

    if (!resp.ok) {
      return this.handleError(resp);
    }

    return await resp.json() as GetEnergyCrisisPoliciesResponse;
  }

  private async handleError(resp: Response): Promise<never> {
    const body = await resp.text();
    if (resp.status === 400) {
      try {
        const parsed = JSON.parse(body);
        if (parsed.violations) {
          throw new ValidationError(parsed.violations);
        }
      } catch (e) {
        if (e instanceof ValidationError) throw e;
      }
    }
    throw new ApiError(resp.status, `Request failed with status ${resp.status}`, body);
  }
}

