/*
 This file is part of GNU Taler
 (C) 2026 Taler Systems S.A.

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.

 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */

import {
  CurrencySpecification,
  ScopeInfo,
  stringifyScopeInfo,
} from "@gnu-taler/taler-util";
import { ConfigRecord, WalletIndexedDbTransaction } from "./db.js";

export interface GetCurrencyInfoDbResult {
  /**
   * Currency specification.
   */
  currencySpec: CurrencySpecification;

  /**
   * How did the currency info get set?
   */
  source: "exchange" | "user" | "preset";
}

export interface StoreCurrencyInfoDbRequest {
  scopeInfo: ScopeInfo;
  currencySpec: CurrencySpecification;
  source: "exchange" | "user" | "preset";
}

export interface WalletDbTransaction {
  getCurrencyInfo(
    scopeInfo: ScopeInfo,
  ): Promise<GetCurrencyInfoDbResult | undefined>;

  getConfig<T extends ConfigRecord["key"]>(
    key: T,
  ): Promise<Extract<ConfigRecord, { key: T }> | undefined>;

  /**
   * Store currency info for a scope.
   *
   * Overrides existing currency infos.
   */
  upsertCurrencyInfo(req: StoreCurrencyInfoDbRequest): Promise<void>;

  insertCurrencyInfoUnlessExists(
    req: StoreCurrencyInfoDbRequest,
  ): Promise<void>;
}

export class IdbWalletTransaction implements WalletDbTransaction {
  tx: WalletIndexedDbTransaction;
  constructor(tx: WalletIndexedDbTransaction) {
    this.tx = tx;
  }
  async getCurrencyInfo(
    scopeInfo: ScopeInfo,
  ): Promise<GetCurrencyInfoDbResult | undefined> {
    const tx = this.tx;
    const s = stringifyScopeInfo(scopeInfo);
    const res = await tx.currencyInfo.get(s);
    if (!res) {
      return undefined;
    }
    return {
      currencySpec: res.currencySpec,
      source: res.source,
    };
  }

  async getConfig<T extends ConfigRecord["key"]>(
    key: T,
  ): Promise<Extract<ConfigRecord, { key: T }> | undefined> {
    const tx = this.tx;
    return (await tx.config.get(key)) as any;
  }

  async upsertCurrencyInfo(req: StoreCurrencyInfoDbRequest): Promise<void> {
    const tx = this.tx;
    await tx.currencyInfo.put({
      scopeInfoStr: stringifyScopeInfo(req.scopeInfo),
      currencySpec: req.currencySpec,
      source: req.source,
    });
  }

  async insertCurrencyInfoUnlessExists(
    req: StoreCurrencyInfoDbRequest,
  ): Promise<void> {
    const tx = this.tx;
    const scopeInfoStr = stringifyScopeInfo(req.scopeInfo);
    const oldRec = await tx.currencyInfo.get(scopeInfoStr);
    if (oldRec) {
      return;
    }
    await tx.currencyInfo.put({
      scopeInfoStr: stringifyScopeInfo(req.scopeInfo),
      currencySpec: req.currencySpec,
      source: req.source,
    });
  }
}
