import { Currency, ExchangeRate } from "../data/entities.js";
const exchangeRateKey = (fromCurrencyCode, toCurrencyCode, date, source) => `${fromCurrencyCode}|${toCurrencyCode}|${date.getTime()}|${source}`;
class RateFetchingService {
  constructor(em) {
    this.em = em;
    this.providers = /* @__PURE__ */ new Map();
  }
  /**
   * Register a rate provider
   */
  registerProvider(provider) {
    this.providers.set(provider.source, provider);
  }
  async fetchRatesForDate(date, scope, options = {}) {
    const result = {
      totalFetched: 0,
      byProvider: {},
      errors: []
    };
    const existingCurrencies = await this.getExistingCurrencies(scope);
    const currencyCodeSet = new Set(existingCurrencies.map((c) => c.code));
    const providerList = options.providers?.length ? options.providers : Array.from(this.providers.keys());
    const outcomes = await Promise.all(
      providerList.map(
        (providerSource) => this.fetchFromProvider(providerSource, date, scope, currencyCodeSet)
      )
    );
    for (const outcome of outcomes) {
      if (outcome.kind === "skipped") {
        result.errors.push(outcome.error);
        continue;
      }
      if (outcome.kind === "failed") {
        result.errors.push(`${outcome.providerSource}: ${outcome.error}`);
        result.byProvider[outcome.providerSource] = { count: 0, errors: [outcome.error] };
        continue;
      }
      try {
        const stored = await this.storeRates(outcome.rates, scope);
        result.byProvider[outcome.providerSource] = { count: stored };
        result.totalFetched += stored;
      } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        result.errors.push(`${outcome.providerSource}: ${message}`);
        result.byProvider[outcome.providerSource] = { count: 0, errors: [message] };
      }
    }
    return result;
  }
  async fetchFromProvider(providerSource, date, scope, currencyCodeSet) {
    const provider = this.providers.get(providerSource);
    if (!provider) {
      return { kind: "skipped", providerSource, error: `Unknown provider: ${providerSource}` };
    }
    if (!provider.isAvailable()) {
      return { kind: "skipped", providerSource, error: `Provider not available: ${providerSource}` };
    }
    try {
      const rates = await provider.fetchRates(date, scope, currencyCodeSet);
      const validRates = rates.filter(
        (r) => currencyCodeSet.has(r.fromCurrencyCode) && currencyCodeSet.has(r.toCurrencyCode)
      );
      return { kind: "fetched", providerSource, rates: validRates };
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      return { kind: "failed", providerSource, error: message };
    }
  }
  async getExistingCurrencies(scope) {
    return this.em.find(Currency, {
      tenantId: scope.tenantId,
      organizationId: scope.organizationId,
      isActive: true,
      deletedAt: null
    });
  }
  async storeRates(rates, scope) {
    if (rates.length === 0) return 0;
    let stored = 0;
    await this.em.transactional(async (em) => {
      const fromCurrencyCodes = Array.from(new Set(rates.map((rate) => rate.fromCurrencyCode)));
      const toCurrencyCodes = Array.from(new Set(rates.map((rate) => rate.toCurrencyCode)));
      const sources = Array.from(new Set(rates.map((rate) => rate.source)));
      const dates = Array.from(new Set(rates.map((rate) => rate.date.getTime()))).map(
        (time) => new Date(time)
      );
      const existingRates = await em.find(ExchangeRate, {
        organizationId: scope.organizationId,
        tenantId: scope.tenantId,
        fromCurrencyCode: { $in: fromCurrencyCodes },
        toCurrencyCode: { $in: toCurrencyCodes },
        date: { $in: dates },
        source: { $in: sources }
      });
      const existingByKey = /* @__PURE__ */ new Map();
      for (const existing of existingRates) {
        existingByKey.set(
          exchangeRateKey(existing.fromCurrencyCode, existing.toCurrencyCode, existing.date, existing.source),
          existing
        );
      }
      const now = /* @__PURE__ */ new Date();
      for (const rate of rates) {
        const key = exchangeRateKey(rate.fromCurrencyCode, rate.toCurrencyCode, rate.date, rate.source);
        const existing = existingByKey.get(key);
        if (existing) {
          existing.rate = rate.rate;
          existing.type = rate.type ?? null;
          existing.updatedAt = now;
          em.persist(existing);
        } else {
          const newRate = em.create(ExchangeRate, {
            organizationId: scope.organizationId,
            tenantId: scope.tenantId,
            fromCurrencyCode: rate.fromCurrencyCode,
            toCurrencyCode: rate.toCurrencyCode,
            rate: rate.rate,
            date: rate.date,
            source: rate.source,
            type: rate.type ?? null,
            isActive: true,
            createdAt: now,
            updatedAt: now
          });
          em.persist(newRate);
          existingByKey.set(key, newRate);
        }
        stored++;
      }
      await em.flush();
    });
    return stored;
  }
}
export {
  RateFetchingService
};
//# sourceMappingURL=rateFetchingService.js.map
