/*
 * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
 * Copyright (c) 2021, Mahmoud Mandour <ma.mandourr@gmail.com>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/String.h>
#include <AK/StringView.h>
#include <LibSQL/AST/AST.h>
#include <LibSQL/AST/Lexer.h>
#include <LibSQL/AST/Token.h>

namespace SQL::AST {

namespace Limits {
// https://www.sqlite.org/limits.html
constexpr size_t maximum_expression_tree_depth = 1000;
constexpr size_t maximum_subquery_depth = 100;
}

class Parser {
    struct Error {
        String message;
        SourcePosition position;

        String to_string() const
        {
            return String::formatted("{} (line: {}, column: {})", message, position.line, position.column);
        }
    };

public:
    explicit Parser(Lexer lexer);

    NonnullRefPtr<Statement> next_statement();

    bool has_errors() const { return m_parser_state.m_errors.size(); }
    const Vector<Error>& errors() const { return m_parser_state.m_errors; }

protected:
    NonnullRefPtr<Expression> parse_expression(); // Protected for unit testing.

private:
    struct ParserState {
        explicit ParserState(Lexer);

        Lexer m_lexer;
        Token m_token;
        Vector<Error> m_errors;
        size_t m_current_expression_depth { 0 };
        size_t m_current_subquery_depth { 0 };
    };

    NonnullRefPtr<Statement> parse_statement();
    NonnullRefPtr<Statement> parse_statement_with_expression_list(RefPtr<CommonTableExpressionList>);
    NonnullRefPtr<CreateSchema> parse_create_schema_statement();
    NonnullRefPtr<CreateTable> parse_create_table_statement();
    NonnullRefPtr<AlterTable> parse_alter_table_statement();
    NonnullRefPtr<DropTable> parse_drop_table_statement();
    NonnullRefPtr<DescribeTable> parse_describe_table_statement();
    NonnullRefPtr<Insert> parse_insert_statement(RefPtr<CommonTableExpressionList>);
    NonnullRefPtr<Update> parse_update_statement(RefPtr<CommonTableExpressionList>);
    NonnullRefPtr<Delete> parse_delete_statement(RefPtr<CommonTableExpressionList>);
    NonnullRefPtr<Select> parse_select_statement(RefPtr<CommonTableExpressionList>);
    RefPtr<CommonTableExpressionList> parse_common_table_expression_list();

    NonnullRefPtr<Expression> parse_primary_expression();
    NonnullRefPtr<Expression> parse_secondary_expression(NonnullRefPtr<Expression> primary);
    bool match_secondary_expression() const;
    RefPtr<Expression> parse_literal_value_expression();
    RefPtr<Expression> parse_column_name_expression(String with_parsed_identifier = {}, bool with_parsed_period = false);
    RefPtr<Expression> parse_unary_operator_expression();
    RefPtr<Expression> parse_binary_operator_expression(NonnullRefPtr<Expression> lhs);
    RefPtr<Expression> parse_chained_expression();
    RefPtr<Expression> parse_cast_expression();
    RefPtr<Expression> parse_case_expression();
    RefPtr<Expression> parse_exists_expression(bool invert_expression, TokenType opening_token = TokenType::Exists);
    RefPtr<Expression> parse_collate_expression(NonnullRefPtr<Expression> expression);
    RefPtr<Expression> parse_is_expression(NonnullRefPtr<Expression> expression);
    RefPtr<Expression> parse_match_expression(NonnullRefPtr<Expression> lhs, bool invert_expression);
    RefPtr<Expression> parse_null_expression(NonnullRefPtr<Expression> expression, bool invert_expression);
    RefPtr<Expression> parse_between_expression(NonnullRefPtr<Expression> expression, bool invert_expression);
    RefPtr<Expression> parse_in_expression(NonnullRefPtr<Expression> expression, bool invert_expression);

    NonnullRefPtr<ColumnDefinition> parse_column_definition();
    NonnullRefPtr<TypeName> parse_type_name();
    NonnullRefPtr<SignedNumber> parse_signed_number();
    NonnullRefPtr<CommonTableExpression> parse_common_table_expression();
    NonnullRefPtr<QualifiedTableName> parse_qualified_table_name();
    NonnullRefPtr<ReturningClause> parse_returning_clause();
    NonnullRefPtr<ResultColumn> parse_result_column();
    NonnullRefPtr<TableOrSubquery> parse_table_or_subquery();
    NonnullRefPtr<OrderingTerm> parse_ordering_term();
    void parse_schema_and_table_name(String& schema_name, String& table_name);
    ConflictResolution parse_conflict_resolution();

    template<typename ParseCallback>
    void parse_comma_separated_list(bool surrounded_by_parentheses, ParseCallback&& parse_callback)
    {
        if (surrounded_by_parentheses)
            consume(TokenType::ParenOpen);

        while (!has_errors() && !match(TokenType::Eof)) {
            parse_callback();

            if (!match(TokenType::Comma))
                break;

            consume(TokenType::Comma);
        };

        if (surrounded_by_parentheses)
            consume(TokenType::ParenClose);
    }

    Token consume();
    Token consume(TokenType type);
    bool consume_if(TokenType type);
    bool match(TokenType type) const;

    void expected(StringView what);
    void syntax_error(String message);

    SourcePosition position() const;

    ParserState m_parser_state;
};

}