#ifndef VERIFY_CLIENT_HPP
#define VERIFY_CLIENT_HPP

#include <string>
#include <map>
#include <functional>
#include <memory>
#include <vector>

#ifdef USE_OPENSSL
#include <openssl/evp.h>
#include <openssl/rand.h>
#endif

#ifdef USE_SYSTEM_CURL
#include <curl/curl.h>
#else
// 简化的CURL模拟 - 用于测试
#endif

#include "json.hpp"

namespace verify {

using json = nlohmann::json;

class CryptoException : public std::exception {
public:
    explicit CryptoException(const std::string& msg) : message_(msg) {}
    const char* what() const noexcept override { return message_.c_str(); }
private:
    std::string message_;
};

class ApiException : public std::exception {
public:
    ApiException(int code, const std::string& msg) : code_(code), message_(msg) {}
    int code() const { return code_; }
    const char* what() const noexcept override { return message_.c_str(); }
private:
    int code_;
    std::string message_;
};

enum class EncryptType {
    None,
    AES,
    RC4
};

class Crypto {
public:
    Crypto(EncryptType type, const std::string& key);
    ~Crypto();

    std::string encrypt(const std::string& plaintext);
    std::string decrypt(const std::string& ciphertext);

private:
    EncryptType type_;
    std::string key_;

    std::string encryptAES(const std::string& plaintext);
    std::string decryptAES(const std::string& ciphertext);
    std::string encryptRC4(const std::string& plaintext);
    std::string decryptRC4(const std::string& ciphertext);
    std::vector<uint8_t> padKey(const std::vector<uint8_t>& key, size_t targetLen);
};

class HttpClient {
public:
    HttpClient();
    ~HttpClient();

    void setBaseUrl(const std::string& url);
    void setTimeout(int seconds);
    void addHeader(const std::string& key, const std::string& value);
    void clearHeaders();

    json get(const std::string& path);
    json post(const std::string& path, const json& body);
    json postForm(const std::string& path, const std::map<std::string, std::string>& fields, const std::map<std::string, std::string>& files = {});

    std::vector<uint8_t> download(const std::string& path);

    void setCrypto(std::shared_ptr<Crypto> crypto);

private:
    std::string baseUrl_;
    int timeout_ = 30;
    std::map<std::string, std::string> headers_;
    std::shared_ptr<Crypto> crypto_;

    static size_t writeCallback(void* contents, size_t size, size_t nmemb, std::string* userp);
    static size_t writeBinaryCallback(void* contents, size_t size, size_t nmemb, std::vector<uint8_t>* userp);

    std::string buildUrl(const std::string& path);
    json processResponse(const std::string& response);
};

struct AppConfig {
    uint64_t id;
    std::string name;
    std::string description;
    std::string iconUrl;
    std::string status;
    std::string billingType;
    std::string loginPolicy;
    int maxDevices;
    std::string multiOpenMode;
    int maxInstances;
    bool enableTrial;
    double trialBalance;
    int heartbeatInterval;
    int heartbeatTimeout;
};

struct UserInfo {
    uint64_t userId;
    std::string username;
    double balance;
    std::string status;
};

struct DeviceInfo {
    uint64_t id;
    std::string deviceId;
    std::string deviceName;
    std::string deviceType;
    std::string status;
    int onlineSessions;
    std::string createdAt;
};

struct VersionInfo {
    bool hasUpdate;
    std::string latestVersion;
    std::string downloadUrl;
    int64_t fileSize;
    std::string fileHash;
    std::string entryFile;
    std::string updateNotes;
    std::string updateStrategy;
    std::string updateType;
    std::string updateMethod;
};

class Client {
public:
    Client(const std::string& baseUrl, const std::string& appKey);
    ~Client();

    void setToken(const std::string& token);
    void setEncryptType(EncryptType type, const std::string& key);
    void setTimeout(int seconds);

    // Application info
    AppConfig getAppInfo();
    VersionInfo checkUpdate(const std::string& version = "");
    std::vector<json> getAnnouncements();

    // User authentication
    uint64_t registerUser(const std::string& username,
                          const std::string& password,
                          const std::string& deviceId,
                          const std::string& deviceName = "",
                          const std::string& deviceType = "windows",
                          const std::string& instanceId = "",
                          const std::string& email = "",
                          const std::string& emailCode = "");

    json login(const std::string& username,
               const std::string& password,
               const std::string& deviceId,
               const std::string& deviceName = "",
               const std::string& deviceType = "windows",
               const std::string& instanceId = "");

    void sendEmailCode(const std::string& email, const std::string& purpose = "register");
    void resetPassword(const std::string& email, const std::string& code, const std::string& newPassword);
    void changePassword(const std::string& username, const std::string& oldPassword, const std::string& newPassword);

    // Account
    UserInfo getAccount(uint64_t userId);
    json heartbeat(uint64_t userId, const std::string& deviceId, const std::string& instanceId = "");

    // Recharge
    json recharge(const std::string& username, const std::string& cardKey, const std::string& deviceId);
    json trial(uint64_t userId);

    // Device management
    std::vector<DeviceInfo> getDevices();
    json getDeviceCount(uint64_t userId);
    void unbindDevice(uint64_t userId, const std::string& deviceId);
    void unbindDeviceWithAuth(const std::string& username, const std::string& password, const std::string& deviceId);
    std::vector<json> getInstances(uint64_t userId);
    void forceOfflineInstance(uint64_t userId, const std::string& instanceId);

    // Cloud data
    json getConstants();
    json getConstant(const std::string& key);
    std::vector<uint8_t> downloadConstant(const std::string& key);

    json getVariables();
    json getVariable(const std::string& key);
    std::vector<uint8_t> downloadVariable(const std::string& key);
    json uploadVariable(const std::string& key, const std::string& filePath);
    void updateVariables(const std::map<std::string, std::string>& variables);

    json createVariableRecord(const std::string& key, const json& data);
    json getVariableRecords(const std::string& key, int page = 1, int pageSize = 20);
    void deleteVariableRecord(const std::string& key, uint64_t recordId);

    // Dynamic code
    json executeDynamicCode(const std::string& key, const json& params, uint64_t targetUserId = 0);

private:
    std::string baseUrl_;
    std::string appKey_;
    std::string token_;
    std::shared_ptr<HttpClient> http_;
    std::shared_ptr<Crypto> crypto_;
    EncryptType encryptType_ = EncryptType::None;

    std::string buildPath(const std::string& endpoint);
};

} // namespace verify

#endif // VERIFY_CLIENT_HPP
