#pragma once

#include <flutter/encodable_value.h>
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <windows.h>

#include <atomic>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <vector>

namespace coreframe_flutter_windows {

struct WindowsNativeFileSelection {
  std::wstring source_path;
  std::wstring name;
};

struct WindowsStagedNativeFile {
  std::string name;
  int64_t size;
  std::string staged_path;
  std::optional<int64_t> last_modified_milliseconds;
  std::optional<std::string> media_type;

  flutter::EncodableValue ToChannelValue() const;
};

enum class WindowsNativeFileStagingFailure {
  kCapacityExceeded,
  kUnreadable,
};

struct WindowsNativeFileStagingResult {
  std::vector<WindowsStagedNativeFile> files;
  std::optional<WindowsNativeFileStagingFailure> failure;
};

// Returns the closest useful extension patterns the Windows dialog can express.
std::vector<std::wstring>
WindowsPatternsForAccept(const std::vector<std::string> &accept);

// Copies picker selections into the user-private Coreframe staging directory.
// This type does no UI work and is kept separate so staging can be tested and
// run entirely off the platform thread.
class WindowsNativeFileStager {
public:
  static constexpr size_t kMaximumFiles = 32;
  static constexpr int64_t kMaximumFileBytes = 128LL * 1024 * 1024;
  static constexpr int64_t kMaximumTotalBytes = 256LL * 1024 * 1024;

  explicit WindowsNativeFileStager(
      std::wstring staging_directory, size_t maximum_files = kMaximumFiles,
      int64_t maximum_file_bytes = kMaximumFileBytes,
      int64_t maximum_total_bytes = kMaximumTotalBytes);

  WindowsNativeFileStagingResult
  Stage(const std::vector<WindowsNativeFileSelection> &selections) const;

  static void Remove(const std::vector<WindowsStagedNativeFile> &files);
  static void SweepStale(const std::wstring &staging_directory);

private:
  std::wstring staging_directory_;
  size_t maximum_files_;
  int64_t maximum_file_bytes_;
  int64_t maximum_total_bytes_;
};

class WindowsNativeFiles {
public:
  explicit WindowsNativeFiles(flutter::PluginRegistrarWindows *registrar);
  ~WindowsNativeFiles();

  WindowsNativeFiles(const WindowsNativeFiles &) = delete;
  WindowsNativeFiles &operator=(const WindowsNativeFiles &) = delete;

private:
  struct PendingPick;
  struct Completion;

  void HandleMethodCall(
      const flutter::MethodCall<flutter::EncodableValue> &call,
      std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
  void
  Pick(const flutter::EncodableValue *arguments,
       std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
  void CompletePending();
  std::optional<LRESULT> HandleWindowMessage(HWND hwnd, UINT message,
                                             WPARAM wparam, LPARAM lparam);

  flutter::PluginRegistrarWindows *registrar_;
  HWND window_ = nullptr;
  UINT completion_message_ = 0;
  int window_proc_delegate_id_ = -1;
  std::unique_ptr<flutter::MethodChannel<flutter::EncodableValue>> channel_;
  std::unique_ptr<PendingPick> pending_;
  std::wstring staging_directory_;
  std::wstring resource_storage_root_;
  std::vector<std::thread> workers_;
  std::mutex completion_mutex_;
  std::unique_ptr<Completion> completion_;
  std::atomic<bool> attached_{true};
};

} // namespace coreframe_flutter_windows
