// Copyright 2026 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/webapps/browser/launch_queue/launch_queue.h"

#include <memory>
#include <utility>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "components/webapps/browser/launch_queue/launch_params.h"
#include "components/webapps/browser/launch_queue/launch_queue_delegate.h"
#include "content/public/browser/file_system_access_permission_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "net/base/net_errors.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_directory_handle.mojom.h"
#include "third_party/blink/public/mojom/web_launch/web_launch.mojom.h"

namespace webapps {

class MockLaunchQueueDelegate : public LaunchQueueDelegate {
 public:
  MOCK_METHOD(bool,
              IsInScope,
              (const LaunchParams& launch_params, const GURL& current_url),
              (const, override));
  MOCK_METHOD(content::PathInfo,
              GetPathInfo,
              (const base::FilePath& entry_path),
              (const, override));
  MOCK_METHOD(bool,
              IsValidLaunchParams,
              (const LaunchParams& params),
              (const, override));
};

class FakeWebLaunchService : public blink::mojom::WebLaunchService {
 public:
  FakeWebLaunchService() = default;
  ~FakeWebLaunchService() override = default;

  void Bind(mojo::ScopedInterfaceEndpointHandle handle) {
    receiver_.reset();
    receiver_.Bind(
        mojo::PendingAssociatedReceiver<blink::mojom::WebLaunchService>(
            std::move(handle)));
  }

  // blink::mojom::WebLaunchService:
  void EnqueueLaunchParams(
      const GURL& launch_url,
      base::TimeTicks time_navigation_started_in_browser,
      bool navigation_started,
      std::vector<blink::mojom::FileSystemAccessEntryPtr> files) override {
    launched_url_ = launch_url;
    enqueue_called_ = true;
    files_ = std::move(files);
  }

  bool enqueue_called() const { return enqueue_called_; }
  const GURL& launched_url() const { return launched_url_; }
  const std::vector<blink::mojom::FileSystemAccessEntryPtr>& files() const {
    return files_;
  }

  void Reset() {
    enqueue_called_ = false;
    launched_url_ = GURL();
    files_.clear();
  }

 private:
  mojo::AssociatedReceiver<blink::mojom::WebLaunchService> receiver_{this};
  bool enqueue_called_ = false;
  GURL launched_url_;
  std::vector<blink::mojom::FileSystemAccessEntryPtr> files_;
};

class LaunchQueueTest : public content::RenderViewHostTestHarness {
 public:
  void SetUp() override {
    content::RenderViewHostTestHarness::SetUp();

    auto delegate =
        std::make_unique<testing::NiceMock<MockLaunchQueueDelegate>>();
    delegate_ = delegate.get();

    ON_CALL(*delegate_, IsValidLaunchParams)
        .WillByDefault(testing::Return(true));
    ON_CALL(*delegate_, IsInScope)
        .WillByDefault(
            [](const LaunchParams& params, const GURL& url) { return true; });

    launch_queue_ =
        std::make_unique<LaunchQueue>(web_contents(), std::move(delegate));

    InitTestApi(web_contents()->GetPrimaryMainFrame());
  }

  void TearDown() override {
    delegate_ = nullptr;
    launch_queue_.reset();
    content::RenderViewHostTestHarness::TearDown();
  }

  void InitTestApi(content::RenderFrameHost* rfh) {
    rfh->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting(
        blink::mojom::WebLaunchService::Name_,
        base::BindRepeating(&FakeWebLaunchService::Bind,
                            base::Unretained(&fake_launch_service_)));
  }

 protected:
  LaunchParams CreateLaunchParams(const GURL& target_url,
                                  bool started_new_navigation = true) {
    LaunchParams params;
    params.set_target_url(target_url);
    params.set_started_new_navigation(started_new_navigation);
    params.set_app_id("test_app_id");
    return params;
  }

  std::unique_ptr<LaunchQueue> launch_queue_;
  raw_ptr<MockLaunchQueueDelegate> delegate_;
  FakeWebLaunchService fake_launch_service_;
};

TEST_F(LaunchQueueTest, EnqueueImmediatelyDispatches) {
  GURL launch_url("https://example.com/launch");
  LaunchParams params =
      CreateLaunchParams(launch_url, /*started_new_navigation=*/false);

  launch_queue_->Enqueue(std::move(params));
  launch_queue_->FlushForTesting();

  EXPECT_TRUE(fake_launch_service_.enqueue_called());
  EXPECT_EQ(fake_launch_service_.launched_url(), launch_url);
}

TEST_F(LaunchQueueTest, EnqueueInvalidParams) {
  GURL launch_url("https://example.com/launch");
  LaunchParams params = CreateLaunchParams(launch_url);
  params.set_paths({base::FilePath(FILE_PATH_LITERAL("sensitive_file.txt"))});

  EXPECT_CALL(*delegate_, IsValidLaunchParams(testing::_))
      .WillOnce(testing::Return(false));

  launch_queue_->Enqueue(std::move(params));
  content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
                                                             launch_url);

  launch_queue_->FlushForTesting();

  EXPECT_TRUE(fake_launch_service_.enqueue_called());
  EXPECT_TRUE(fake_launch_service_.files().empty());
}

}  // namespace webapps
