/*
 This file is part of GNU Taler
 (C) 2019-2023 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/>
 */

/**
 * Type and schema definitions for notifications from the wallet to clients
 * of the wallet.
 */

/**
 * Imports.
 */
import { AbsoluteTime } from "./time.js";
import { TransactionState } from "./types-taler-wallet-transactions.js";
import {
  ContactEntry,
  ExchangeEntryState,
  MailboxMessageRecord,
  TalerErrorDetail,
  TransactionIdStr,
} from "./types-taler-wallet.js";

export enum NotificationType {
  BalanceChange = "balance-change",
  BankAccountChange = "bank-account-change",
  BackupOperationError = "backup-error",
  ContactAdded = "contact-added",
  ContactDeleted = "contact-deleted",
  MailboxMessageAdded = "mailbox-message-added",
  MailboxMessageDeleted = "mailbox-message-deleted",
  TransactionStateTransition = "transaction-state-transition",
  ExchangeStateTransition = "exchange-state-transition",
  Idle = "idle",
  TaskObservabilityEvent = "task-observability-event",
  RequestObservabilityEvent = "request-observability-event",
}

export interface ErrorInfoSummary {
  code: number;
  hint?: string;
  message?: string;
}

export interface TransactionStateTransitionNotification {
  type: NotificationType.TransactionStateTransition;

  /**
   * Identifier of the affected transaction.
   */
  transactionId: string;

  /**
   * State before the transition.
   */
  oldTxState: TransactionState;

  /**
   * State after the transition.
   */
  newTxState: TransactionState;

  /**
   * Internal ID of the new state.
   * Must not be used by the UI, only used for testing.
   */
  newStId: number;

  /**
   * Short summary of the error for an error transition.
   */
  errorInfo?: ErrorInfoSummary;

  /**
   * Additional "user data" that is dependent on the
   * state transition.
   *
   * Usage should be avoided.
   *
   * Currently used to notify the iOS app about
   * the KYC URL.
   */
  experimentalUserData?: any;
}

export interface ExchangeStateTransitionNotification {
  type: NotificationType.ExchangeStateTransition;
  /**
   * Identification of the exchange entry that this
   * notification is about.
   */
  exchangeBaseUrl: string;

  /**
   * If missing, the notification means that
   * the exchange entry is newly created.
   */
  oldExchangeState?: ExchangeEntryState;

  /**
   * New state of the exchange.
   *
   * If missing, exchange got deleted.
   */
  newExchangeState?: ExchangeEntryState;

  /**
   * Summary of the error that occurred when trying to update the exchange entry,
   * if applicable.
   */
  errorInfo?: ErrorInfoSummary;
}

/**
 * Notification emitted when a contact is added
 */
export interface ContactAddedNotification {
  type: NotificationType.ContactAdded;

  /**
   * The contact that was added
   */
  contact: ContactEntry;
}

/**
 * Notification emitted when a contact is deleted
 */
export interface ContactDeletedNotification {
  type: NotificationType.ContactDeleted;

  /**
   * The contact that was deleted
   */
  contact: ContactEntry;
}

/**
 * Notification emitted when a mailbox message is added
 */
export interface MailboxMessageAddedNotification {
  type: NotificationType.MailboxMessageAdded;

  /**
   * The message that was added
   */
  message: MailboxMessageRecord;
}

/**
 * Notification emitted when a mailbox message is deleted
 */
export interface MailboxMessageDeletedNotification {
  type: NotificationType.MailboxMessageDeleted;

  /**
   * The message that was deleted
   */
  message: MailboxMessageRecord;
}

/**
 * Transaction emitted when a bank account changes.
 */
export interface BankAccountChangeNotification {
  type: NotificationType.BankAccountChange;

  /**
   * ID of the affected bank account.
   */
  bankAccountId: string;
}

export interface BalanceChangeNotification {
  type: NotificationType.BalanceChange;

  /**
   * If set to true, the balance change is internal
   * to the wallet and not visible to the user.
   *
   * (For example when the material balance changes via a refresh, but
   * the available balance stays the same.)
   */
  isInternal?: boolean;

  /**
   * Transaction ID of the transaction that caused the balance update.
   *
   * Only used as a hint for debugging, should not be relied upon by clients.
   */
  hintTransactionId: string;
}

export interface TaskProgressNotification {
  type: NotificationType.TaskObservabilityEvent;
  taskId: string;
  event: ObservabilityEvent;
}

export interface RequestProgressNotification {
  type: NotificationType.RequestObservabilityEvent;
  requestId: string;
  operation: string;
  event: ObservabilityEvent;
}

export enum ObservabilityEventType {
  HttpFetchStart = "http-fetch-start",
  HttpFetchFinishError = "http-fetch-finish-error",
  HttpFetchFinishSuccess = "http-fetch-finish-success",
  DbQueryStart = "db-query-start",
  DbQueryFinishSuccess = "db-query-finish-success",
  DbQueryFinishError = "db-query-finish-error",
  RequestStart = "request-start",
  RequestFinishSuccess = "request-finish-success",
  RequestFinishError = "request-finish-error",
  TaskStart = "task-start",
  TaskStop = "task-stop",
  TaskReset = "task-reset",
  ShepherdTaskResult = "shepherd-task-result",
  DeclareTaskDependency = "declare-task-dependency",
  CryptoStart = "crypto-start",
  CryptoFinishSuccess = "crypto-finish-success",
  CryptoFinishError = "crypto-finish-error",
  Message = "message",
  /**
   * Declare that an observability event is relevant to a particular transaction.
   * If emitted from a request/task, all past/future events for that request/task
   * should be shown for the transaction as well.
   */
  DeclareConcernsTransaction = "declare-concerns-transaction",
}

export type ObservabilityEvent =
  | {
      id: string;
      when: AbsoluteTime;
      type: ObservabilityEventType.HttpFetchStart;
      url: string;
    }
  | {
      id: string;
      when: AbsoluteTime;
      type: ObservabilityEventType.HttpFetchFinishSuccess;
      url: string;
      status: number;
    }
  | {
      id: string;
      when: AbsoluteTime;
      type: ObservabilityEventType.HttpFetchFinishError;
      url: string;
      error: TalerErrorDetail;
    }
  | {
      type: ObservabilityEventType.DbQueryStart;
      name: string;
      location: string;
    }
  | {
      type: ObservabilityEventType.DbQueryFinishSuccess;
      name: string;
      location: string;
    }
  | {
      type: ObservabilityEventType.DbQueryFinishError;
      name: string;
      location: string;
      error: TalerErrorDetail;
    }
  | {
      type: ObservabilityEventType.RequestStart;
    }
  | {
      type: ObservabilityEventType.RequestFinishSuccess;
      durationMs: number;
    }
  | {
      type: ObservabilityEventType.RequestFinishError;
    }
  | {
      type: ObservabilityEventType.TaskStart;
      taskId: string;
    }
  | {
      type: ObservabilityEventType.TaskStop;
      taskId: string;
    }
  | {
      type: ObservabilityEventType.TaskReset;
      taskId: string;
    }
  | {
      type: ObservabilityEventType.DeclareTaskDependency;
      taskId: string;
    }
  | {
      type: ObservabilityEventType.CryptoStart;
      operation: string;
    }
  | {
      type: ObservabilityEventType.CryptoFinishSuccess;
      operation: string;
    }
  | {
      type: ObservabilityEventType.CryptoFinishError;
      operation: string;
    }
  | {
      type: ObservabilityEventType.ShepherdTaskResult;
      resultType: string;
    }
  | {
      type: ObservabilityEventType.Message;
      contents: string;
    }
  | {
      type: ObservabilityEventType.DeclareConcernsTransaction;
      transactionId: TransactionIdStr;
    };

export interface BackupOperationErrorNotification {
  type: NotificationType.BackupOperationError;
  error: TalerErrorDetail;
}

export interface IdleNotification {
  type: NotificationType.Idle;
}

export type WalletNotification =
  | BalanceChangeNotification
  | BankAccountChangeNotification
  | BackupOperationErrorNotification
  | ContactAddedNotification
  | ContactDeletedNotification
  | MailboxMessageAddedNotification
  | MailboxMessageDeletedNotification
  | ExchangeStateTransitionNotification
  | TransactionStateTransitionNotification
  | TaskProgressNotification
  | RequestProgressNotification
  | IdleNotification;
