// 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 "chrome/browser/optimization_guide/optimization_guide_internals_ui.h"

#include <cstdint>
#include <memory>
#include <vector>

#include "base/hash/hash.h"
#include "base/i18n/time_formatting.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/grit/optimization_guide_internals_resources.h"
#include "components/grit/optimization_guide_internals_resources_map.h"
#include "components/optimization_guide/core/delivery/prediction_manager.h"
#include "components/optimization_guide/core/model_execution/feature_keys.h"
#include "components/optimization_guide/core/model_quality/model_quality_util.h"
#include "components/optimization_guide/core/optimization_guide_prefs.h"
#include "components/optimization_guide/optimization_guide_internals/webui/optimization_guide_internals.mojom.h"
#include "components/optimization_guide/optimization_guide_internals/webui/optimization_guide_internals_page_handler_impl.h"
#include "components/optimization_guide/proto/model_execution.pb.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_data_source.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
#include "ui/webui/webui_util.h"

bool OptimizationGuideInternalsUIConfig::IsWebUIEnabled(
    content::BrowserContext* browser_context) {
  Profile* profile = Profile::FromBrowserContext(browser_context);
  auto* service = OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
  return service != nullptr;
}

OptimizationGuideInternalsUI::OptimizationGuideInternalsUI(
    content::WebUI* web_ui)
    : MojoWebUIController(web_ui, /*enable_chrome_send=*/true) {
  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      web_ui->GetWebContents()->GetBrowserContext(),
      optimization_guide_internals::kChromeUIOptimizationGuideInternalsHost);
  webui::SetupWebUIDataSource(
      source, kOptimizationGuideInternalsResources,
      IDR_OPTIMIZATION_GUIDE_INTERNALS_OPTIMIZATION_GUIDE_INTERNALS_HTML);
}

OptimizationGuideInternalsUI::~OptimizationGuideInternalsUI() = default;

void OptimizationGuideInternalsUI::BindInterface(
    mojo::PendingReceiver<
        optimization_guide_internals::mojom::PageHandlerFactory> receiver) {
  // TODO(crbug.com/40215132): Remove the reset which is needed now since
  // |this| is reused on internals page reloads.
  optimization_guide_internals_page_factory_receiver_.reset();
  optimization_guide_internals_page_factory_receiver_.Bind(std::move(receiver));
}

void OptimizationGuideInternalsUI::CreatePageHandler(
    mojo::PendingRemote<optimization_guide_internals::mojom::Page> page) {
  Profile* profile = Profile::FromWebUI(web_ui());
  auto* service = OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
  DCHECK(service);
  OptimizationGuideLogger* optimization_guide_logger =
      service->GetOptimizationGuideLogger();
  optimization_guide_internals_page_handler_ =
      std::make_unique<OptimizationGuideInternalsPageHandlerImpl>(
          std::move(page), optimization_guide_logger);
}

void OptimizationGuideInternalsUI::RequestDownloadedModelsInfo(
    RequestDownloadedModelsInfoCallback callback) {
  Profile* profile = Profile::FromWebUI(web_ui());
  auto* service = OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
  optimization_guide::PredictionManager* prediction_manager =
      service->GetPredictionManager();
  std::vector<optimization_guide_internals::mojom::DownloadedModelInfoPtr>
      downloaded_models_info =
          prediction_manager->GetDownloadedModelsInfoForWebUI();
  std::move(callback).Run(std::move(downloaded_models_info));
}

void OptimizationGuideInternalsUI::RequestLoggedModelQualityClientIds(
    RequestLoggedModelQualityClientIdsCallback callback) {
  PrefService* local_state = g_browser_process->local_state();

  int64_t client_id =
      local_state->GetInt64(optimization_guide::model_execution::prefs::
                                localstate::kModelQualityLoggingClientId);

  // If the client id is zero no client id is set, in that case do nothing.
  if (client_id == 0) {
    std::move(callback).Run({});
    return;
  }

  // Get the client ids for the compose feature for the
  // past 28 days to show on chrome://optimization-guide-internals.
  std::vector<optimization_guide_internals::mojom::LoggedClientIdsPtr>
      logged_client_ids;
  // Initialize time outside to have it change when generating the client ids
  // for different days.
  base::Time now = base::Time::Now();
  // Loop through past 28 days to generate the client ids for compose
  // feature.
  for (int i = 0; i < 28; ++i) {
    base::Time day_i = now - base::Days(i);

    // Hash the client id with the date so that it changes everyday for the
    // feature.
    int64_t client_id_i_compose =
        optimization_guide::GetHashedModelQualityClientId(
            optimization_guide::proto::LogAiDataRequest::FeatureCase::kCompose,
            day_i, client_id);

    logged_client_ids.push_back(
        optimization_guide_internals::mojom::LoggedClientIds::New(
            client_id_i_compose));
  }

  std::move(callback).Run(std::move(logged_client_ids));
}

void OptimizationGuideInternalsUI::RequestMqlsLogs(
    RequestMqlsLogsCallback callback) {
  Profile* profile = Profile::FromWebUI(web_ui());
  auto* service = OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
  if (!service) {
    std::move(callback).Run({});
    return;
  }

  optimization_guide::ModelQualityLogsUploaderService*
      model_quality_logs_uploader_service =
          service->GetModelQualityLogsUploaderService();
  if (!model_quality_logs_uploader_service) {
    std::move(callback).Run({});
    return;
  }

  std::vector<optimization_guide_internals::mojom::MqlsLogPtr> mqls_logs =
      model_quality_logs_uploader_service->GetMqlsLogsForWebUI();
  std::move(callback).Run(std::move(mqls_logs));
}

WEB_UI_CONTROLLER_TYPE_IMPL(OptimizationGuideInternalsUI)
