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

#include "components/autofill/core/browser/payments/payments_window_manager_util.h"

#include <string>
#include <string_view>
#include <utility>

#include "base/check.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "components/autofill/core/browser/data_manager/personal_data_manager.h"
#include "components/autofill/core/browser/data_model/payments/credit_card.h"
#include "components/autofill/core/browser/foundations/autofill_client.h"
#include "components/autofill/core/browser/metrics/payments/bnpl_metrics.h"
#include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
#include "components/autofill/core/browser/payments/payments_request_details.h"
#include "components/autofill/core/browser/payments/payments_util.h"
#include "components/autofill/core/browser/payments/payments_window_manager.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/third_party/mozilla/url_parse.h"

namespace autofill::payments {

base::expected<PaymentsWindowManager::RedirectCompletionResult,
               PaymentsWindowManager::Vcn3dsAuthenticationResult>
ParseUrlForVcn3ds(const GURL& url,
                  const Vcn3dsChallengeOptionMetadata& metadata) {
  std::string redirect_completion_result;
  bool is_failure = false;
  std::string_view query_piece = url.query();
  url::Component query(0, query_piece.length());
  url::Component key;
  url::Component value;
  while (url::ExtractQueryKeyValue(query_piece, &query, &key, &value)) {
    std::string_view key_view = query_piece.substr(key.begin, key.len);
    std::string_view value_view = query_piece.substr(value.begin, value.len);
    if (key_view == metadata.success_query_param_name) {
      redirect_completion_result = std::string(value_view);
    } else if (key_view == metadata.failure_query_param_name) {
      is_failure = true;
    }
  }

  // `redirect_completion_result` being present indicates the user completed the
  // authentication and a request to the Payments servers is required to
  // retrieve the authentication result.
  if (!redirect_completion_result.empty()) {
    return base::ok(PaymentsWindowManager::RedirectCompletionResult(
        redirect_completion_result));
  }

  // `is_failure` being true indicates the authentication has failed.
  if (is_failure) {
    return base::unexpected(PaymentsWindowManager::Vcn3dsAuthenticationResult::
                                kAuthenticationFailed);
  }

  return base::unexpected(PaymentsWindowManager::Vcn3dsAuthenticationResult::
                              kAuthenticationNotCompleted);
}

PaymentsWindowManager::BnplPopupStatus ParseUrlForBnpl(
    const GURL& url,
    const PaymentsWindowManager::BnplContext& bnpl_context) {
  if (url.spec().find(bnpl_context.success_url_prefix.spec()) == 0) {
    return PaymentsWindowManager::BnplPopupStatus::kSuccess;
  }

  if (url.spec().find(bnpl_context.failure_url_prefix.spec()) == 0) {
    return PaymentsWindowManager::BnplPopupStatus::kFailure;
  }

  return PaymentsWindowManager::BnplPopupStatus::kNotFinished;
}

UnmaskRequestDetails CreateUnmaskRequestDetailsForVcn3ds(
    AutofillClient& client,
    const PaymentsWindowManager::Vcn3dsContext& context,
    PaymentsWindowManager::RedirectCompletionResult
        redirect_completion_result) {
  UnmaskRequestDetails request_details;
  request_details.card = context.card;
  request_details.billing_customer_number = GetBillingCustomerId(
      client.GetPersonalDataManager().payments_data_manager());
  request_details.risk_data = context.risk_data;
  request_details.context_token = context.context_token;

  if (const url::Origin& origin =
          client.GetLastCommittedPrimaryMainFrameOrigin();
      !origin.opaque()) {
    request_details.last_committed_primary_main_frame_origin = origin.GetURL();
  }

  request_details.selected_challenge_option = context.challenge_option;
  request_details.redirect_completion_result =
      std::move(redirect_completion_result);
  return request_details;
}

PaymentsWindowManager::Vcn3dsAuthenticationResponse
CreateVcn3dsAuthenticationResponseFromServerResult(
    PaymentsAutofillClient::PaymentsRpcResult result,
    const UnmaskResponseDetails& response_details,
    CreditCard card) {
  PaymentsWindowManager::Vcn3dsAuthenticationResponse response;
  if (result == PaymentsAutofillClient::PaymentsRpcResult::kSuccess) {
    response.result =
        PaymentsWindowManager::Vcn3dsAuthenticationResult::kSuccess;
    card.SetNumber(base::UTF8ToUTF16(response_details.real_pan));
    card.SetExpirationMonthFromString(
        base::UTF8ToUTF16(response_details.expiration_month),
        /*app_locale=*/std::string());
    card.SetExpirationYearFromString(
        base::UTF8ToUTF16(response_details.expiration_year));
    card.set_cvc(base::UTF8ToUTF16(response_details.dcvv));
    response.card = std::move(card);
  } else {
    response.result = PaymentsWindowManager::Vcn3dsAuthenticationResult::
        kAuthenticationFailed;
  }
  return response;
}

void TriggerCompletionCallbackAndLogMetricsForBnpl(
    PaymentsWindowManager::FlowState&& flow_state) {
  CHECK(flow_state.bnpl_context.has_value());
  CHECK(flow_state.bnpl_popup_shown_timestamp.has_value());

  PaymentsWindowManager::BnplPopupStatus status = ParseUrlForBnpl(
      flow_state.most_recent_url_navigation, flow_state.bnpl_context.value());
  PaymentsWindowManager::BnplFlowResult result;
  switch (status) {
    case PaymentsWindowManager::BnplPopupStatus::kSuccess:
      result = PaymentsWindowManager::BnplFlowResult::kSuccess;
      break;
    case PaymentsWindowManager::BnplPopupStatus::kFailure:
      result = PaymentsWindowManager::BnplFlowResult::kFailure;
      break;
    case PaymentsWindowManager::BnplPopupStatus::kNotFinished:
      result = PaymentsWindowManager::BnplFlowResult::kUserClosed;
      break;
  }
  std::move(flow_state.bnpl_context->completion_callback)
      .Run(result, std::move(flow_state.most_recent_url_navigation));
  autofill_metrics::LogBnplPopupWindowResult(flow_state.bnpl_context->issuer_id,
                                             result);
  autofill_metrics::LogBnplPopupWindowLatency(
      /*duration=*/base::TimeTicks::Now() -
          flow_state.bnpl_popup_shown_timestamp.value(),
      /*issuer_id=*/flow_state.bnpl_context->issuer_id,
      /*result=*/result);
}

}  // namespace autofill::payments
