package com.amazonaws.accessanalyzer.analyzer;

import static com.amazonaws.accessanalyzer.analyzer.TestUtil.ANALYZER_ARN;
import static com.amazonaws.accessanalyzer.analyzer.TestUtil.ANALYZER_NAME;
import static com.amazonaws.accessanalyzer.analyzer.TestUtil.CLIENT_REQUEST_TOKEN;
import static com.amazonaws.accessanalyzer.analyzer.TestUtil.LOGICAL_RESOURCE_ID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;

import com.amazonaws.AmazonServiceException;
import lombok.val;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import software.amazon.awssdk.services.accessanalyzer.model.CreateAnalyzerRequest;
import software.amazon.awssdk.services.accessanalyzer.model.CreateAnalyzerResponse;
import software.amazon.awssdk.services.accessanalyzer.model.ServiceQuotaExceededException;
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
import software.amazon.cloudformation.proxy.HandlerErrorCode;
import software.amazon.cloudformation.proxy.Logger;
import software.amazon.cloudformation.proxy.OperationStatus;
import software.amazon.cloudformation.proxy.ProgressEvent;
import software.amazon.cloudformation.proxy.ResourceHandlerRequest;

class CreateHandlerTest {

  @Mock
  private AmazonWebServicesClientProxy proxy;

  @Mock
  private Logger logger;

  @BeforeEach
  void setup() {
    proxy = mock(AmazonWebServicesClientProxy.class);
    logger = mock(Logger.class);
  }

  @Test
  void testCreateAnalyzer() {
    // The desired state does not mention a name or ARN.  They are created from the
    // client request token and logical resource id.
    val desiredState = ResourceModel.builder()
        .type(TestUtil.ACCOUNT)
        .build();
    val request = ResourceHandlerRequest.<ResourceModel>builder()
        .logicalResourceIdentifier(LOGICAL_RESOURCE_ID)
        .clientRequestToken(CLIENT_REQUEST_TOKEN)
        .desiredResourceState(desiredState)
        .build();
    val Response = CreateAnalyzerResponse.builder().arn(ANALYZER_ARN).build();
    doReturn(Response).when(proxy).injectCredentialsAndInvokeV2(any(CreateAnalyzerRequest.class), any());
    val response = invokeHandleRequest(request);
    val actualState = response.getResourceModel();
    assertThat(response).isNotNull();
    assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS);
    assertThat(response.getCallbackContext()).isNull();
    assertThat(response.getResourceModels()).isNull();
    assertThat(response.getResourceModel()).isEqualTo(desiredState);
    assertThat(response.getMessage()).isNull();
    assertThat(response.getErrorCode()).isNull();
    assertThat(actualState.getAnalyzerName()).isEqualTo(desiredState.getAnalyzerName());
    assertThat(actualState.getArn()).isEqualTo(desiredState.getArn());
    assertThat(actualState.getType()).isEqualTo(desiredState.getType());
  }

  @Test
  void testLimitExceededServiceException() {
    doThrow(ServiceQuotaExceededException.builder().message("too many analyzers for account").build())
        .when(proxy)
        .injectCredentialsAndInvokeV2(any(), any());
    val desiredState = ResourceModel.builder()
        .arn(ANALYZER_ARN)
        .analyzerName(ANALYZER_NAME)
        .type(TestUtil.ACCOUNT)
        .build();
    val request = ResourceHandlerRequest.<ResourceModel>builder()
        .logicalResourceIdentifier(LOGICAL_RESOURCE_ID)
        .clientRequestToken(CLIENT_REQUEST_TOKEN)
        .desiredResourceState(desiredState)
        .build();
    val response = invokeHandleRequest(request);
    assertThat(response).isNotNull();
    assertThat(response.getStatus()).isEqualTo(OperationStatus.FAILED);
    assertThat(response.getCallbackContext()).isNull();
    assertThat(response.getResourceModels()).isNull();
    assertThat(response.getResourceModel()).isNull();
    assertThat(response.getMessage()).startsWith("too many analyzers for account");
    assertThat(response.getErrorCode()).isEqualTo(HandlerErrorCode.ServiceLimitExceeded);
  }

  @Test
  void testUnknownServiceException() {
    doThrow(new AmazonServiceException("internal failure"))
        .when(proxy)
        .injectCredentialsAndInvokeV2(any(), any());
    val desiredState = ResourceModel.builder()
        .arn(ANALYZER_ARN)
        .analyzerName(ANALYZER_NAME)
        .type(TestUtil.ACCOUNT)
        .build();
    val request = ResourceHandlerRequest.<ResourceModel>builder()
        .logicalResourceIdentifier(LOGICAL_RESOURCE_ID)
        .clientRequestToken(CLIENT_REQUEST_TOKEN)
        .desiredResourceState(desiredState)
        .build();
    val response = invokeHandleRequest(request);
    assertThat(response).isNotNull();
    assertThat(response.getStatus()).isEqualTo(OperationStatus.FAILED);
    assertThat(response.getResourceModel()).isNull();
    assertThat(response.getMessage()).startsWith("internal failure");
    assertThat(response.getErrorCode()).isEqualTo(HandlerErrorCode.ServiceInternalError);
  }

  private ProgressEvent<ResourceModel, CallbackContext> invokeHandleRequest(
      ResourceHandlerRequest<ResourceModel> request) {
    return new CreateHandler().handleRequest(proxy, request, new CallbackContext(), logger);
  }
}