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

#include "chrome/test/chromedriver/fedcm_commands.h"

#include "base/values.h"
#include "chrome/test/chromedriver/chrome/fedcm_tracker.h"
#include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/chrome/web_view.h"
#include "chrome/test/chromedriver/session.h"

Status ExecuteCancelDialog(Session* session,
                           WebView* web_view,
                           const base::DictValue& params,
                           std::unique_ptr<base::Value>* value,
                           Timeout* timeout) {
  FedCmTracker* tracker = nullptr;
  Status status = web_view->GetFedCmTracker(&tracker);
  if (!status.IsOk()) {
    return status;
  }
  if (!tracker->HasDialog()) {
    return Status(kNoSuchAlert);
  }

  base::DictValue command_params;
  command_params.Set("dialogId", tracker->GetLastDialogId());

  std::unique_ptr<base::Value> result;
  status = web_view->SendCommandAndGetResult("FedCm.dismissDialog",
                                             command_params, &result);
  tracker->DialogClosed();
  return status;
}

Status ExecuteSelectAccount(Session* session,
                            WebView* web_view,
                            const base::DictValue& params,
                            std::unique_ptr<base::Value>* value,
                            Timeout* timeout) {
  FedCmTracker* tracker = nullptr;
  Status status = web_view->GetFedCmTracker(&tracker);
  if (!status.IsOk()) {
    return status;
  }
  if (!tracker->HasDialog()) {
    return Status(kNoSuchAlert);
  }
  if (!params.FindInt("accountIndex")) {
    return Status(kInvalidArgument, "accountIndex must be specified");
  }

  base::DictValue command_params;
  command_params.Set("dialogId", tracker->GetLastDialogId());
  command_params.Set("accountIndex", *params.FindInt("accountIndex"));

  std::unique_ptr<base::Value> result;
  status = web_view->SendCommandAndGetResult("FedCm.selectAccount",
                                             command_params, &result);
  // Only mark the dialog as closed if the command succeeded. For example,
  // if there is a dialog up but it is not an account chooser, selectAccount
  // will fail but the dialog is still up and a later canceldialog command
  // should succeed.
  if (status.IsOk()) {
    tracker->DialogClosed();
  }
  return status;
}

Status ExecuteClickDialogButton(Session* session,
                                WebView* web_view,
                                const base::DictValue& params,
                                std::unique_ptr<base::Value>* value,
                                Timeout* timeout) {
  FedCmTracker* tracker = nullptr;
  Status status = web_view->GetFedCmTracker(&tracker);
  if (!status.IsOk()) {
    return status;
  }
  if (!tracker->HasDialog()) {
    return Status(kNoSuchAlert);
  }
  if (!params.FindString("dialogButton")) {
    return Status(kInvalidArgument, "dialogButton must be specified");
  }

  base::DictValue command_params;
  command_params.Set("dialogId", tracker->GetLastDialogId());

  std::string button = *params.FindString("dialogButton");
  if (button == "TermsOfService" || button == "PrivacyPolicy") {
    std::optional<int> index = params.FindInt("index");
    if (!index) {
      return Status(kInvalidArgument, "index must be specified");
    }
    command_params.Set("accountIndex", *index);
    command_params.Set("accountUrlType", button);
    std::unique_ptr<base::Value> result;
    return web_view->SendCommandAndGetResult("FedCm.openUrl", command_params,
                                             &result);
  }

  command_params.Set("dialogButton", button);

  std::unique_ptr<base::Value> result;
  status = web_view->SendCommandAndGetResult("FedCm.clickDialogButton",
                                             command_params, &result);
  tracker->DialogClosed();
  return status;
}

Status ExecuteGetAccounts(Session* session,
                          WebView* web_view,
                          const base::DictValue& params,
                          std::unique_ptr<base::Value>* value,
                          Timeout* timeout) {
  FedCmTracker* tracker = nullptr;
  Status status = web_view->GetFedCmTracker(&tracker);
  if (!status.IsOk()) {
    return status;
  }
  if (!tracker->HasDialog()) {
    return Status(kNoSuchAlert);
  }
  *value = std::make_unique<base::Value>(tracker->GetLastAccounts().Clone());
  return Status(kOk);
}

Status ExecuteGetDialogType(Session* session,
                            WebView* web_view,
                            const base::DictValue& params,
                            std::unique_ptr<base::Value>* value,
                            Timeout* timeout) {
  FedCmTracker* tracker = nullptr;
  Status status = web_view->GetFedCmTracker(&tracker);
  if (!status.IsOk()) {
    return status;
  }
  if (!tracker->HasDialog()) {
    return Status(kNoSuchAlert);
  }
  *value = std::make_unique<base::Value>(tracker->GetLastDialogType());
  return Status(kOk);
}

Status ExecuteGetFedCmTitle(Session* session,
                            WebView* web_view,
                            const base::DictValue& params,
                            std::unique_ptr<base::Value>* value,
                            Timeout* timeout) {
  FedCmTracker* tracker = nullptr;
  Status status = web_view->GetFedCmTracker(&tracker);
  if (!status.IsOk()) {
    return status;
  }
  if (!tracker->HasDialog()) {
    return Status(kNoSuchAlert);
  }
  base::DictValue dict;
  dict.Set("title", tracker->GetLastTitle());
  std::optional<std::string> subtitle = tracker->GetLastSubtitle();
  if (subtitle) {
    dict.Set("subtitle", *subtitle);
  }
  *value = std::make_unique<base::Value>(std::move(dict));
  return Status(kOk);
}

Status ExecuteSetDelayEnabled(Session* session,
                              WebView* web_view,
                              const base::DictValue& params,
                              std::unique_ptr<base::Value>* value,
                              Timeout* timeout) {
  // We don't technically need the tracker to implement this command. However,
  // the tracker calls enable during initialization, so if it gets initialized
  // after this command, it would overwrite the delay enabled flag.
  // To avoid that, ensure it is created here.
  FedCmTracker* tracker = nullptr;
  Status status = web_view->GetFedCmTracker(&tracker);
  if (!status.IsOk()) {
    return status;
  }

  if (!params.FindBool("enabled")) {
    return Status(kInvalidArgument, "enabled must be specified");
  }

  base::DictValue command_params;
  command_params.Set("disableRejectionDelay", !*params.FindBool("enabled"));

  std::unique_ptr<base::Value> result;
  status = web_view->SendCommandAndGetResult("FedCm.enable", command_params,
                                             &result);
  return status;
}

Status ExecuteResetCooldown(Session* session,
                            WebView* web_view,
                            const base::DictValue& params,
                            std::unique_ptr<base::Value>* value,
                            Timeout* timeout) {
  std::unique_ptr<base::Value> result;
  Status status = web_view->SendCommandAndGetResult("FedCm.resetCooldown",
                                                    base::DictValue(), &result);
  return status;
}
