#pragma once

#include <filesystem>
#include <frostbite2D/types/type_alias.h>
#include <optional>
#include <string>
#include <vector>

namespace frostbite2D {

namespace fs = std::filesystem;

/**
 * @brief 文件信息结构体
 */
struct FileInfo {
  std::string name;           ///< 文件名（包含扩展名）
  std::string extension;      ///< 文件扩展名（包含点，如 ".txt"）
  std::string fullPath;       ///< 完整路径
  uint64 size = 0;            ///< 文件大小（字节）
  bool isDirectory = false;   ///< 是否为目录
  bool isRegularFile = false; ///< 是否为普通文件
  bool exists = false;        ///< 是否存在
};

/**
 * @brief 资源管理类（单例）
 *
 * 提供文件读写、目录操作、路径处理等功能。
 * 支持设置工作目录，所有相对路径将基于工作目录解析。
 * 支持 UTF-8 编码的中文路径。
 *
 * @example
 * auto& asset = Asset::get();
 * asset.setWorkingDirectory("D:/游戏/资源");
 * std::string content;
 * asset.readTextFile("配置/设置.json", content);
 */
class Asset {
public:
  /**
   * @brief 获取单例实例
   * @return 资源管理器实例引用
   */
  static Asset &get();

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

  // ---------------------------------------------------------------------------
  // 文件读写
  // ---------------------------------------------------------------------------

  /**
   * @brief 读取文本文件
   * @param path 文件路径（相对或绝对路径）
   * @param outContent 输出文件内容
   * @return 读取成功返回 true
   */
  bool readTextFile(const std::string &path, std::string &outContent);

  /**
   * @brief 写入文本文件
   * @param path 文件路径
   * @param content 要写入的内容
   * @param append 是否追加模式，默认覆盖
   * @return 写入成功返回 true
   */
  bool writeTextFile(const std::string &path, const std::string &content,
                     bool append = false);

  /**
   * @brief 读取二进制文件
   * @param path 文件路径
   * @param outData 输出字节数组
   * @return 读取成功返回 true
   */
  bool readBinaryFile(const std::string &path, std::vector<uint8> &outData);

  /**
   * @brief 写入二进制文件
   * @param path 文件路径
   * @param data 要写入的字节数组
   * @param append 是否追加模式，默认覆盖
   * @return 写入成功返回 true
   */
  bool writeBinaryFile(const std::string &path, const std::vector<uint8> &data,
                       bool append = false);

  /**
   * @brief 读取文本文件到字符串（便捷方法）
   * @param path 文件路径
   * @return 文件内容，失败返回 std::nullopt
   */
  std::optional<std::string> readFileToString(const std::string &path);

  /**
   * @brief 读取二进制文件到字节数组（便捷方法）
   * @param path 文件路径
   * @return 字节数组，失败返回 std::nullopt
   */
  std::optional<std::vector<uint8>> readFileToBytes(const std::string &path);

  // ---------------------------------------------------------------------------
  // 文件/目录检查
  // ---------------------------------------------------------------------------

  /**
   * @brief 检查路径是否存在
   * @param path 路径
   * @return 存在返回 true
   */
  bool exists(const std::string &path) const;

  /**
   * @brief 检查路径是否为目录
   * @param path 路径
   * @return 是目录返回 true
   */
  bool isDirectory(const std::string &path) const;

  /**
   * @brief 检查路径是否为普通文件
   * @param path 路径
   * @return 是普通文件返回 true
   */
  bool isRegularFile(const std::string &path) const;

  /**
   * @brief 检查文件或目录是否为空
   * @param path 路径
   * @return 为空返回 true
   */
  bool isEmpty(const std::string &path) const;

  // ---------------------------------------------------------------------------
  // 目录操作
  // ---------------------------------------------------------------------------

  /**
   * @brief 创建单层目录
   * @param path 目录路径
   * @return 创建成功返回 true
   */
  bool createDirectory(const std::string &path);

  /**
   * @brief 递归创建多层目录
   * @param path 目录路径
   * @return 创建成功返回 true
   */
  bool createDirectories(const std::string &path);

  /**
   * @brief 删除文件
   * @param path 文件路径
   * @return 删除成功返回 true
   */
  bool removeFile(const std::string &path);

  /**
   * @brief 删除目录及其所有内容
   * @param path 目录路径
   * @return 删除成功返回 true
   */
  bool removeDirectory(const std::string &path);

  // ---------------------------------------------------------------------------
  // 文件操作
  // ---------------------------------------------------------------------------

  /**
   * @brief 复制文件
   * @param from 源文件路径
   * @param to 目标文件路径
   * @param overwrite 是否覆盖已存在的文件
   * @return 复制成功返回 true
   */
  bool copyFile(const std::string &from, const std::string &to,
                bool overwrite = false);

  /**
   * @brief 移动文件
   * @param from 源文件路径
   * @param to 目标文件路径
   * @return 移动成功返回 true
   */
  bool moveFile(const std::string &from, const std::string &to);

  /**
   * @brief 重命名文件或目录
   * @param oldPath 原路径
   * @param newPath 新路径
   * @return 重命名成功返回 true
   */
  bool rename(const std::string &oldPath, const std::string &newPath);

  // ---------------------------------------------------------------------------
  // 目录遍历
  // ---------------------------------------------------------------------------

  /**
   * @brief 列出目录中的所有文件
   * @param directoryPath 目录路径
   * @param recursive 是否递归遍历子目录
   * @return 文件路径列表
   */
  std::vector<std::string> listFiles(const std::string &directoryPath,
                                     bool recursive = false);

  /**
   * @brief 列出目录中的所有子目录
   * @param directoryPath 目录路径
   * @param recursive 是否递归遍历子目录
   * @return 子目录路径列表
   */
  std::vector<std::string> listDirectories(const std::string &directoryPath,
                                           bool recursive = false);

  /**
   * @brief 列出目录中的所有文件和子目录
   * @param directoryPath 目录路径
   * @param recursive 是否递归遍历子目录
   * @return 所有项目路径列表
   */
  std::vector<std::string> listAll(const std::string &directoryPath,
                                   bool recursive = false);

  /**
   * @brief 按扩展名列出文件
   * @param directoryPath 目录路径
   * @param extension 文件扩展名（如 ".png"）
   * @param recursive 是否递归遍历子目录
   * @return 匹配的文件路径列表
   */
  std::vector<std::string>
  listFilesWithExtension(const std::string &directoryPath,
                         const std::string &extension, bool recursive = false);

  // ---------------------------------------------------------------------------
  // 文件信息
  // ---------------------------------------------------------------------------

  /**
   * @brief 获取文件详细信息
   * @param path 文件路径
   * @return 文件信息结构体
   */
  FileInfo getFileInfo(const std::string &path);

  /**
   * @brief 获取文件大小
   * @param path 文件路径
   * @return 文件大小（字节）
   */
  uint64 getFileSize(const std::string &path) const;

  // ---------------------------------------------------------------------------
  // 路径处理
  // ---------------------------------------------------------------------------

  /**
   * @brief 获取文件名（包含扩展名）
   * @param path 文件路径
   * @return 文件名
   */
  std::string getFileName(const std::string &path) const;

  /**
   * @brief 获取文件名（不含扩展名）
   * @param path 文件路径
   * @return 文件名
   */
  std::string getFileNameWithoutExtension(const std::string &path) const;

  /**
   * @brief 获取文件扩展名
   * @param path 文件路径
   * @return 扩展名（包含点，如 ".txt"）
   */
  std::string getExtension(const std::string &path) const;

  /**
   * @brief 获取父目录路径
   * @param path 文件路径
   * @return 父目录路径
   */
  std::string getParentPath(const std::string &path) const;

  /**
   * @brief 获取绝对路径
   * @param path 相对或绝对路径
   * @return 绝对路径
   */
  std::string getAbsolutePath(const std::string &path) const;

  /**
   * @brief 获取规范路径（解析符号链接和相对路径）
   * @param path 路径
   * @return 规范化路径
   */
  std::string getCanonicalPath(const std::string &path) const;

  /**
   * @brief 合并两个路径
   * @param left 左侧路径
   * @param right 右侧路径
   * @return 合并后的路径
   */
  std::string combinePath(const std::string &left,
                          const std::string &right) const;

  /**
   * @brief 规范化路径（转换为系统首选格式）
   * @param path 路径
   * @return 规范化路径
   */
  std::string normalizePath(const std::string &path) const;

  // ---------------------------------------------------------------------------
  // 工作目录
  // ---------------------------------------------------------------------------

  /**
   * @brief 获取当前工作目录
   * @return 当前工作目录路径
   */
  std::string getCurrentPath() const;

  /**
   * @brief 设置当前工作目录
   * @param path 目录路径
   * @return 设置成功返回 true
   */
  bool setCurrentPath(const std::string &path);

  /**
   * @brief 设置资源工作目录
   *
   * 设置后，所有相对路径操作都将基于此目录。
   *
   * @param path 工作目录路径
   */
  void setWorkingDirectory(const std::string &path);

  /**
   * @brief 获取资源工作目录
   * @return 工作目录路径
   */
  const std::string &getWorkingDirectory() const;

  /**
   * @brief 解析相对路径为完整路径
   * @param relativePath 相对路径
   * @return 完整路径
   */
  std::string resolvePath(const std::string &relativePath) const;

  // ---------------------------------------------------------------------------
  // 资源根目录
  // ---------------------------------------------------------------------------

  /**
   * @brief 设置资源根目录
   * @param root 资源根目录路径
   */
  void setAssetRoot(const std::string &root);

  /**
   * @brief 获取资源根目录
   * @return 资源根目录路径
   */
  const std::string &getAssetRoot() const;

  /**
   * @brief 解析资源相对路径
   *
   * 将相对路径解析为：工作目录/资源根目录/相对路径
   *
   * @param relativePath 相对路径
   * @return 完整路径
   */
  std::string resolveAssetPath(const std::string &relativePath) const;

private:
  Asset() = default;
  ~Asset() = default;

  fs::path toPath(const std::string &path) const;
  std::string fromPath(const fs::path &path) const;
  std::string resolveFullPath(const std::string &path) const;

  std::string workingDirectory_;
  std::string assetRoot_;
};

} // namespace frostbite2D
