#pragma once
#include <drogon/exports.h>
#include <trantor/utils/Date.h>
#include <trantor/utils/Logger.h>
#include <drogon/utils/Utilities.h>
#include <cctype>
#include <string>
#include <limits>
#include <optional>
#include <string_view>
namespace drogon
{
class DROGON_EXPORT Cookie
{
  public:
    Cookie(std::string key, std::string value)
        : key_(std::move(key)), value_(std::move(value))
    {
    }
    Cookie() = default;
    enum class SameSite
    {
        kNull,
        kLax,
        kStrict,
        kNone
    };
    void setExpiresDate(const trantor::Date &date)
    {
        expiresDate_ = date;
    }
    void setHttpOnly(bool only)
    {
        httpOnly_ = only;
    }
    void setSecure(bool secure)
    {
        secure_ = secure;
    }
    void setDomain(const std::string &domain)
    {
        domain_ = domain;
    }
    void setDomain(std::string &&domain)
    {
        domain_ = std::move(domain);
    }
    void setPath(const std::string &path)
    {
        path_ = path;
    }
    void setPath(std::string &&path)
    {
        path_ = std::move(path);
    }
    void setKey(const std::string &key)
    {
        key_ = key;
    }
    void setKey(std::string &&key)
    {
        key_ = std::move(key);
    }
    void setValue(const std::string &value)
    {
        value_ = value;
    }
    void setValue(std::string &&value)
    {
        value_ = std::move(value);
    }
    void setMaxAge(int value)
    {
        maxAge_ = value;
    }
    void setSameSite(SameSite sameSite)
    {
        sameSite_ = sameSite;
    }
    void setPartitioned(bool partitioned)
    {
        partitioned_ = partitioned;
        if (partitioned)
        {
            setSecure(true);
        }
    }
    std::string cookieString() const;
    std::string getCookieString() const
    {
        return cookieString();
    }
    const trantor::Date &expiresDate() const
    {
        return expiresDate_;
    }
    const trantor::Date &getExpiresDate() const
    {
        return expiresDate_;
    }
    const std::string &domain() const
    {
        return domain_;
    }
    const std::string &getDomain() const
    {
        return domain_;
    }
    const std::string &path() const
    {
        return path_;
    }
    const std::string &getPath() const
    {
        return path_;
    }
    const std::string &key() const
    {
        return key_;
    }
    const std::string &getKey() const
    {
        return key_;
    }
    const std::string &value() const
    {
        return value_;
    }
    const std::string &getValue() const
    {
        return value_;
    }
    operator bool() const
    {
        return (!key_.empty()) && (!value_.empty());
    }
    bool isHttpOnly() const
    {
        return httpOnly_;
    }
    bool isSecure() const
    {
        return secure_;
    }
    bool isPartitioned() const
    {
        return partitioned_;
    }
    std::optional<int> maxAge() const
    {
        return maxAge_;
    }
    std::optional<int> getMaxAge() const
    {
        return maxAge_;
    }
    SameSite sameSite() const
    {
        return sameSite_;
    }
    SameSite getSameSite() const
    {
        return sameSite_;
    }
    static bool stricmp(const std::string_view str1,
                        const std::string_view str2)
    {
        auto str1Len{str1.length()};
        auto str2Len{str2.length()};
        if (str1Len != str2Len)
            return false;
        for (size_t idx{0}; idx < str1Len; ++idx)
        {
            auto lowerChar{tolower(str1[idx])};
            if (lowerChar != str2[idx])
            {
                return false;
            }
        }
        return true;
    }
    static SameSite convertString2SameSite(std::string_view sameSite)
    {
        if (stricmp(sameSite, "lax"))
            return Cookie::SameSite::kLax;
        if (stricmp(sameSite, "strict"))
            return Cookie::SameSite::kStrict;
        if (stricmp(sameSite, "none"))
            return Cookie::SameSite::kNone;
        if (!stricmp(sameSite, "null"))
        {
            LOG_WARN
                << "'" << sameSite
                << "' is not a valid SameSite policy. 'Null', 'Lax', 'Strict' "
                   "or "
                   "'None' are proper values. Return value is SameSite::kNull.";
        }
        return Cookie::SameSite::kNull;
    }
    static std::string_view convertSameSite2String(SameSite sameSite)
    {
        switch (sameSite)
        {
            case SameSite::kLax:
                return "Lax";
            case SameSite::kStrict:
                return "Strict";
            case SameSite::kNone:
                return "None";
            case SameSite::kNull:
                return "Null";
            default:
                return "UNDEFINED";
        }
    }
  private:
    trantor::Date expiresDate_{(std::numeric_limits<int64_t>::max)()};
    bool httpOnly_{true};
    bool secure_{false};
    bool partitioned_{false};
    std::string domain_;
    std::string path_;
    std::string key_;
    std::string value_;
    std::optional<int> maxAge_;
    SameSite sameSite_{SameSite::kNull};
};
}  