// 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/browser/ui/download/download_bubble_row_view_info.h"

#include "chrome/browser/download/download_ui_safe_browsing_util.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/download/download_item_mode.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/enterprise/buildflags/buildflags.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/views/vector_icons.h"

#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
#include "chrome/browser/enterprise/connectors/common.h"
#endif

#if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
#include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
#endif

using download::DownloadItem;
using offline_items_collection::FailState;
using TailoredVerdict = safe_browsing::ClientDownloadResponse::TailoredVerdict;
using TailoredWarningType = DownloadUIModel::TailoredWarningType;

DownloadBubbleRowViewInfoObserver::DownloadBubbleRowViewInfoObserver() =
    default;

DownloadBubbleRowViewInfoObserver::~DownloadBubbleRowViewInfoObserver() {
  CHECK(!IsInObserverList());
}

DownloadBubbleRowViewInfo::DownloadBubbleRowViewInfo(
    DownloadUIModel::DownloadUIModelPtr model)
    : model_(std::move(model)), state_(model_->GetState()) {
  // Ignore whether we changed anything because it's the initial setup
  PopulateFromModel();

  model_->SetDelegate(this);
}

DownloadBubbleRowViewInfo::~DownloadBubbleRowViewInfo() {
  model_->SetDelegate(nullptr);
}

void DownloadBubbleRowViewInfo::SetQuickActionsForTesting(
    const std::vector<DownloadBubbleQuickAction>& actions) {
  quick_actions_ = actions;
}

void DownloadBubbleRowViewInfo::OnDownloadOpened() {
  model_->SetActionedOn(true);
}

void DownloadBubbleRowViewInfo::OnDownloadUpdated() {
  if (state_ != model_->GetState()) {
    NotifyObservers(&DownloadBubbleRowViewInfoObserver::OnDownloadStateChanged,
                    state_, model_->GetState());
  }

  PopulateFromModel();
  NotifyObservers(&DownloadBubbleRowViewInfoObserver::OnInfoChanged);
}

void DownloadBubbleRowViewInfo::OnDownloadDestroyed(
    const offline_items_collection::ContentId& id) {
  NotifyObservers(&DownloadBubbleRowViewInfoObserver::OnDownloadDestroyed, id);
}

void DownloadBubbleRowViewInfo::PopulateFromModel() {
  Reset();
  icon_and_color_ = IconAndColorForDownload(*model_);
  // Add primary button/quick actions for in-progress (paused or active), and
  // completed downloads
  quick_actions_ = QuickActionsForDownload(*model_);
  progress_bar_ = ProgressBarForDownload(*model_);

  switch (model_->GetState()) {
    case DownloadItem::IN_PROGRESS:
    case DownloadItem::COMPLETE:
      PopulateForInProgressOrComplete();
      return;
    case DownloadItem::INTERRUPTED: {
      const FailState fail_state = model_->GetLastFailState();
      if (fail_state != FailState::USER_CANCELED) {
        PopulateForInterrupted(fail_state);
        return;
      }
    }
      [[fallthrough]];
    case DownloadItem::CANCELLED:
    case DownloadItem::MAX_DOWNLOAD_STATE:
      return;
  }
}

void DownloadBubbleRowViewInfo::PopulateForInProgressOrComplete() {
  switch (model_->GetInsecureDownloadStatus()) {
    case download::DownloadItem::InsecureDownloadStatus::BLOCK:
    case download::DownloadItem::InsecureDownloadStatus::WARN:
      PopulateSuspiciousUiPattern();
      primary_button_command_ = DownloadCommands::Command::KEEP;
      return;
    case download::DownloadItem::InsecureDownloadStatus::UNKNOWN:
    case download::DownloadItem::InsecureDownloadStatus::SAFE:
    case download::DownloadItem::InsecureDownloadStatus::VALIDATED:
    case download::DownloadItem::InsecureDownloadStatus::SILENT_BLOCK:
      break;
  }

#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
  if (enterprise_connectors::ShouldPromptReviewForDownload(
          model_->profile(), model_->GetDownloadItem())) {
    switch (model_->GetDangerType()) {
      case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
        primary_button_command_ = DownloadCommands::Command::REVIEW;
        return;
      case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
        secondary_text_color_ = kColorDownloadItemTextWarning;
        primary_button_command_ = DownloadCommands::Command::REVIEW;
        return;
      case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
        secondary_text_color_ = kColorDownloadItemTextWarning;
        primary_button_command_ = DownloadCommands::Command::REVIEW;
        return;
      default:
        break;
    }
  }
#endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)

  if (TailoredWarningType type = model_->GetTailoredWarningType();
      type != TailoredWarningType::kNoTailoredWarning) {
    PopulateForTailoredWarning(type);
    return;
  }

  switch (model_->GetDangerType()) {
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
      if (model_->IsExtensionDownload()) {
        PopulateSuspiciousUiPattern();
        return;
      } else {
        if (WasSafeBrowsingVerdictObtained(model_->GetDownloadItem())) {
          PopulateSuspiciousUiPattern();
          return;
        }
        if (ShouldShowWarningForNoSafeBrowsing(model_->profile())) {
          PopulateForFileTypeWarningNoSafeBrowsing();
          return;
        }
        PopulateSuspiciousUiPattern();
        return;
      }
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
    case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
      PopulateDangerousUiPattern();
      return;
    case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
      bool request_ap_verdicts = false;
#if BUILDFLAG(FULL_SAFE_BROWSING)
      request_ap_verdicts =
          safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
              model_->profile())
              ->IsUnderAdvancedProtection();
#endif
      if (request_ap_verdicts) {
        has_subpage_ = true;
        secondary_text_color_ = kColorDownloadItemTextWarning;
        return;
      } else {
        PopulateSuspiciousUiPattern();
        return;
      }
    }
    case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING: {
      has_subpage_ = true;
      secondary_text_color_ = kColorDownloadItemTextWarning;
      primary_button_command_ = DownloadCommands::Command::DISCARD;
      return;
    }
    case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING: {
      secondary_text_color_ = kColorDownloadItemTextWarning;
      has_subpage_ = true;
      return;
    }
    case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING: {
      secondary_text_color_ = kColorDownloadItemTextWarning;
      has_subpage_ = true;
      return;
    }
    case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
      has_subpage_ = true;
      return;
    case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING:
      has_subpage_ = true;
      return;
    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED:
      primary_button_command_ = DownloadCommands::Command::OPEN_WHEN_COMPLETE;
      secondary_text_color_ = kColorDownloadItemTextWarning;
      main_button_enabled_ = false;
      return;
    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED:
    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE:
    case download::DOWNLOAD_DANGER_TYPE_FORCE_SAVE_TO_GDRIVE:
    case download::DOWNLOAD_DANGER_TYPE_FORCE_SAVE_TO_ONEDRIVE:
    case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK:
    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
    case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
    case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
    case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
    case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY:
    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED:
    case download::DOWNLOAD_DANGER_TYPE_MAX:
      break;
  }
}

void DownloadBubbleRowViewInfo::PopulateForInterrupted(
    offline_items_collection::FailState fail_state) {
  // Only handle danger types that are terminated in the interrupted state in
  // this function. The other danger types are handled in
  // `PopulateForInProgressOrComplete`.
  switch (model_->GetDangerType()) {
    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED: {
      has_subpage_ = true;
      return;
    }
    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE: {
      has_subpage_ = true;
      return;
    }

    case download::DOWNLOAD_DANGER_TYPE_FORCE_SAVE_TO_GDRIVE:
    case download::DOWNLOAD_DANGER_TYPE_FORCE_SAVE_TO_ONEDRIVE:
    case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK: {
#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
      if (enterprise_connectors::ShouldPromptReviewForDownload(
              model_->profile(), model_->GetDownloadItem())) {
        primary_button_command_ = DownloadCommands::Command::REVIEW;
        return;
      }
#endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
      has_subpage_ = true;
      return;
    }
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
    case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
    case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
    case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
    case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
    case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
    case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_LOCAL_PASSWORD_SCANNING:
    case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
    case download::DOWNLOAD_DANGER_TYPE_ASYNC_LOCAL_PASSWORD_SCANNING:
    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_FAILED:
    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE:
    case download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_OPENED_DANGEROUS:
    case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
    case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
    case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
    case download::DOWNLOAD_DANGER_TYPE_ALLOWLISTED_BY_POLICY:
    case download::DOWNLOAD_DANGER_TYPE_BLOCKED_SCAN_FAILED:
    case download::DOWNLOAD_DANGER_TYPE_MAX:
      break;
  }

  switch (fail_state) {
    // TODO(alshawwa): Handle LOCAL_DOWNLOAD_BLOCKED separately
    case FailState::LOCAL_DOWNLOAD_BLOCKED:
    case FailState::FILE_BLOCKED: {
      has_subpage_ = true;
      return;
    }
    case FailState::FILE_NAME_TOO_LONG:
    case FailState::FILE_NO_SPACE:
    case FailState::SERVER_UNAUTHORIZED: {
      has_subpage_ = true;
      return;
    }
    // No Retry in these cases.
    case FailState::FILE_TOO_LARGE:
    case FailState::FILE_VIRUS_INFECTED:
    case FailState::FILE_SECURITY_CHECK_FAILED:
    case FailState::FILE_ACCESS_DENIED:
    case FailState::SERVER_FORBIDDEN:
    case FailState::FILE_SAME_AS_SOURCE:
    case FailState::SERVER_BAD_CONTENT: {
      return;
    }
    // Try resume if possible or retry if not in these cases, and in the default
    // case.
    case FailState::NETWORK_INVALID_REQUEST:
    case FailState::NETWORK_FAILED:
    case FailState::NETWORK_TIMEOUT:
    case FailState::NETWORK_DISCONNECTED:
    case FailState::NETWORK_SERVER_DOWN:
    case FailState::FILE_TRANSIENT_ERROR:
    case FailState::USER_SHUTDOWN:
    case FailState::CRASH:
    case FailState::SERVER_CONTENT_LENGTH_MISMATCH:
    case FailState::SERVER_NO_RANGE:
    case FailState::SERVER_CROSS_ORIGIN_REDIRECT:
    case FailState::FILE_FAILED:
    case FailState::FILE_HASH_MISMATCH:
    case FailState::SERVER_FAILED:
    case FailState::SERVER_CERT_PROBLEM:
    case FailState::SERVER_UNREACHABLE:
    case FailState::FILE_TOO_SHORT:
      break;
    // Not possible because the USER_CANCELED fail state does not allow a call
    // into this function
    case FailState::USER_CANCELED:
    // Deprecated
    case FailState::NETWORK_INSTABILITY:
    case FailState::CANNOT_DOWNLOAD:
      NOTREACHED();
    case FailState::NO_FAILURE:
      return;
  }

  primary_button_command_ = model_->CanResume()
                                ? DownloadCommands::Command::RESUME
                                : DownloadCommands::Command::RETRY;
}

void DownloadBubbleRowViewInfo::PopulateForTailoredWarning(
    TailoredWarningType tailored_warning_type) {
  CHECK(model_->GetDownloadItem());
  switch (tailored_warning_type) {
    case TailoredWarningType::kSuspiciousArchive:
      return PopulateSuspiciousUiPattern();
    case TailoredWarningType::kCookieTheft:
      return PopulateDangerousUiPattern();
    case TailoredWarningType::kNoTailoredWarning: {
      NOTREACHED();
    }
  }
}

void DownloadBubbleRowViewInfo::PopulateForFileTypeWarningNoSafeBrowsing() {
  PopulateSuspiciousUiPattern();
}

void DownloadBubbleRowViewInfo::PopulateSuspiciousUiPattern() {
  has_subpage_ = true;
  secondary_text_color_ = kColorDownloadItemTextWarning;
}

void DownloadBubbleRowViewInfo::PopulateDangerousUiPattern() {
  has_subpage_ = true;
  secondary_text_color_ = kColorDownloadItemTextDangerous;
}

void DownloadBubbleRowViewInfo::Reset() {
  icon_and_color_ = IconAndColor{};
  secondary_text_color_ = std::nullopt;
  quick_actions_.clear();
  main_button_enabled_ = true;
  has_subpage_ = false;
  primary_button_command_ = std::nullopt;
  progress_bar_ = DownloadBubbleProgressBar::NoProgressBar();
}
