// Copyright 2022 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/live_caption/live_translate_controller.h"

#include <memory>
#include <string>
#include <utility>

#include "base/functional/bind.h"
#include "base/i18n/language_tag.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/metrics_hashes.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "components/live_caption/features.h"
#include "components/live_caption/pref_names.h"
#include "components/live_caption/translation_dispatcher.h"
#include "components/live_caption/translation_util.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "content/public/browser/browser_context.h"
#include "google_apis/google_api_keys.h"
#include "media/mojo/mojom/speech_recognition_result.h"

namespace captions {
namespace {

constexpr char kLiveOnDeviceTranslateDispatcherResult[] =
    "Accessibility.LiveTranslate.OnDeviceTranslation.Result";
constexpr char kLiveGoogleApiTranslateDispatcherResult[] =
    "Accessibility.LiveTranslate.GoogleApiTranslation.Result";
constexpr char kLiveOnDeviceTranslateDispatcherLatency[] =
    "Accessibility.LiveTranslate.OnDeviceTranslation.Latency";
constexpr char kLiveGoogleApiTranslateDispatcherLatency[] =
    "Accessibility.LiveTranslate.GoogleApiTranslation.Latency";
constexpr char kLiveTotalTranslationLatency[] =
    "Accessibility.LiveTranslate.GetTranslation.Latency";

}  // namespace

LiveTranslateController::LiveTranslateController(
    PrefService* profile_prefs,
    std::unique_ptr<TranslationDispatcher> on_device_dispatcher,
    std::unique_ptr<TranslationDispatcher> google_api_dispatcher)
    : profile_prefs_(profile_prefs),
      pref_change_registrar_(std::make_unique<PrefChangeRegistrar>()),
      on_device_dispatcher_(std::move(on_device_dispatcher)),
      google_api_dispatcher_(std::move(google_api_dispatcher)) {
  pref_change_registrar_->Init(profile_prefs_);
  pref_change_registrar_->Add(
      prefs::kLiveTranslateEnabled,
      base::BindRepeating(
          &LiveTranslateController::OnLiveTranslateEnabledChanged,
          // Unretained is safe because |this| owns |pref_change_registrar_|.
          base::Unretained(this)));
}

LiveTranslateController::~LiveTranslateController() = default;
// static
void LiveTranslateController::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterBooleanPref(prefs::kLiveTranslateEnabled, false);

  registry->RegisterLanguageTagPref(
      prefs::kLiveTranslateTargetLanguageCode,
      base::i18n::GetKnownLanguageTag("en"),
      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
}

void LiveTranslateController::GetTranslation(const std::string& result,
                                             std::string source_language,
                                             std::string target_language,
                                             TranslateEventCallback callback) {
  base::UmaHistogramSparse(
      "Accessibility.LiveTranslate.GetTranslation.SourceLanguage",
      base::HashMetricName(
          speech::GetBCP47LanguageCodeFromSodaLanguage(source_language)
              .value_or(source_language)));
  base::UmaHistogramSparse(
      "Accessibility.LiveTranslate.GetTranslation.TargetLanguage",
      base::HashMetricName(target_language));

  base::TimeTicks total_start_time = base::TimeTicks::Now();
  bool use_on_device = on_device_dispatcher_ &&
                       base::FeatureList::IsEnabled(
                           live_caption::kLiveCaptionOnDeviceTranslation);
  if (use_on_device &&
      base::FeatureList::IsEnabled(
          live_caption::kLiveCaptionOnDeviceTranslationEnglishOnly)) {
    auto is_english = [](const std::string& lang) {
      return base::StartsWith(lang, "en", base::CompareCase::INSENSITIVE_ASCII);
    };
    auto is_zh_tw = [](const std::string& lang) {
      return base::EqualsCaseInsensitiveASCII(lang, "zh-tw") ||
             base::EqualsCaseInsensitiveASCII(lang, "cmn-hant-tw");
    };

    if (!(is_english(source_language) || is_english(target_language)) ||
        is_zh_tw(source_language) || is_zh_tw(target_language)) {
      use_on_device = false;
    }
  }

  if (use_on_device) {
    on_device_dispatcher_->GetTranslation(
        result, source_language, target_language,
        base::BindOnce(&LiveTranslateController::OnOnDeviceTranslated,
                       weak_factory_.GetWeakPtr(), result, source_language,
                       target_language, std::move(callback), total_start_time));
  } else {
    google_api_dispatcher_->GetTranslation(
        result, source_language, target_language,
        base::BindOnce(&LiveTranslateController::OnGoogleApiTranslated,
                       weak_factory_.GetWeakPtr(), std::move(callback),
                       total_start_time, total_start_time));
  }
}

void LiveTranslateController::OnOnDeviceTranslated(
    std::string_view result,
    std::string_view source_language,
    std::string_view target_language,
    TranslateEventCallback callback,
    base::TimeTicks total_start_time,
    const TranslateEvent& translate_event) {
  base::UmaHistogramBoolean(kLiveOnDeviceTranslateDispatcherResult,
                            translate_event.has_value());
  if (!translate_event.has_value() && google_api_dispatcher_) {
    google_api_dispatcher_->GetTranslation(
        result, source_language, target_language,
        base::BindOnce(&LiveTranslateController::OnGoogleApiTranslated,
                       weak_factory_.GetWeakPtr(), std::move(callback),
                       total_start_time, base::TimeTicks::Now()));
    return;
  }

  if (translate_event.has_value()) {
    base::TimeDelta latency = base::TimeTicks::Now() - total_start_time;
    base::UmaHistogramTimes(kLiveOnDeviceTranslateDispatcherLatency, latency);
    base::UmaHistogramTimes(kLiveTotalTranslationLatency, latency);
  }
  std::move(callback).Run(translate_event);
}

void LiveTranslateController::OnGoogleApiTranslated(
    TranslateEventCallback callback,
    base::TimeTicks total_start_time,
    base::TimeTicks google_api_start_time,
    const TranslateEvent& translate_event) {
  base::UmaHistogramBoolean(kLiveGoogleApiTranslateDispatcherResult,
                            translate_event.has_value());
  if (translate_event.has_value()) {
    base::TimeTicks now = base::TimeTicks::Now();
    base::UmaHistogramTimes(kLiveGoogleApiTranslateDispatcherLatency,
                            now - google_api_start_time);
    base::UmaHistogramTimes(kLiveTotalTranslationLatency,
                            now - total_start_time);
  }
  std::move(callback).Run(translate_event);
}

void LiveTranslateController::OnLiveTranslateEnabledChanged() {
  if (profile_prefs_->GetBoolean(prefs::kLiveTranslateEnabled))
    profile_prefs_->SetBoolean(prefs::kLiveCaptionEnabled, true);
}

}  // namespace captions
