// 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.

#import "ios/web/download/web_state_content_download_task.h"

#import "base/functional/bind.h"
#import "base/functional/callback.h"
#import "base/functional/callback_helpers.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/web/public/web_state.h"
#import "net/base/net_errors.h"

typedef void (^ProceduralBlockWithError)(NSError*);

@interface WebStateDownloadDelegateBridge
    : NSObject <CRWWebViewDownloadDelegate>
@end

@implementation WebStateDownloadDelegateBridge {
  base::OnceCallback<void(NSError*)> _callback;
}

- (instancetype)initWithCallback:(base::OnceCallback<void(NSError*)>)callback {
  self = [super init];
  if (self) {
    _callback = std::move(callback);
  }
  return self;
}
- (void)downloadDidFinish {
  std::move(_callback).Run(nil);
}

- (void)downloadDidFailWithError:(NSError*)error {
  std::move(_callback).Run(error);
}

@end

namespace web {
WebStateContentDownloadTask::WebStateContentDownloadTask(
    WebState* web_state,
    const GURL& original_url,
    NSString* originating_host,
    NSString* http_method,
    const std::string& content_disposition,
    int64_t total_bytes,
    const std::string& mime_type,
    NSString* identifier,
    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
    : DownloadTaskImpl(web_state,
                       original_url,
                       originating_host,
                       http_method,
                       content_disposition,
                       total_bytes,
                       mime_type,
                       identifier,
                       task_runner) {}

WebStateContentDownloadTask::~WebStateContentDownloadTask() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!IsDone()) {
    Cancel();
  }
}

void WebStateContentDownloadTask::StartInternal(const base::FilePath& path) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  auto callback =
      base::BindOnce(&WebStateContentDownloadTask::DownloadDidFinish,
                     weak_factory_.GetWeakPtr());
  download_delegate_ = [[WebStateDownloadDelegateBridge alloc]
      initWithCallback:std::move(callback)];
  web_state_->DownloadCurrentPage(
      base::SysUTF8ToNSString(path.value()), download_delegate_,
      base::CallbackToBlock(
          base::BindOnce(&WebStateContentDownloadTask::DownloadWasCreated,
                         weak_factory_.GetWeakPtr())));
}

void WebStateContentDownloadTask::CancelInternal() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  weak_factory_.InvalidateWeakPtrs();
  // Client will receive status update through DownloadTask status update.
  [download_ cancelDownload:^{
  }];
}

#pragma mark - Private

void WebStateContentDownloadTask::DownloadWasCreated(
    id<CRWWebViewDownload> download) {
  download_ = download;
  // Task startup is complete, trigger observers.
  OnDownloadUpdated();
}

void WebStateContentDownloadTask::DownloadDidFinish(NSError* error) {
  if (!error) {
    percent_complete_ = 100;
    received_bytes_ = total_bytes_;
    OnDownloadFinished(DownloadResult(net::OK));
  } else {
    OnDownloadFinished(DownloadResult(net::ERR_FAILED));
  }
}

}  // namespace web
