package io.delphi.qa.auto.awsmfrw.managers.helpers.aws;

import com.amazonaws.services.lambda.AWSLambdaAsync;
import com.amazonaws.services.lambda.AWSLambdaAsyncClientBuilder;
import com.amazonaws.services.lambda.model.InvokeRequest;
import com.amazonaws.services.lambda.model.InvokeResult;
import com.amazonaws.services.lambda.model.TooManyRequestsException;
import io.delphi.qa.auto.awsmfrw.common_baby.IsaCommonMethods;
import io.delphi.qa.auto.awsmfrw.common_baby.IsaLogging;
import io.delphi.qa.auto.awsmfrw.models.aws.lm.RsAwsLambda;
import io.qameta.allure.Step;
import lombok.SneakyThrows;
import org.joda.time.DateTime;

import java.nio.ByteBuffer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class HlAwsLambda extends BsAwsSdkV1 {

  private AWSLambdaAsync client;
  private int INT_LAMBDA_RETRY_BOUND = 5;
  private int INT_LAMBDA_COUNTER = 0;

  public HlAwsLambda() {
    client = AWSLambdaAsyncClientBuilder.standard().withRegion(region).build();
  }

  @SneakyThrows @Step("AWS::Lambda: get response")
  public RsAwsLambda getResponse(String lambdaName, String rqEventText) {
    RsAwsLambda response = getAsyncResponse(lambdaName, rqEventText);
    return response == null ? getSyncResponse(lambdaName, rqEventText) : response;
  }

  private RsAwsLambda getAsyncResponse(String lambdaName, String rqEventText) {
    StringBuilder msg = new StringBuilder("\ninvoke aws lambda as ASYNC:");
    msg.append("\n\t").append(DateTime.now().toString()).append(" - name: ").append(lambdaName);
    msg.append("\n\t")
        .append(DateTime.now().toString())
        .append(" - request:\n")
        .append(rqEventText);
    InvokeRequest rq =
        new InvokeRequest()
            .withFunctionName(lambdaName)
            .withPayload(ByteBuffer.wrap(rqEventText.getBytes()));
    RsAwsLambda rsAwsLambda = null;
    try {
      Future<InvokeResult> future_res = client.invokeAsync(rq);
      msg.append("\n\t").append(DateTime.now().toString()).append(" - status:").append(" invoked");
      msg.append("\n\t")
          .append(DateTime.now().toString())
          .append(" - status:")
          .append(" running...");
      IsaCommonMethods.doWithWait(future_res::isDone, 3, 3, TimeUnit.SECONDS);
      InvokeResult rs = future_res.get();
      if (rs.getStatusCode() == 200) {
        msg.append("\n\t")
            .append(DateTime.now().toString())
            .append(" - status:")
            .append(" finished");
        msg.append("\n\t").append(DateTime.now().toString()).append(" - code: 200");
        ByteBuffer response_payload = rs.getPayload();
        rsAwsLambda = gson.fromJson(new String(response_payload.array()), RsAwsLambda.class);
        msg.append("\n\t")
            .append(DateTime.now().toString())
            .append(" - response:\n")
            .append(gson.toJson(rsAwsLambda));
      } else {
        msg.append("\n\t")
            .append(DateTime.now().toString())
            .append(" - status:")
            .append(DateTime.now().getMillis())
            .append(" finished");
        msg.append("\n\t")
            .append(DateTime.now().toString())
            .append(" - code: ")
            .append(rs.getStatusCode());
      }
    } catch (InterruptedException | ExecutionException e) {
      msg.append("\n\t -exception:").append(e.getMessage());
      //      throw new RuntimeException(e);
    } catch (TooManyRequestsException e) {
      IsaLogging.log(this.getClass(), e.getMessage());
    } finally {
      IsaLogging.log(this.getClass(), msg.toString());
    }
    return rsAwsLambda;
  }

  @Step("AWS::Lambda: get response")
  public RsAwsLambda getSyncResponse(String lambdaName, String rqEventText) {
    StringBuilder msg = new StringBuilder("\ninvoke aws lambda as SYNCHRONIZED:");
    msg.append("\n\t").append(DateTime.now().toString()).append(" - name: ").append(lambdaName);
    msg.append("\n\t")
        .append(DateTime.now().toString())
        .append(" - request:\n")
        .append(rqEventText);
    InvokeRequest rq =
        new InvokeRequest()
            .withFunctionName(lambdaName)
            .withPayload(ByteBuffer.wrap(rqEventText.getBytes()));
    RsAwsLambda rsAwsLambda = null;
    try {
      InvokeResult result = client.invoke(rq);
      msg.append("\n\t").append(DateTime.now().toString()).append(" - status:").append(" invoked");
      msg.append("\n\t")
          .append(DateTime.now().toString())
          .append(" - status:")
          .append(" running...");
      if (result.getStatusCode() == 200) {
        msg.append("\n\t")
            .append(DateTime.now().toString())
            .append(" - status:")
            .append(" finished");
        msg.append("\n\t").append(DateTime.now().toString()).append(" - code: 200");
        ByteBuffer response_payload = result.getPayload();
        rsAwsLambda = gson.fromJson(new String(response_payload.array()), RsAwsLambda.class);
        msg.append("\n\t")
            .append(DateTime.now().toString())
            .append(" - response:\n")
            .append(gson.toJson(rsAwsLambda));
      } else {
        msg.append("\n\t")
            .append(DateTime.now().toString())
            .append(" - status:")
            .append(DateTime.now().getMillis())
            .append(" finished");
        msg.append("\n\t")
            .append(DateTime.now().toString())
            .append(" - code: ")
            .append(result.getStatusCode());
      }
    } catch (TooManyRequestsException e) {
      IsaLogging.log(this.getClass(), e.getMessage());
    } finally {
      IsaLogging.log(this.getClass(), msg.toString());
    }
    return rsAwsLambda;
  }
}
