#pragma once
#include "duckdb/common/arena_linked_list.hpp"
#include "duckdb/common/exception.hpp"
#include "duckdb/common/optional_ptr.hpp"
#include "duckdb/common/string.hpp"
#include "duckdb/common/types/string_type.hpp"
#include "duckdb/parser/expression/cast_expression.hpp"
#include "duckdb/parser/expression/constant_expression.hpp"
#include "duckdb/parser/parsed_expression.hpp"
#include "parser/special_string_utils.hpp"

namespace duckdb {

class PEGTransformer; // Forward declaration

enum class ParseResultType : uint8_t {
	LIST,
	OPTIONAL,
	REPEAT,
	CHOICE,
	EXPRESSION,
	IDENTIFIER,
	KEYWORD,
	OPERATOR,
	STATEMENT,
	EXTENSION,
	NUMBER,
	STRING,
	INVALID
};

inline const char *ParseResultToString(ParseResultType type) {
	switch (type) {
	case ParseResultType::LIST:
		return "LIST";
	case ParseResultType::OPTIONAL:
		return "OPTIONAL";
	case ParseResultType::REPEAT:
		return "REPEAT";
	case ParseResultType::CHOICE:
		return "CHOICE";
	case ParseResultType::EXPRESSION:
		return "EXPRESSION";
	case ParseResultType::IDENTIFIER:
		return "IDENTIFIER";
	case ParseResultType::KEYWORD:
		return "KEYWORD";
	case ParseResultType::OPERATOR:
		return "OPERATOR";
	case ParseResultType::STATEMENT:
		return "STATEMENT";
	case ParseResultType::EXTENSION:
		return "EXTENSION";
	case ParseResultType::NUMBER:
		return "NUMBER";
	case ParseResultType::STRING:
		return "STRING";
	case ParseResultType::INVALID:
		return "INVALID";
	}
	return "INVALID";
}

class ParseResult {
public:
	explicit ParseResult(ParseResultType type, optional_idx offset) : type(type), offset(offset) {
	}
	virtual ~ParseResult() = default;

	template <class TARGET>
	TARGET &Cast() {
		if (TARGET::TYPE != ParseResultType::INVALID && type != TARGET::TYPE) {
			throw InternalException("Failed to cast parse result of type %s to type %s for rule %s",
			                        ParseResultToString(TARGET::TYPE), ParseResultToString(type), name);
		}
		return reinterpret_cast<TARGET &>(*this);
	}

	ParseResultType type;
	string name;
	optional_idx offset;

	virtual void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                              const std::string &indent, bool is_last) const {
		ss << indent << (is_last ? "└─" : "├─") << " " << ParseResultToString(type);
		if (!name.empty()) {
			ss << " (" << name << ")";
		}
	}

	// The public entry point
	std::string ToString() const {
		std::stringstream ss;
		std::unordered_set<const ParseResult *> visited;
		// The root is always the "last" element at its level
		ToStringInternal(ss, visited, "", true);
		return ss.str();
	}
};

struct IdentifierParseResult : ParseResult {
	static constexpr ParseResultType TYPE = ParseResultType::IDENTIFIER;
	string identifier;

	explicit IdentifierParseResult(string identifier_p, optional_idx offset)
	    : ParseResult(TYPE, offset), identifier(std::move(identifier_p)) {
	}

	void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                      const std::string &indent, bool is_last) const override {
		ParseResult::ToStringInternal(ss, visited, indent, is_last);
		ss << ": " << identifier << "\n";
	}
};

struct KeywordParseResult : ParseResult {
	static constexpr ParseResultType TYPE = ParseResultType::KEYWORD;
	string keyword;

	explicit KeywordParseResult(string keyword_p, optional_idx offset)
	    : ParseResult(TYPE, offset), keyword(std::move(keyword_p)) {
	}

	void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                      const std::string &indent, bool is_last) const override {
		ParseResult::ToStringInternal(ss, visited, indent, is_last);
		ss << ": \"" << keyword << "\"\n";
	}
};

struct ListParseResult : ParseResult {
	static constexpr ParseResultType TYPE = ParseResultType::LIST;

public:
	explicit ListParseResult(vector<optional_ptr<ParseResult>> results_p, string name_p, optional_idx offset)
	    : ParseResult(TYPE, offset), children(std::move(results_p)) {
		name = std::move(name_p);
	}

	vector<optional_ptr<ParseResult>> GetChildren() const {
		return children;
	}

	optional_ptr<ParseResult> GetChild(idx_t index) {
		if (index >= children.size()) {
			throw InternalException("Child index out of bounds");
		}
		return children[index];
	}

	template <class T>
	T &Child(idx_t index) {
		auto child_ptr = GetChild(index);
		return child_ptr->Cast<T>();
	}

	void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                      const std::string &indent, bool is_last) const override {
		ss << indent << (is_last ? "└─" : "├─");

		if (visited.count(this)) {
			ss << " List (" << name << ") [... already printed ...]\n";
			return;
		}
		visited.insert(this);

		ss << " " << ParseResultToString(type);
		if (!name.empty()) {
			ss << " (" << name << ")";
		}
		ss << " [" << children.size() << " children]\n";

		std::string child_indent = indent + (is_last ? "   " : "│  ");
		for (size_t i = 0; i < children.size(); ++i) {
			if (children[i]) {
				children[i]->ToStringInternal(ss, visited, child_indent, i == children.size() - 1);
			} else {
				ss << child_indent << (i == children.size() - 1 ? "└─" : "├─") << " [nullptr]\n";
			}
		}
	}

private:
	vector<optional_ptr<ParseResult>> children;
};

struct RepeatParseResult : ParseResult {
	static constexpr ParseResultType TYPE = ParseResultType::REPEAT;
	vector<optional_ptr<ParseResult>> children;

	explicit RepeatParseResult(vector<optional_ptr<ParseResult>> results_p, optional_idx offset)
	    : ParseResult(TYPE, offset), children(std::move(results_p)) {
	}

	template <class T>
	T &Child(idx_t index) {
		if (index >= children.size()) {
			throw InternalException("Child index out of bounds");
		}
		return children[index]->Cast<T>();
	}

	void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                      const std::string &indent, bool is_last) const override {
		ss << indent << (is_last ? "└─" : "├─");

		if (visited.count(this)) {
			ss << " Repeat (" << name << ") [... already printed ...]\n";
			return;
		}
		visited.insert(this);

		ss << " " << ParseResultToString(type);
		if (!name.empty()) {
			ss << " (" << name << ")";
		}
		ss << " [" << children.size() << " children]\n";

		std::string child_indent = indent + (is_last ? "   " : "│  ");
		for (size_t i = 0; i < children.size(); ++i) {
			if (children[i]) {
				children[i]->ToStringInternal(ss, visited, child_indent, i == children.size() - 1);
			} else {
				ss << child_indent << (i == children.size() - 1 ? "└─" : "├─") << " [nullptr]\n";
			}
		}
	}
};

struct OptionalParseResult : ParseResult {
	static constexpr ParseResultType TYPE = ParseResultType::OPTIONAL;
	optional_ptr<ParseResult> optional_result;

	explicit OptionalParseResult() : ParseResult(TYPE, optional_idx()), optional_result(nullptr) {
	}
	explicit OptionalParseResult(optional_ptr<ParseResult> result_p, optional_idx offset)
	    : ParseResult(TYPE, offset), optional_result(result_p) {
		name = result_p->name;
	}

	bool HasResult() const {
		return optional_result != nullptr;
	}

	void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                      const std::string &indent, bool is_last) const override {
		if (HasResult()) {
			// The optional node has a value, so we "collapse" it by just printing its child.
			// We pass the same indentation and is_last status, so it takes the place of the Optional node.
			optional_result->ToStringInternal(ss, visited, indent, is_last);
		} else {
			// The optional node is empty, which is useful information, so we print it.
			ss << indent << (is_last ? "└─" : "├─") << " " << ParseResultToString(type) << " [empty]\n";
		}
	}
};

class ChoiceParseResult : public ParseResult {
public:
	static constexpr ParseResultType TYPE = ParseResultType::CHOICE;

	explicit ChoiceParseResult(optional_ptr<ParseResult> parse_result_p, idx_t selected_idx_p, optional_idx offset)
	    : ParseResult(TYPE, offset), result(parse_result_p), selected_idx(selected_idx_p) {
		name = parse_result_p->name;
	}

	optional_ptr<ParseResult> result;
	idx_t selected_idx;

	void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                      const std::string &indent, bool is_last) const override {
		if (result) {
			// The choice was resolved. We print a marker and then print the child below it.
			ss << indent << (is_last ? "└─" : "├─") << " [" << ParseResultToString(type) << " (idx: " << selected_idx
			   << ")] ->\n";

			// The child is now on a new indentation level and is the only child of our marker.
			std::string child_indent = indent + (is_last ? "   " : "│  ");
			result->ToStringInternal(ss, visited, child_indent, true);
		} else {
			// The choice had no result.
			ss << indent << (is_last ? "└─" : "├─") << " " << ParseResultToString(type) << " [no result]\n";
		}
	}
};

class NumberParseResult : public ParseResult {
public:
	static constexpr ParseResultType TYPE = ParseResultType::NUMBER;

	explicit NumberParseResult(string number_p, optional_idx offset)
	    : ParseResult(TYPE, offset), number(std::move(number_p)) {
	}
	string number;

	void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                      const std::string &indent, bool is_last) const override {
		ParseResult::ToStringInternal(ss, visited, indent, is_last);
		ss << ": " << number << "\n";
	}
};

class StringLiteralParseResult : public ParseResult {
public:
	static constexpr ParseResultType TYPE = ParseResultType::STRING;

	explicit StringLiteralParseResult(string string_p, SpecialStringCharacter string_type_p, optional_idx offset)
	    : ParseResult(TYPE, offset), result(std::move(string_p)), string_type(string_type_p) {
	}

	string GetRawString() const {
		return result;
	}

	unique_ptr<ParsedExpression> ToExpression() {
		switch (string_type) {
		case SpecialStringCharacter::STANDARD:
			return make_uniq<ConstantExpression>(Value(result));
		case SpecialStringCharacter::NATIONAL_STRING:
			return make_uniq<CastExpression>(LogicalType::VARCHAR, make_uniq<ConstantExpression>(Value(result)));
		case SpecialStringCharacter::HEXADECIMAL_STRING: {
			string hex_string = "x" + result;
			return make_uniq<ConstantExpression>(Value(hex_string));
		}
		case SpecialStringCharacter::BIT_STRING: {
			string bit_string = "b" + result;
			return make_uniq<ConstantExpression>(Value(bit_string));
		}
		case SpecialStringCharacter::ESCAPE_STRING:
			string escaped_result;
			escaped_result.reserve(result.size());

			for (size_t i = 0; i < result.size(); ++i) {
				if (result[i] == '\\' && i + 1 < result.size()) {
					i++;
					switch (result[i]) {
					case 'n':
						escaped_result += '\n';
						break;
					case 't':
						escaped_result += '\t';
						break;
					case 'r':
						escaped_result += '\r';
						break;
					case '\\':
						escaped_result += '\\';
						break;
					case '\'':
						escaped_result += '\'';
						break;
					default:
						escaped_result += result[i];
						break;
					}
				} else {
					escaped_result += result[i];
				}
			}
			return make_uniq<ConstantExpression>(Value(escaped_result));
		}
		return make_uniq<ConstantExpression>(Value(result));
	}

	string result;

	SpecialStringCharacter string_type;

	void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                      const std::string &indent, bool is_last) const override {
		ParseResult::ToStringInternal(ss, visited, indent, is_last);
		string special_string;
		if (string_type == SpecialStringCharacter::ESCAPE_STRING) {
			special_string = "E";
		} else if (string_type == SpecialStringCharacter::NATIONAL_STRING) {
			special_string = "N";
		} else if (string_type == SpecialStringCharacter::HEXADECIMAL_STRING) {
			special_string = "X";
		}
		ss << ": " << special_string << "\"" << result << "\"n";
	}
};

class OperatorParseResult : public ParseResult {
public:
	static constexpr ParseResultType TYPE = ParseResultType::OPERATOR;

	explicit OperatorParseResult(string operator_p, optional_idx offset)
	    : ParseResult(TYPE, offset), operator_token(std::move(operator_p)) {
	}
	string operator_token;

	void ToStringInternal(std::stringstream &ss, std::unordered_set<const ParseResult *> &visited,
	                      const std::string &indent, bool is_last) const override {
		ParseResult::ToStringInternal(ss, visited, indent, is_last);
		ss << ": " << operator_token << "\n";
	}
};

} // namespace duckdb
