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

#include "services/network/file_opener_for_upload.h"

#include "base/functional/callback_helpers.h"
#include "base/task/thread_pool.h"
#include "net/base/net_errors.h"
#include "services/network/public/mojom/network_context_client.mojom.h"

namespace network {

namespace {

// `opened_files` need to be closed on a blocking task runner, so move the
// `opened_files` vector onto a sequence that can block so it gets destroyed
// there.
void PostCloseFiles(std::vector<base::File> opened_files) {
  base::ThreadPool::PostTask(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
      base::DoNothingWithBoundArgs(std::move(opened_files)));
}

}  // namespace

FileOpenerForUpload::FileOpenerForUpload(
    std::vector<base::FilePath> paths,
    const GURL& url,
    int32_t process_id,
    mojom::NetworkContextClient* const network_context_client,
    SetUpUploadCallback set_up_upload_callback)
    : paths_(std::move(paths)),
      url_(url),
      process_id_(process_id),
      network_context_client_(network_context_client),
      set_up_upload_callback_(std::move(set_up_upload_callback)) {}

FileOpenerForUpload::~FileOpenerForUpload() {
  if (!opened_files_.empty()) {
    PostCloseFiles(std::move(opened_files_));
  }
}

void FileOpenerForUpload::Start() {
  opened_files_.reserve(paths_.size());
  StartOpeningNextBatch();
}

// static
void FileOpenerForUpload::OnFilesForUploadOpened(
    base::WeakPtr<FileOpenerForUpload> file_opener,
    size_t num_files_requested,
    int error_code,
    std::vector<base::File> opened_files) {
  if (!file_opener) {
    PostCloseFiles(std::move(opened_files));
    return;
  }

  if (error_code == net::OK && num_files_requested != opened_files.size()) {
    error_code = net::ERR_FAILED;
  }

  if (error_code != net::OK) {
    PostCloseFiles(std::move(opened_files));
    file_opener->FilesForUploadOpenedDone(error_code);
    return;
  }
  file_opener->opened_files_.insert(
      file_opener->opened_files_.end(),
      std::make_move_iterator(opened_files.begin()),
      std::make_move_iterator(opened_files.end()));

  if (file_opener->opened_files_.size() < file_opener->paths_.size()) {
    file_opener->StartOpeningNextBatch();
    return;
  }

  file_opener->FilesForUploadOpenedDone(net::OK);
}

void FileOpenerForUpload::StartOpeningNextBatch() {
  size_t num_files_to_request = std::min(paths_.size() - opened_files_.size(),
                                         kMaxFileUploadRequestsPerBatch);
  std::vector<base::FilePath> batch_paths(
      paths_.begin() + opened_files_.size(),
      paths_.begin() + opened_files_.size() + num_files_to_request);

  // TODO(crbug.com/379869738) Remove FromUnsafeValue.
  network_context_client_->OnFileUploadRequested(
      OriginatingProcessId::FromUnsafeValue(process_id_), /*async=*/true,
      batch_paths, url_.get(),
      base::BindOnce(&FileOpenerForUpload::OnFilesForUploadOpened,
                     weak_ptr_factory_.GetWeakPtr(), num_files_to_request));
}

void FileOpenerForUpload::FilesForUploadOpenedDone(int error_code) {
  if (error_code == net::OK) {
    std::move(set_up_upload_callback_).Run(std::move(opened_files_));
  } else {
    std::move(set_up_upload_callback_)
        .Run(base::unexpected(static_cast<net::Error>(error_code)));
  }
}

}  // namespace network
