// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <flutter/method_call.h>
#include <flutter/method_result_functions.h>
#include <flutter/standard_method_codec.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <windows.h>

#include <memory>
#include <optional>
#include <string>

#include "messages.g.h"
#include "url_launcher_plugin.h"

namespace url_launcher_windows {
namespace test {

namespace {

using flutter::EncodableMap;
using flutter::EncodableValue;
using ::testing::DoAll;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::SetArgPointee;

class MockSystemApis : public SystemApis {
 public:
  MOCK_METHOD(LSTATUS, RegCloseKey, (HKEY key), (override));
  MOCK_METHOD(LSTATUS, RegQueryValueExW,
              (HKEY key, LPCWSTR value_name, LPDWORD type, LPBYTE data,
               LPDWORD data_size),
              (override));
  MOCK_METHOD(LSTATUS, RegOpenKeyExW,
              (HKEY key, LPCWSTR sub_key, DWORD options, REGSAM desired,
               PHKEY result),
              (override));
  MOCK_METHOD(HINSTANCE, ShellExecuteW,
              (HWND hwnd, LPCWSTR operation, LPCWSTR file, LPCWSTR parameters,
               LPCWSTR directory, int show_flags),
              (override));
};

}  // namespace

TEST(UrlLauncherPlugin, CanLaunchSuccessTrue) {
  std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();

  // Return success values from the registery commands.
  HKEY fake_key = reinterpret_cast<HKEY>(1);
  EXPECT_CALL(*system, RegOpenKeyExW)
      .WillOnce(DoAll(SetArgPointee<4>(fake_key), Return(ERROR_SUCCESS)));
  EXPECT_CALL(*system, RegQueryValueExW).WillOnce(Return(ERROR_SUCCESS));
  EXPECT_CALL(*system, RegCloseKey(fake_key)).WillOnce(Return(ERROR_SUCCESS));

  UrlLauncherPlugin plugin(std::move(system));
  ErrorOr<bool> result = plugin.CanLaunchUrl("https://some.url.com");

  ASSERT_FALSE(result.has_error());
  EXPECT_TRUE(result.value());
}

TEST(UrlLauncherPlugin, CanLaunchQueryFailure) {
  std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();

  // Return success values from the registery commands, except for the query,
  // to simulate a scheme that is in the registry, but has no URL handler.
  HKEY fake_key = reinterpret_cast<HKEY>(1);
  EXPECT_CALL(*system, RegOpenKeyExW)
      .WillOnce(DoAll(SetArgPointee<4>(fake_key), Return(ERROR_SUCCESS)));
  EXPECT_CALL(*system, RegQueryValueExW).WillOnce(Return(ERROR_FILE_NOT_FOUND));
  EXPECT_CALL(*system, RegCloseKey(fake_key)).WillOnce(Return(ERROR_SUCCESS));

  UrlLauncherPlugin plugin(std::move(system));
  ErrorOr<bool> result = plugin.CanLaunchUrl("https://some.url.com");

  ASSERT_FALSE(result.has_error());
  EXPECT_FALSE(result.value());
}

TEST(UrlLauncherPlugin, CanLaunchHandlesOpenFailure) {
  std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();

  // Return failure for opening.
  EXPECT_CALL(*system, RegOpenKeyExW).WillOnce(Return(ERROR_BAD_PATHNAME));

  UrlLauncherPlugin plugin(std::move(system));
  ErrorOr<bool> result = plugin.CanLaunchUrl("https://some.url.com");

  ASSERT_FALSE(result.has_error());
  EXPECT_FALSE(result.value());
}

TEST(UrlLauncherPlugin, LaunchReportsSuccess) {
  std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();

  // Return a success value (>32) from launching.
  EXPECT_CALL(*system, ShellExecuteW)
      .WillOnce(Return(reinterpret_cast<HINSTANCE>(33)));

  UrlLauncherPlugin plugin(std::move(system));
  ErrorOr<bool> result = plugin.LaunchUrl("https://some.url.com");

  ASSERT_FALSE(result.has_error());
  EXPECT_TRUE(result.value());
}

TEST(UrlLauncherPlugin, LaunchReportsFailure) {
  std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();

  // Return error 31 from launching, indicating no handler.
  EXPECT_CALL(*system, ShellExecuteW)
      .WillOnce(Return(reinterpret_cast<HINSTANCE>(SE_ERR_NOASSOC)));

  UrlLauncherPlugin plugin(std::move(system));
  ErrorOr<bool> result = plugin.LaunchUrl("https://some.url.com");

  ASSERT_FALSE(result.has_error());
  EXPECT_FALSE(result.value());
}

TEST(UrlLauncherPlugin, LaunchReportsError) {
  std::unique_ptr<MockSystemApis> system = std::make_unique<MockSystemApis>();

  // Return a failure value (<=32) from launching.
  EXPECT_CALL(*system, ShellExecuteW)
      .WillOnce(Return(reinterpret_cast<HINSTANCE>(32)));

  UrlLauncherPlugin plugin(std::move(system));
  ErrorOr<bool> result = plugin.LaunchUrl("https://some.url.com");

  EXPECT_TRUE(result.has_error());
}

}  // namespace test
}  // namespace url_launcher_windows
