// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation
import XCTest

@testable import FirebaseAuth

@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
class SendVerificationCodeTests: RPCBaseTests {
  private let kTestSecret = "secret"
  private let kTestReceipt = "receipt"
  private let kTestReCAPTCHAToken = "reCAPTCHAToken"
  private let kPhoneNumberKey = "phoneNumber"
  private let kReceiptKey = "iosReceipt"
  private let kSecretKey = "iosSecret"
  private let kExpectedAPIURL =
    "https://www.googleapis.com/identitytoolkit/v3/relyingparty/sendVerificationCode?key=APIKey"

  /** @fn testSendVerificationCodeRequest
      @brief Tests the sendVerificationCode request with a ReCAPTCHA token.
   */
  func testSendVerificationCodeRequestReCAPTCHA() async throws {
    let request = makeSendVerificationCodeRequest(CodeIdentity.recaptcha(kTestReCAPTCHAToken))
    XCTAssertEqual(request.phoneNumber, kTestPhoneNumber)
    switch request.codeIdentity {
    case let .recaptcha(token):
      XCTAssertEqual(token, kTestReCAPTCHAToken)
    default:
      XCTFail("Should be a reCAPTCHA")
    }
    try await checkRequest(
      request: request,
      expected: kExpectedAPIURL,
      key: kPhoneNumberKey,
      value: kTestPhoneNumber
    )
    let requestDictionary = try XCTUnwrap(rpcIssuer.decodedRequest as? [String: AnyHashable])
    XCTAssertEqual(requestDictionary["recaptchaToken"], kTestReCAPTCHAToken)
  }

  /** @fn testSendVerificationCodeRequest
      @brief Tests the sendVerificationCode request with an App Credential
   */
  func testSendVerificationCodeRequestCredential() async throws {
    let credential = AuthAppCredential(receipt: kTestReceipt, secret: kTestSecret)
    let request = makeSendVerificationCodeRequest(CodeIdentity.credential(credential))
    XCTAssertEqual(request.phoneNumber, kTestPhoneNumber)
    switch request.codeIdentity {
    case let .credential(credential):
      XCTAssertEqual(credential.secret, kTestSecret)
      XCTAssertEqual(credential.receipt, kTestReceipt)
    default:
      XCTFail("Should be a credential")
    }
    try await checkRequest(
      request: request,
      expected: kExpectedAPIURL,
      key: kPhoneNumberKey,
      value: kTestPhoneNumber
    )
    let requestDictionary = try XCTUnwrap(rpcIssuer.decodedRequest as? [String: AnyHashable])
    XCTAssertEqual(requestDictionary[kReceiptKey], kTestReceipt)
    XCTAssertEqual(requestDictionary[kSecretKey], kTestSecret)
  }

  func testSendVerificationCodeRequestErrors() async throws {
    let kInvalidPhoneNumberErrorMessage = "INVALID_PHONE_NUMBER"
    let kQuotaExceededErrorMessage = "QUOTA_EXCEEDED"
    let kAppNotVerifiedErrorMessage = "APP_NOT_VERIFIED"
    let kCaptchaCheckFailedErrorMessage = "CAPTCHA_CHECK_FAILED"

    try await checkBackendError(
      request: makeSendVerificationCodeRequest(CodeIdentity.recaptcha(kTestReCAPTCHAToken)),
      message: kInvalidPhoneNumberErrorMessage,
      errorCode: AuthErrorCode.invalidPhoneNumber
    )
    try await checkBackendError(
      request: makeSendVerificationCodeRequest(CodeIdentity.recaptcha(kTestReCAPTCHAToken)),
      message: kQuotaExceededErrorMessage,
      errorCode: AuthErrorCode.quotaExceeded
    )
    try await checkBackendError(
      request: makeSendVerificationCodeRequest(CodeIdentity.recaptcha(kTestReCAPTCHAToken)),
      message: kAppNotVerifiedErrorMessage,
      errorCode: AuthErrorCode.appNotVerified
    )
    try await checkBackendError(
      request: makeSendVerificationCodeRequest(CodeIdentity.recaptcha(kTestReCAPTCHAToken)),
      message: kCaptchaCheckFailedErrorMessage,
      errorCode: AuthErrorCode.captchaCheckFailed
    )
  }

  /** @fn testSuccessfulSendVerificationCodeResponse
      @brief This test simulates a successful verify CustomToken flow.
   */
  func testSuccessfulSendVerificationCodeResponse() async throws {
    let kVerificationIDKey = "sessionInfo"
    let kFakeVerificationID = "testVerificationID"

    rpcIssuer.respondBlock = {
      try self.rpcIssuer.respond(withJSON: [kVerificationIDKey: kFakeVerificationID])
    }
    let rpcResponse = try await authBackend.call(with:
      makeSendVerificationCodeRequest(CodeIdentity.recaptcha(kTestReCAPTCHAToken)))
    XCTAssertNotNil(rpcResponse)
    XCTAssertEqual(rpcResponse.verificationID, kFakeVerificationID)
  }

  private func makeSendVerificationCodeRequest(_ codeIdentity: CodeIdentity)
    -> SendVerificationCodeRequest {
    return SendVerificationCodeRequest(phoneNumber: kTestPhoneNumber,
                                       codeIdentity: codeIdentity,
                                       requestConfiguration: makeRequestConfiguration())
  }
}
