import * as CommonFunctions from '../../common-functions/common-functions';
import * as Models from '../../models/models';
import * as lodash from 'lodash';
import { StepFormatter } from './StepFormatter';
import { messages } from 'cucumber-messages';

export class ScenarioFormatter {
  private gherkinFeature: messages.GherkinDocument.IFeature | null | undefined ;

  public constructor ( gherkinFeature: messages.GherkinDocument.IFeature | null | undefined ) {
    this.gherkinFeature = gherkinFeature;
  }

  /**
   * Parse steps of scenario
   *
   * @param {object} scenario: scenario object
   * @private
   */
  public parseScenario ( scenario: Models.Scenario, featureName: string ): Models.Scenario {
    scenario = this.initializeScenario( scenario );
    if( typeof scenario.id === 'undefined' ){
      console.log( `The scenario ${scenario.name} of the feature: ${featureName} doesn't have id.` );
      return {} as Models.Scenario;
    }

    if( this.isOutlineScenario( scenario ) ){
      scenario.keyword = 'Scenario Outline';
    }

    scenario.results = Models.scenarioResultsInitializer();
    scenario.before = Models.beforeOrAfterInitializer() ;
    scenario.after = Models.beforeOrAfterInitializer() ;

    scenario?.steps?.map( ( step: Models.Step ) =>{
      const parsedStep = new StepFormatter().parseStep( step );
      switch( parsedStep.keyword.trim() ){
      case 'Before':
        scenario.before.steps.push( parsedStep );
        scenario.before.results = this.updateBeforeOrAfterStadistics( scenario.before.results, parsedStep.result );
        break;
      case 'After':
        scenario.after.steps.push( parsedStep );
        scenario.after.results = this.updateBeforeOrAfterStadistics( scenario.after.results, parsedStep.result );
        break;
      }
      scenario.results = this.updateScenarioStadistics( scenario.results, parsedStep.result );
      return parsedStep;
    } );

    scenario.steps = scenario?.steps == null ? null : scenario.steps.filter( step => step.keyword.trim() !== 'Before' && step.keyword.trim() !== 'After' );
    // scenario.before = before;
    // scenario.after = after;
    scenario.results.overview.durationHHMMSS = CommonFunctions.dateTimeFunctions.convertTimeFromNanoSecondsToHHMMSS( scenario.results.overview.duration );
    if( scenario.before.steps.length === 0 ){
      // delete scenario.before;
    }else{
      scenario.results.before.durationHHMMSS = CommonFunctions.dateTimeFunctions.convertTimeFromNanoSecondsToHHMMSS( scenario.results.before.duration );
    }

    if( scenario.after.steps.length === 0 ){
      // delete scenario.after;
    }else{
      scenario.results.after.durationHHMMSS = CommonFunctions.dateTimeFunctions.convertTimeFromNanoSecondsToHHMMSS( scenario.results.after.duration );
    }

    return scenario;
  }

  /**
   * Create first scenario for scenarios outline
   */
  public createNewFirstScenarioOutline (
    jsonScenario: Models.Scenario,
    gherkinScenario: messages.GherkinDocument.Feature.IScenario | null | undefined,
    jsonFeature: Models.Feature, scenarioIndex: number ): Models.Scenario {
    // if ( typeof providedScenario === 'undefined' || typeof providedScenario?.examples?.[0].tableHeader?.cells === 'undefined' ){
    //   return null;
    // }

    const firstScenarioOutline: Models.Scenario = lodash.cloneDeep( jsonScenario );
    firstScenarioOutline.id = firstScenarioOutline.id.substring( 0, firstScenarioOutline.id.length - 3 ) + ';;1';
    firstScenarioOutline.name = CommonFunctions.parsingFunctions.escapeHTML( firstScenarioOutline.name );
    firstScenarioOutline.before = {} as Models.BeforeOrAfter;
    firstScenarioOutline.after = {} as Models.BeforeOrAfter;
    ( firstScenarioOutline.steps as Models.Step[] ).forEach( ( stepJs: Models.Step ) => {
      delete stepJs.text;
      delete stepJs.rows;
      delete stepJs.result.error_message;
      stepJs.embeddings = [];
      stepJs.attachments = [];
      stepJs.result.status = Models.Status.passed;

      ( gherkinScenario?.steps as messages.GherkinDocument.Feature.IStep[] ).map( stepPd => {
        if ( ( stepPd.location as messages.ILocation ).line === stepJs.line ) {
          stepJs.name = CommonFunctions.parsingFunctions.escapeHTML( stepPd.text as string );
        }
        return stepPd;
      } );
    } );

    const examples = gherkinScenario?.examples?.[0] ;
    const examplesHeaderCells =
    ( ( examples?.tableHeader as messages.GherkinDocument.Feature.ITableRow ).cells as messages.GherkinDocument.Feature.TableRow.ITableCell[] ).map( cell => cell.value );
    const examplesRows = ( examples?.tableBody as messages.GherkinDocument.Feature.ITableRow[] ).map( row => {
      const rowContent: string[] = [];
      ( row.cells as messages.GherkinDocument.Feature.TableRow.ITableCell[] ).forEach( cell => {
        rowContent.push( cell.value as string );
      } );
      return rowContent;
    } );
    let rows = [];
    rows.push( examplesHeaderCells );
    rows = rows.concat( examplesRows );
    firstScenarioOutline.examples = rows as string[][];

    firstScenarioOutline.examples.forEach( ( example, index, arr ) => {
      if ( index < arr.length - 1 ) {

        firstScenarioOutline.results.overview.duration += ( jsonFeature.elements as Models.Scenario[] )[scenarioIndex + index].results.overview.duration;
        firstScenarioOutline.results.steps.passed += ( jsonFeature.elements as Models.Scenario[] )[scenarioIndex + index].results.steps.passed;
        firstScenarioOutline.results.steps.failed += ( jsonFeature.elements as Models.Scenario[] )[scenarioIndex + index].results.steps.failed;
        firstScenarioOutline.results.steps.undefined += ( jsonFeature.elements as Models.Scenario[] )[scenarioIndex + index].results.steps.undefined;
        firstScenarioOutline.results.steps.pending += ( jsonFeature.elements as Models.Scenario[] )[scenarioIndex + index].results.steps.pending;
        firstScenarioOutline.results.steps.ambiguous += ( jsonFeature.elements as Models.Scenario[] )[scenarioIndex + index].results.steps.ambiguous;
        firstScenarioOutline.results.steps.skipped += ( jsonFeature.elements as Models.Scenario[] )[scenarioIndex + index].results.steps.skipped;
      }
    } );

    firstScenarioOutline.results.overview.durationHHMMSS = CommonFunctions.dateTimeFunctions.convertTimeFromNanoSecondsToHHMMSS(
      firstScenarioOutline.results.overview.duration
    );

    firstScenarioOutline.isFirstScenarioOutline = true;
    return firstScenarioOutline;
  }

  public isOutlineScenario( scenario: Models.Scenario ): boolean{
    if( this.gherkinFeature?.children?.some( scenarioFromFeature =>
      scenarioFromFeature.scenario?.name === scenario.name && scenarioFromFeature?.scenario?.examples && scenarioFromFeature?.scenario?.examples.length > 0 ) ){
      return true;
    }
    return false;
  }

  private initializeScenario( scenario: Models.Scenario ): Models.Scenario{
    scenario.isFirstScenarioOutline = false;
    scenario.examples = [];
    scenario.keyword = scenario.keyword == null || typeof scenario.keyword === 'undefined' ? '' : scenario.keyword;
    scenario.name = scenario.name == null || typeof scenario.name === 'undefined' ? '' : scenario.name;
    scenario.type = scenario.type == null || typeof scenario.type === 'undefined' ? '' : scenario.type;
    scenario.results = Models.scenarioResultsInitializer();
    scenario.before = Models.beforeOrAfterInitializer() ;
    scenario.after = Models.beforeOrAfterInitializer() ;
    scenario.tags = scenario.tags == null || typeof scenario.tags === 'undefined' ? [] : scenario.tags;
    return scenario;
  }

  private updateBeforeOrAfterStadistics( results: Models.BeforeOrAfterResults, stepResults: Models.StepResultsOverview ): Models.BeforeOrAfterResults{
    results.overview.duration += stepResults.duration;
    results.overview.status = stepResults.status !== Models.Status.passed
      ? stepResults.status
      : results.overview.status;
    return results;
  }

  private updateScenarioStadistics( results: Models.ScenarioResults ,stepResults: Models.StepResultsOverview ): Models.ScenarioResults{
    results.overview.duration += stepResults?.duration > 0 ? stepResults.duration : 0 ;
    results.overview.status = stepResults.status !== Models.Status.failed
      ? Models.Status.failed
      : results.overview.status;

    switch( stepResults.status ){
    case Models.Status.passed:
      results.steps.passed += 1;
      break;
    case Models.Status.failed:
      results.steps.failed += 1;
      break;
    case Models.Status.ambiguous:
      results.steps.ambiguous += 1;
      break;
    case Models.Status.pending:
      results.steps.pending += 1;
      break;
    case Models.Status.skipped:
      results.steps.skipped += 1;
      break;
    case Models.Status.undefined:
      results.steps.undefined += 1;
      break;
    }
    results.steps.total += 1;
    return results;
  }
}
