package com.adevinta.android.barista.rule.flaky;

import com.adevinta.android.barista.rule.flaky.internal.AllowFlakyStatement;
import com.adevinta.android.barista.rule.flaky.internal.FlakyStatementBuilder;
import com.adevinta.android.barista.rule.flaky.internal.RepeatStatement;
import java.lang.annotation.Annotation;
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;

public class FlakyStatementBuilderTest {

  private static final String CLASS = "class name";
  private static final String TEST = "test name";

  @Test(expected = IllegalStateException.class)
  public void allowFlakyAndRepeatAreNotAllowedTogether() throws Exception {
    Statement baseStatement = new SomeStatement();
    Description description = withAnnotations(allowFlaky(), repeat());

    createStatement(baseStatement, description);
  }

  @Test
  public void sameStatementReturnedWhenNoAnnotationsFound() throws Exception {
    Statement baseStatement = new SomeStatement();
    Description description = Description.EMPTY;

    Statement resultStatement = createStatement(baseStatement, description);

    assertEquals(baseStatement, resultStatement);
  }

  @Test
  public void repeatStatementReturnedWhenRepeatAnnotationFound() throws Exception {
    Statement baseStatement = new SomeStatement();
    Description description = withAnnotations(repeat());

    Statement resultStatement = createStatement(baseStatement, description);

    assertTrue(resultStatement instanceof RepeatStatement);
  }

  @Test
  public void repeatStatementReturnedWhenSettingDefaultRepeatAttempts() throws Exception {
    Statement baseStatement = new SomeStatement();
    Description description = Description.EMPTY;

    Statement resultStatement = createStatementWithRepeatAttemptsByDefault(baseStatement, description);

    assertTrue(resultStatement instanceof RepeatStatement);
  }

  @Test
  public void allowFlakyStatementReturnedWhenAllowFlakyAnnotationFound() throws Exception {
    Statement baseStatement = new SomeStatement();
    Description description = withAnnotations(allowFlaky());

    Statement resultStatement = createStatement(baseStatement, description);

    assertTrue(resultStatement instanceof AllowFlakyStatement);
  }

  @Test
  public void allowFlakyStatementReturnedWhenNoAnnotationsFoundButUsesDefault() throws Exception {
    Statement baseStatement = new SomeStatement();
    Description description = Description.EMPTY;

    Statement resultStatement = createStatementWithAllowFlakyByDefault(baseStatement, description);

    assertTrue(resultStatement instanceof AllowFlakyStatement);
  }

  @Test
  public void lastStatementReturnedWhenDefaultRepeatAttemptsAndAllowFlakyStatementUsedAtTheSameTime() throws Exception {
    Statement baseStatement = new SomeStatement();
    Description description = Description.EMPTY;

    Statement resultStatement = createStatementWithFlakyAndReturn(baseStatement, description);

    assertTrue(resultStatement instanceof RepeatStatement);
  }

  private Statement createStatementWithFlakyAndReturn(Statement baseStatement, Description description) {
    return new FlakyStatementBuilder()
        .setBase(baseStatement)
        .setDescription(description)
        .allowFlakyAttemptsByDefault(5)
        .setRepeatAttemptsByDefault(3)
        .build();
  }

  private Statement createStatementWithAllowFlakyByDefault(Statement baseStatement, Description description) {
    return new FlakyStatementBuilder()
        .setBase(baseStatement)
        .setDescription(description)
        .allowFlakyAttemptsByDefault(5)
        .build();
  }

  //region Shortcut methods
  private Statement createStatement(Statement baseStatement, Description description) {
    return new FlakyStatementBuilder()
        .setBase(baseStatement)
        .setDescription(description)
        .build();
  }

  private Statement createStatementWithRepeatAttemptsByDefault(Statement baseStatement, Description description) {
    return new FlakyStatementBuilder()
        .setBase(baseStatement)
        .setDescription(description)
        .setRepeatAttemptsByDefault(3)
        .build();
  }

  private Description withAnnotations(Annotation... annotations) {
    return Description.createTestDescription(CLASS, TEST, annotations);
  }

  private Repeat repeat() {
    return new Repeat() {
      @Override
      public Class<? extends Annotation> annotationType() {
        return Repeat.class;
      }

      @Override
      public int times() {
        return 0;
      }
    };
  }

  private AllowFlaky allowFlaky() {
    return new AllowFlaky() {
      @Override
      public Class<? extends Annotation> annotationType() {
        return AllowFlaky.class;
      }

      @Override
      public int attempts() {
        return 0;
      }
    };
  }

  private static class SomeStatement extends Statement {
    @Override
    public void evaluate() throws Throwable {
      // NA
    }
  }
  //endregion
}
