// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "device/fido/mac/scoped_touch_id_test_environment.h"

#import <Security/Security.h>

#include <ostream>

#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "crypto/apple/scoped_fake_keychain_v2.h"
#include "device/fido/mac/authenticator_config.h"
#include "device/fido/mac/fake_touch_id_context.h"

namespace device::fido::mac {

static ScopedTouchIdTestEnvironment* g_current_environment = nullptr;

ScopedTouchIdTestEnvironment::ScopedTouchIdTestEnvironment(
    AuthenticatorConfig config)
    : config_(std::move(config)),
      keychain_(std::make_unique<crypto::apple::ScopedFakeKeychainV2>(
          config_.keychain_access_group)) {
  DCHECK(!g_current_environment);
  g_current_environment = this;

  // Override TouchIdContext::Create and TouchIdContext::IsAvailable.
  touch_id_context_create_ptr_ = TouchIdContext::g_create_;
  TouchIdContext::g_create_ = &ForwardCreate;

  touch_id_context_touch_id_available_ptr_ =
      TouchIdContext::g_touch_id_available_;
  TouchIdContext::g_touch_id_available_ = &ForwardTouchIdAvailable;
}

ScopedTouchIdTestEnvironment::~ScopedTouchIdTestEnvironment() {
  DCHECK(!next_touch_id_context_) << "unclaimed SimulatePromptSuccess() call";

  DCHECK(touch_id_context_create_ptr_);
  TouchIdContext::g_create_ = touch_id_context_create_ptr_;

  DCHECK(touch_id_context_touch_id_available_ptr_);
  TouchIdContext::g_touch_id_available_ =
      touch_id_context_touch_id_available_ptr_;
  g_current_environment = nullptr;
}

// static
std::unique_ptr<TouchIdContext> ScopedTouchIdTestEnvironment::ForwardCreate() {
  return g_current_environment->CreateTouchIdContext();
}

// static
bool ScopedTouchIdTestEnvironment::ForwardTouchIdAvailable(
    AuthenticatorConfig config) {
  return g_current_environment->TouchIdAvailable(std::move(config));
}

bool ScopedTouchIdTestEnvironment::SetTouchIdAvailable(bool available) {
  return touch_id_available_ = available;
}

bool ScopedTouchIdTestEnvironment::TouchIdAvailable(
    AuthenticatorConfig config) {
  return touch_id_available_;
}

void ScopedTouchIdTestEnvironment::SimulateTouchIdPromptSuccess() {
  CHECK(!next_touch_id_context_);
  next_touch_id_context_.reset(new FakeTouchIdContext);
  next_touch_id_context_->set_callback_result(true);
}

void ScopedTouchIdTestEnvironment::SimulateTouchIdPromptFailure() {
  CHECK(!next_touch_id_context_);
  next_touch_id_context_.reset(new FakeTouchIdContext);
  next_touch_id_context_->set_callback_result(false);
}

void ScopedTouchIdTestEnvironment::DoNotResolveNextPrompt() {
  next_touch_id_context_.reset(new FakeTouchIdContext);
  next_touch_id_context_->DoNotResolveNextPrompt();
}

std::unique_ptr<TouchIdContext>
ScopedTouchIdTestEnvironment::CreateTouchIdContext() {
  CHECK(next_touch_id_context_)
      << "Call SimulateTouchIdPromptSuccess/Failure() for every "
         "context created in the test environment.";
  return std::move(next_touch_id_context_);
}

}  // namespace device::fido::mac
