#pragma once

#include <concepts>
#include <cstddef>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>

namespace sliger::json {

struct Pos {
    int line;
    int col;
};

struct Err {
    Pos pos;
    std::string msg;
};

template <typename T> class Res {
public:
    Res(T value)
        : maybe_value(std::move(value))
        , maybe_error()
    {
    }
    Res(Err error)
        : maybe_value()
        , maybe_error(std::move(error))
    {
    }

    inline auto ok() const -> bool { return this->maybe_value.has_value(); }

    inline auto val() const& -> const T& { return this->maybe_value.value(); }
    inline auto val() & -> T& { return this->maybe_value.value(); }
    inline auto val() && -> T&& { return std::move(this->maybe_value.value()); }

    inline auto err() const& -> const Err& { return this->maybe_error.value(); }
    inline auto err() & -> Err& { return this->maybe_error.value(); }
    inline auto err() && -> Err&&
    {
        return std::move(this->maybe_error.value());
    }

private:
    std::optional<T> maybe_value;
    std::optional<Err> maybe_error;
};

template <> class Res<void> {
public:
    Res()
        : maybe_error()
    {
    }
    Res(Err error)
        : maybe_error(std::move(error))
    {
    }

    inline auto ok() const -> bool { return not this->maybe_error.has_value(); }

    inline auto err() const& -> const Err& { return this->maybe_error.value(); }
    inline auto err() & -> Err& { return this->maybe_error.value(); }
    inline auto err() && -> Err&&
    {
        return std::move(this->maybe_error.value());
    }

private:
    std::optional<Err> maybe_error;
};

enum class Type {
    Null,
    String,
    Number,
    Bool,
    Array,
    Object,
};

struct Value {
    virtual ~Value() = default;
    virtual auto type() const -> Type = 0;

    template <typename T>
        requires std::derived_from<T, Value>
    inline auto as() & -> T&
    {
        return static_cast<T&>(*this);
    }

    template <typename T>
        requires std::derived_from<T, Value>
    inline auto as() const& -> const T&
    {
        return static_cast<const T&>(*this);
    }
};

struct Null final : public Value {
    auto type() const -> Type override { return Type::Null; }
};

struct String final : public Value {
    String(std::string value)
        : value(std::move(value))
    {
    }
    auto type() const -> Type override { return Type::String; }

    std::string value;
};

struct Number final : public Value {
    Number(double value)
        : value(value)
    {
    }
    auto type() const -> Type override { return Type::Number; }

    double value;
};

struct Bool final : public Value {
    Bool(bool value)
        : value(value)
    {
    }
    auto type() const -> Type override { return Type::Bool; }

    bool value;
};

using ArrayValues = std::vector<std::unique_ptr<Value>>;
struct Array final : public Value {
    Array(ArrayValues values)
        : values(std::move(values))
    {
    }
    auto type() const -> Type override { return Type::Array; }

    ArrayValues values;
};

using ObjectFields = std::unordered_map<std::string, std::unique_ptr<Value>>;
struct Object final : public Value {
    Object(ObjectFields fields)
        : fields(std::move(fields))
    {
    }
    auto type() const -> Type override { return Type::Object; }

    ObjectFields fields;
};

enum class TokTyp {
    Eof,
    String,
    Float,
    False,
    True,
    Null,
    LBrace,
    RBrace,
    LBracket,
    RBracket,
    Comma,
    Colon,
};

auto tok_typ_to_string(TokTyp typ) -> std::string;

struct Tok {
    Tok(TokTyp typ, Pos pos)
        : typ(typ)
        , pos(pos)
        , val_id(0)
    {
    }
    Tok(TokTyp typ, Pos pos, size_t val_id)
        : typ(typ)
        , pos(pos)
        , val_id(val_id)
    {
    }
    Tok(TokTyp typ, Pos pos, double float_val)
        : typ(typ)
        , pos(pos)
        , float_val(float_val)
    {
    }
    Tok(TokTyp typ, Pos pos, bool bool_val)
        : typ(typ)
        , pos(pos)
        , bool_val(bool_val)
    {
    }

    TokTyp typ;
    Pos pos;
    union {
        size_t val_id;
        double float_val;
        bool bool_val;
    };
};

class Lexer {
public:
    Lexer(std::string_view text)
        : text(text)
    {
    }

    auto next() -> Res<Tok>;

    inline auto pos() const -> Pos { return { this->line, this->col }; }

    inline auto val(size_t val_id) const -> const std::string&
    {
        return this->strs_vals.at(val_id);
    }

private:
    inline void step()
    {
        if (this->cur() == '\n') {
            this->col = 1;
            this->line += 1;
        } else {
            this->col += 1;
        }
        this->i += 1;
    }

    inline auto intern_str(std::string val) -> size_t
    {
        if (this->strs_val_id_map.contains(val))
            return strs_val_id_map.at(val);
        auto id = this->strs_vals.size();
        this->strs_vals.push_back(val);
        this->strs_val_id_map.insert_or_assign(val, id);
        return id;
    }

    inline auto test_in(std::string_view chs) const -> bool
    {
        for (auto ch : chs)
            if (test(ch))
                return true;
        return false;
    }
    inline auto test(char ch) const -> bool { return !done() && cur() == ch; }
    inline auto done() const -> bool { return this->i >= this->text.size(); }
    inline auto cur() const -> char { return this->text.at(this->i); }

    std::string_view text;
    size_t i = 0;
    int line = 1;
    int col = 1;
    std::unordered_map<std::string, size_t> strs_val_id_map;
    std::vector<std::string> strs_vals;
};

class Parser {
public:
    Parser(std::string_view text)
        : lexer(text)
        , cur(lexer.next())
    {
    }

    auto parse_val() -> Res<std::unique_ptr<Value>>;

private:
    auto unexpected_tok_err(TokTyp expected, std::string_view msg)
        -> Res<std::unique_ptr<Value>>;

    inline auto step() -> Res<void>
    {
        auto tok = this->lexer.next();
        if (not tok.ok())
            return tok.err();
        this->cur = tok;
        return {};
    }
    inline auto curtyp() const -> TokTyp { return this->cur.val().typ; }

    Lexer lexer;
    Res<Tok> cur;
};

inline auto parse_json(std::string_view value) -> Res<std::unique_ptr<Value>>
{
    return Parser(value).parse_val();
}

class Writer {
public:
    inline auto operator<<(auto value) -> Writer&
    {
        this->add(value);
        return *this;
    }
    inline void add(const char* value) { this->data.append(value); }
    inline void add(std::string_view value) { this->data.append(value); }
    inline void add(const std::string& value) { this->data.append(value); }
    inline void add(auto value) { this->data.push_back(value); }

    template <typename T, typename F>
    auto add_comma_seperated(const T& values, F f)
    {
        auto first = true;
        for (const auto& value : values) {
            if (!first) {
                add(",");
            }
            first = false;
            f(*this, value);
        }
    }

    inline auto done() -> std::string { return std::move(this->data); }

private:
    std::string data;
};

struct ToAndFromJson {
    virtual ~ToAndFromJson() = default;

    virtual void to_json(Writer& writer) const = 0;
};

auto to_json(const std::derived_from<ToAndFromJson> auto& value) -> std::string
{
    auto writer = Writer();
    value.to_json(writer);
    return writer.done();
}

template <typename T>
    requires std::derived_from<T, ToAndFromJson>
auto from_json(std::string_view json_string) -> T
{
    return T::from_json(json_string);
}

}
