import * as plugins from './smartguard.plugins.js';
import { Guard, type TGuardFunction } from './classes.guard.js';

export interface IExecOptions {
  mode?: 'parallel' | 'serial';
  stopOnFail?: boolean;
} 

/**
 * Extended GuardSet that inherits from Guard
 * and provides additional functionalities.
 */
export class GuardSet<T> extends Guard<T> {
  public guards: Array<Guard<T>>;

  constructor(guardArray: Array<Guard<T>> = []) {
    super(async (dataArg: T) => {
      return this.allGuardsPass(dataArg);
    })
    this.guards = guardArray;
  }

  /**
   * executes all guards in all guardSets against a data argument
   * @param dataArg
   */
  public async execAllWithData(dataArg: T, optionsArg: IExecOptions = {
    mode: 'parallel',
    stopOnFail: false
  }): Promise<boolean[]> {
    const resultPromises: Array<Promise<boolean>> = [];
    
    for (const guard of this.guards) {
      const guardResultPromise = guard.exec(dataArg);
      if (optionsArg.mode === 'serial') {
        await guardResultPromise;
      }
      resultPromises.push(guardResultPromise);
      if (optionsArg.stopOnFail) {
        if (!await guardResultPromise) {
          return await Promise.all(resultPromises);
        }
      }
    }

    const results = await Promise.all(resultPromises);
    return results;
  }

  /**
   * checks if all guards pass
   * @param dataArg
   */
  public async allGuardsPass(dataArg: T, optionsArg: IExecOptions = {
    mode: 'parallel',
    stopOnFail: false
  }): Promise<boolean> {
    const results = await this.execAllWithData(dataArg, optionsArg);
    return results.every(result => result);
  }

  /**
   * checks if any guard passes
   * @param dataArg
   */
  public async anyGuardsPass(dataArg: T): Promise<boolean> {
    const results = await this.execAllWithData(dataArg, {
      mode: 'parallel',
      stopOnFail: false
    });
    return results.some(result => result);
  }

  /**
   * returns the first reason for why something fails
   * @param dataArg
   * @returns 
   */
  public getFailedHint (dataArg: T): Promise<string> {
    for (const guard of this.guards) {
      const failedHint = guard.getFailedHint(dataArg);
      if (failedHint) {
        return failedHint;
      }
    }
  }
}
