// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

@import camera_avfoundation;
#if __has_include(<camera_avfoundation/camera_avfoundation-umbrella.h>)
@import camera_avfoundation.Test;
#endif
@import AVFoundation;
@import XCTest;
#import <OCMock/OCMock.h>
#import "CameraTestUtils.h"

@interface CameraPermissionTests : XCTestCase

@end

@implementation CameraPermissionTests

#pragma mark - camera permissions

- (void)testRequestCameraPermission_completeWithoutErrorIfPrevoiuslyAuthorized {
  XCTestExpectation *expectation =
      [self expectationWithDescription:
                @"Must copmlete without error if camera access was previously authorized."];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
      .andReturn(AVAuthorizationStatusAuthorized);

  FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
    if (error == nil) {
      [expectation fulfill];
    }
  });
  [self waitForExpectationsWithTimeout:1 handler:nil];
}
- (void)testRequestCameraPermission_completeWithErrorIfPreviouslyDenied {
  XCTestExpectation *expectation =
      [self expectationWithDescription:
                @"Must complete with error if camera access was previously denied."];
  FlutterError *expectedError =
      [FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt"
                          message:@"User has previously denied the camera access request. Go to "
                                  @"Settings to enable camera access."
                          details:nil];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
      .andReturn(AVAuthorizationStatusDenied);
  FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
    if ([error isEqual:expectedError]) {
      [expectation fulfill];
    }
  });
  [self waitForExpectationsWithTimeout:1 handler:nil];
}

- (void)testRequestCameraPermission_completeWithErrorIfRestricted {
  XCTestExpectation *expectation =
      [self expectationWithDescription:@"Must complete with error if camera access is restricted."];
  FlutterError *expectedError = [FlutterError errorWithCode:@"CameraAccessRestricted"
                                                    message:@"Camera access is restricted. "
                                                    details:nil];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
      .andReturn(AVAuthorizationStatusRestricted);

  FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
    if ([error isEqual:expectedError]) {
      [expectation fulfill];
    }
  });
  [self waitForExpectationsWithTimeout:1 handler:nil];
}

- (void)testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess {
  XCTestExpectation *grantedExpectation = [self
      expectationWithDescription:@"Must complete without error if user choose to grant access"];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
      .andReturn(AVAuthorizationStatusNotDetermined);
  // Mimic user choosing "allow" in permission dialog.
  OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo
                              completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) {
                                block(YES);
                                return YES;
                              }]]);

  FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
    if (error == nil) {
      [grantedExpectation fulfill];
    }
  });
  [self waitForExpectationsWithTimeout:1 handler:nil];
}

- (void)testRequestCameraPermission_completeWithErrorIfUserDenyAccess {
  XCTestExpectation *expectation =
      [self expectationWithDescription:@"Must complete with error if user choose to deny access"];
  FlutterError *expectedError =
      [FlutterError errorWithCode:@"CameraAccessDenied"
                          message:@"User denied the camera access request."
                          details:nil];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo])
      .andReturn(AVAuthorizationStatusNotDetermined);

  // Mimic user choosing "deny" in permission dialog.
  OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo
                              completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) {
                                block(NO);
                                return YES;
                              }]]);
  FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) {
    if ([error isEqual:expectedError]) {
      [expectation fulfill];
    }
  });

  [self waitForExpectationsWithTimeout:1 handler:nil];
}

#pragma mark - audio permissions

- (void)testRequestAudioPermission_completeWithoutErrorIfPrevoiuslyAuthorized {
  XCTestExpectation *expectation =
      [self expectationWithDescription:
                @"Must copmlete without error if audio access was previously authorized."];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
      .andReturn(AVAuthorizationStatusAuthorized);

  FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
    if (error == nil) {
      [expectation fulfill];
    }
  });
  [self waitForExpectationsWithTimeout:1 handler:nil];
}
- (void)testRequestAudioPermission_completeWithErrorIfPreviouslyDenied {
  XCTestExpectation *expectation =
      [self expectationWithDescription:
                @"Must complete with error if audio access was previously denied."];
  FlutterError *expectedError =
      [FlutterError errorWithCode:@"AudioAccessDeniedWithoutPrompt"
                          message:@"User has previously denied the audio access request. Go to "
                                  @"Settings to enable audio access."
                          details:nil];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
      .andReturn(AVAuthorizationStatusDenied);
  FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
    if ([error isEqual:expectedError]) {
      [expectation fulfill];
    }
  });
  [self waitForExpectationsWithTimeout:1 handler:nil];
}

- (void)testRequestAudioPermission_completeWithErrorIfRestricted {
  XCTestExpectation *expectation =
      [self expectationWithDescription:@"Must complete with error if audio access is restricted."];
  FlutterError *expectedError = [FlutterError errorWithCode:@"AudioAccessRestricted"
                                                    message:@"Audio access is restricted. "
                                                    details:nil];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
      .andReturn(AVAuthorizationStatusRestricted);

  FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
    if ([error isEqual:expectedError]) {
      [expectation fulfill];
    }
  });
  [self waitForExpectationsWithTimeout:1 handler:nil];
}

- (void)testRequestAudioPermission_completeWithoutErrorIfUserGrantAccess {
  XCTestExpectation *grantedExpectation = [self
      expectationWithDescription:@"Must complete without error if user choose to grant access"];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
      .andReturn(AVAuthorizationStatusNotDetermined);
  // Mimic user choosing "allow" in permission dialog.
  OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeAudio
                              completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) {
                                block(YES);
                                return YES;
                              }]]);

  FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
    if (error == nil) {
      [grantedExpectation fulfill];
    }
  });
  [self waitForExpectationsWithTimeout:1 handler:nil];
}

- (void)testRequestAudioPermission_completeWithErrorIfUserDenyAccess {
  XCTestExpectation *expectation =
      [self expectationWithDescription:@"Must complete with error if user choose to deny access"];
  FlutterError *expectedError = [FlutterError errorWithCode:@"AudioAccessDenied"
                                                    message:@"User denied the audio access request."
                                                    details:nil];

  id mockDevice = OCMClassMock([AVCaptureDevice class]);
  OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio])
      .andReturn(AVAuthorizationStatusNotDetermined);

  // Mimic user choosing "deny" in permission dialog.
  OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeAudio
                              completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) {
                                block(NO);
                                return YES;
                              }]]);
  FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) {
    if ([error isEqual:expectedError]) {
      [expectation fulfill];
    }
  });

  [self waitForExpectationsWithTimeout:1 handler:nil];
}

@end
