#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

#ifdef _WIN32
#include <windows.h>
#endif

namespace {

std::string ReadFile(const std::filesystem::path& path) {
  std::ifstream file(path, std::ios::binary);
  if (!file) {
    return {};
  }

  std::ostringstream stream;
  stream << file.rdbuf();
  return stream.str();
}

std::filesystem::path FindRepoRoot() {
  std::filesystem::path current = std::filesystem::current_path();
  while (!current.empty()) {
    if (std::filesystem::exists(current / "xmake.lua") &&
        std::filesystem::exists(current / "apps/desktop/resources/windows/crossdesk.rc")) {
      return current;
    }
    current = current.parent_path();
  }
  return {};
}

bool ExpectContains(const char* name, const std::string& value,
                    const std::string& expected) {
  if (value.find(expected) != std::string::npos) {
    return true;
  }

  std::cerr << name << " missing expected text: " << expected << "\n";
  return false;
}

bool ExpectNotContains(const char* name, const std::string& value,
                       const std::string& unexpected) {
  if (value.find(unexpected) == std::string::npos) {
    return true;
  }

  std::cerr << name << " contains unexpected text: " << unexpected << "\n";
  return false;
}

#ifdef _WIN32
bool ExpectActivationContext(const std::filesystem::path& manifest_path) {
  ACTCTXW context = {};
  context.cbSize = sizeof(context);
  std::wstring source = manifest_path.wstring();
  context.lpSource = source.c_str();

  HANDLE activation_context = CreateActCtxW(&context);
  if (activation_context == INVALID_HANDLE_VALUE) {
    std::cerr << "CreateActCtxW failed for " << manifest_path.string()
              << ", error=" << GetLastError() << "\n";
    return false;
  }

  ReleaseActCtx(activation_context);
  return true;
}
#endif

}  // namespace

int main() {
  const std::filesystem::path repo_root = FindRepoRoot();
  if (repo_root.empty()) {
    std::cerr << "failed to locate repository root\n";
    return 1;
  }

  const std::string rc = ReadFile(repo_root / "apps/desktop/resources/windows/crossdesk.rc");
  const std::string portable_rc =
      ReadFile(repo_root / "apps/desktop/resources/windows/crossdesk_portable.rc");
  const std::string service_rc =
      ReadFile(repo_root / "apps/desktop/resources/windows/crossdesk_service.rc");
  const std::string session_helper_rc =
      ReadFile(repo_root / "apps/desktop/resources/windows/crossdesk_session_helper.rc");
  const std::string wgc_plugin_rc =
      ReadFile(repo_root / "apps/desktop/resources/windows/wgc_plugin.rc");
  const std::string version_info =
      ReadFile(repo_root / "apps/desktop/resources/windows/version_info.rcinc");
  const std::string manifest =
      ReadFile(repo_root / "apps/desktop/resources/windows/crossdesk.manifest");
  const std::string debug_manifest =
      ReadFile(repo_root / "apps/desktop/resources/windows/crossdesk_debug.manifest");
  const std::string portable_manifest =
      ReadFile(repo_root / "apps/desktop/resources/windows/crossdesk_portable.manifest");
  const std::string targets = ReadFile(repo_root / "apps/desktop/xmake/targets.lua");

  bool ok = true;
  ok &= ExpectContains("crossdesk.rc", rc, "crossdesk.manifest");
  ok &= ExpectContains("crossdesk.rc", rc, "crossdesk_debug.manifest");
  ok &= ExpectContains("crossdesk.rc", rc, "CROSSDESK_DEBUG");
  ok &= ExpectContains("crossdesk.rc", rc, "RT_MANIFEST");
  ok &= ExpectContains("crossdesk_portable.rc", portable_rc,
                       "crossdesk_portable.manifest");
  ok &= ExpectContains("crossdesk_portable.rc", portable_rc, "RT_MANIFEST");
  ok &= ExpectContains("crossdesk.rc", rc, "version_info.rcinc");
  ok &= ExpectContains("crossdesk_portable.rc", portable_rc,
                       "version_info.rcinc");
  ok &= ExpectContains("crossdesk_service.rc", service_rc,
                       "crossdesk_service.exe");
  ok &= ExpectContains("crossdesk_session_helper.rc", session_helper_rc,
                       "crossdesk_session_helper.exe");
  ok &= ExpectContains("crossdesk_session_helper.rc", session_helper_rc,
                       "CROSSDESK_PORTABLE");
  ok &= ExpectContains("crossdesk_session_helper.rc", session_helper_rc,
                       "crossdesk_portable.manifest");
  ok &= ExpectContains("wgc_plugin.rc", wgc_plugin_rc, "wgc_plugin.dll");
  ok &= ExpectContains("version_info.rcinc", version_info, "ProductName");
  ok &= ExpectContains("version_info.rcinc", version_info,
                       "ProductVersion");
  ok &= ExpectContains("version_info.rcinc", version_info,
                       "OriginalFilename");
  ok &= ExpectContains("apps/desktop/xmake/targets.lua", targets,
                       "apps/desktop/resources/windows/crossdesk_portable.rc");
  ok &= ExpectContains("apps/desktop/xmake/targets.lua", targets,
                       "apps/desktop/resources/windows/crossdesk_service.rc");
  ok &= ExpectContains("apps/desktop/xmake/targets.lua", targets,
                       "apps/desktop/resources/windows/crossdesk_session_helper.rc");
  ok &= ExpectContains("apps/desktop/xmake/targets.lua", targets,
                       "apps/desktop/resources/windows/wgc_plugin.rc");
  ok &= ExpectContains("apps/desktop/xmake/targets.lua", targets, "CROSSDESK_PORTABLE");
  ok &= ExpectContains("crossdesk.manifest", manifest,
                       "level=\"requireAdministrator\"");
  ok &= ExpectContains("crossdesk.manifest", manifest,
                       "http://schemas.microsoft.com/SMI/2005/WindowsSettings");
  ok &= ExpectContains("crossdesk.manifest", manifest,
                       "http://schemas.microsoft.com/SMI/2016/WindowsSettings");
  ok &= ExpectNotContains("crossdesk.manifest", manifest,
                          "processorArchitecture=\"*\"");
  ok &= ExpectContains("crossdesk_debug.manifest", debug_manifest,
                       "level=\"asInvoker\"");
  ok &= ExpectContains("crossdesk_debug.manifest", debug_manifest,
                       "http://schemas.microsoft.com/SMI/2005/WindowsSettings");
  ok &= ExpectContains("crossdesk_debug.manifest", debug_manifest,
                       "http://schemas.microsoft.com/SMI/2016/WindowsSettings");
  ok &= ExpectNotContains("crossdesk_debug.manifest", debug_manifest,
                          "processorArchitecture=\"*\"");
  ok &= ExpectContains("crossdesk_portable.manifest", portable_manifest,
                       "level=\"asInvoker\"");
  ok &= ExpectNotContains("crossdesk_portable.manifest", portable_manifest,
                          "level=\"requireAdministrator\"");
  ok &= ExpectContains("crossdesk_portable.manifest", portable_manifest,
                       "http://schemas.microsoft.com/SMI/2005/WindowsSettings");
  ok &= ExpectContains("crossdesk_portable.manifest", portable_manifest,
                       "http://schemas.microsoft.com/SMI/2016/WindowsSettings");
  ok &= ExpectNotContains("crossdesk_portable.manifest", portable_manifest,
                          "processorArchitecture=\"*\"");
#ifdef _WIN32
  ok &=
      ExpectActivationContext(repo_root / "apps/desktop/resources/windows/crossdesk.manifest");
  ok &= ExpectActivationContext(repo_root /
                                "apps/desktop/resources/windows/crossdesk_debug.manifest");
  ok &= ExpectActivationContext(repo_root /
                                "apps/desktop/resources/windows/crossdesk_portable.manifest");
#endif
  return ok ? 0 : 1;
}
