import { Mintblue } from '../mintblue.js';
import { experimentalStreamedTransaction } from '../types.js';
import { Reader, Action, ActionDispatcher } from './types.js';
import { LogClass } from './utils.js';

/**
 * Implementation of the Reader interface for the Mintblue SDK.
 * This class facilitates listening to transaction events using Mintblue and dispatching them accordingly.
 *
 * @category Machine
 */
@LogClass()
export class MintblueReader implements Reader {
  private actionDispatcher!: ActionDispatcher;
  private mintblue: Mintblue;
  private project_id: string;

  /**
   * Factory method to create an instance of the MintblueReader.
   * @param sdkToken - The token for the Mintblue SDK.
   * @param project_id - The ID of the project to listen to.
   * @returns An instance of MintblueReader.
   */
  static async create(sdkToken: string, project_id: string) {
    const mintblue = await Mintblue.create({ token: sdkToken });
    return new MintblueReader(mintblue, project_id);
  }

  /**
   * Constructs a MintblueReader.
   * @param mintblue - The Mintblue instance.
   * @param project_id - The ID of the project to listen to.
   */
  constructor(mintblue: Mintblue, project_id: string) {
    this.project_id = project_id;
    this.mintblue = mintblue;
  }

  /**
   * Initializes the action dispatcher.
   * @param actionDispatcher - The function to dispatch actions.
   */
  initialize(actionDispatcher: ActionDispatcher): void {
    this.actionDispatcher = actionDispatcher;
  }

  /**
   * Starts the reader to begin listening for Mintblue project events.
   */
  start(lastActionId?: string): void {
    if (!this.mintblue) {
      throw new Error('mintBlue SDK not initialized yet');
    }

    this.mintblue.experimentalStreamProject({
      project_id: this.project_id,
      history: true,
      ...(lastActionId ? { last_event_id: lastActionId } : {}),
      onError(e) {
        console.log('Mintblue driver error: ', e);
      },
      onTransaction: async (tx) => {
        const action = await this.parseTransactionIntoAction(tx);
        if (action) {
          action.id = tx.txid;
          await this.actionDispatcher(action, { project_id: this.project_id, tx }, false); // TODO isResynchronizing
          // const _lastProcessedActionId = action.id;
        }
      },
    });
  }

  /**
   * Parses a Mintblue transaction into a standardized action.
   * @param tx - The Mintblue transaction.
   * @returns An action or null if parsing is unsuccessful.
   */
  private async parseTransactionIntoAction(tx: experimentalStreamedTransaction): Promise<Action | null> {
    if (!this.mintblue) {
      throw new Error('mintBlue SDK not initialized yet');
    }

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const [output, _] = await this.mintblue.parseTransaction(tx.txid, tx.rawtx);

    if (output.type === 'data' && typeof output.value === 'object') {
      if ((output.value as any).f && (output.value as any).args) {
        const action: Action = {
          id: tx.txid,
          ...(output.value as any),
        };
        return action;
      }
    } else if (output.type === 'data' && typeof output.value === 'string') {
      const action = {
        id: tx.txid,
        ...JSON.parse(output.value),
      };
      return action;
    }

    return null;
  }

  /**
   * Stops the reader from listening to events. (Implementation may be added in the future)
   */
  stop(): void {
    //
  }
}
