use sqruff_lib_core::dialects::base::Dialect;
use sqruff_lib_core::dialects::init::DialectKind;
use sqruff_lib_core::dialects::syntax::SyntaxKind;
use sqruff_lib_core::helpers::{Config, ToMatchable};
use sqruff_lib_core::parser::grammar::anyof::one_of;
use sqruff_lib_core::parser::grammar::base::Ref;
use sqruff_lib_core::parser::grammar::delimited::Delimited;
use sqruff_lib_core::parser::grammar::sequence::{Bracketed, Sequence};
use sqruff_lib_core::parser::lexer::Matcher;
use sqruff_lib_core::parser::matchable::MatchableTrait;
use sqruff_lib_core::parser::parsers::StringParser;
use sqruff_lib_core::parser::segments::meta::MetaSegment;
use sqruff_lib_core::vec_of_erased;

use crate::{ansi, postgres};

pub fn dialect() -> Dialect {
    raw_dialect().config(|dialect| dialect.expand())
}

pub fn raw_dialect() -> Dialect {
    let ansi_dialect = ansi::raw_dialect();
    let postgres_dialect = postgres::dialect();
    let mut duckdb_dialect = postgres_dialect;
    duckdb_dialect.name = DialectKind::Duckdb;

    duckdb_dialect.add_keyword_to_set("reserved_keywords", "Summarize");
    duckdb_dialect.add_keyword_to_set("reserved_keywords", "Macro");

    duckdb_dialect.add([
        (
            "SingleIdentifierGrammar".into(),
            one_of(vec_of_erased![
                Ref::new("NakedIdentifierSegment"),
                Ref::new("QuotedIdentifierSegment"),
                Ref::new("SingleQuotedIdentifierSegment")
            ])
            .to_matchable()
            .into(),
        ),
        (
            "DivideSegment".into(),
            one_of(vec_of_erased![
                StringParser::new("//", SyntaxKind::BinaryOperator),
                StringParser::new("/", SyntaxKind::BinaryOperator)
            ])
            .to_matchable()
            .into(),
        ),
        (
            "UnionGrammar".into(),
            ansi_dialect
                .grammar("UnionGrammar")
                .copy(
                    Some(vec_of_erased![Sequence::new(vec_of_erased![
                        Ref::keyword("BY"),
                        Ref::keyword("NAME")
                    ])
                    .config(|this| this.optional())]),
                    None,
                    None,
                    None,
                    Vec::new(),
                    false,
                )
                .into(),
        ),
        (
            "LoadStatementSegment".into(),
            Sequence::new(vec_of_erased![
                Ref::keyword("LOAD"),
                Ref::new("SingleIdentifierGrammar"),
            ])
            .to_matchable()
            .into(),
        ),
        (
            "SummarizeStatementSegment".into(),
            Sequence::new(vec_of_erased![
                Ref::keyword("SUMMARIZE"),
                one_of(vec_of_erased![
                    Ref::new("SingleIdentifierGrammar"),
                    Ref::new("SelectStatementSegment")
                ])
            ])
            .to_matchable()
            .into(),
        ),
        (
            "DescribeStatementSegment".into(),
            Sequence::new(vec_of_erased![
                Ref::keyword("DESCRIBE"),
                one_of(vec_of_erased![
                    Ref::new("SingleIdentifierGrammar"),
                    Ref::new("SelectStatementSegment")
                ])
            ])
            .to_matchable()
            .into(),
        ),
        (
            "CreateMacroStatementSegment".into(),
            Sequence::new(vec_of_erased![
                Ref::keyword("CREATE"),
                one_of(vec_of_erased![
                    Ref::keyword("TEMP"),
                    Ref::keyword("TEMPORARY")
                ])
                .config(|config| config.optional()),
                one_of(vec_of_erased![
                    Ref::keyword("MACRO"),
                    Ref::keyword("FUNCTION")
                ]),
                Ref::new("SingleIdentifierGrammar"),
                Bracketed::new(vec_of_erased![Delimited::new(vec_of_erased![Ref::new(
                    "BaseExpressionElementGrammar"
                )])]),
                Ref::keyword("AS"),
                one_of(vec_of_erased![
                    Ref::new("SelectStatementSegment"),
                    Ref::new("BaseExpressionElementGrammar")
                ])
            ])
            .to_matchable()
            .into(),
        ),
    ]);

    duckdb_dialect.insert_lexer_matchers(
        vec![Matcher::string(
            "double_divide",
            "//",
            SyntaxKind::DoubleDivide,
        )],
        "divide",
    );

    duckdb_dialect.replace_grammar(
        "SelectClauseElementSegment",
        one_of(vec_of_erased![
            Sequence::new(vec_of_erased![
                Ref::new("WildcardExpressionSegment"),
                one_of(vec_of_erased![
                    Sequence::new(vec_of_erased![
                        Ref::keyword("EXCLUDE"),
                        one_of(vec_of_erased![
                            Ref::new("ColumnReferenceSegment"),
                            Bracketed::new(vec_of_erased![Delimited::new(vec_of_erased![
                                Ref::new("ColumnReferenceSegment")
                            ])])
                        ])
                    ]),
                    Sequence::new(vec_of_erased![
                        Ref::keyword("REPLACE"),
                        Bracketed::new(vec_of_erased![Delimited::new(vec_of_erased![
                            Sequence::new(vec_of_erased![
                                Ref::new("BaseExpressionElementGrammar"),
                                Ref::new("AliasExpressionSegment").optional()
                            ])
                        ])])
                    ])
                ])
                .config(|config| {
                    config.optional();
                })
            ]),
            Sequence::new(vec_of_erased![
                Ref::new("BaseExpressionElementGrammar"),
                Ref::new("AliasExpressionSegment").optional()
            ])
        ])
        .to_matchable(),
    );

    duckdb_dialect.replace_grammar(
        "OrderByClauseSegment",
        Sequence::new(vec_of_erased![
            Ref::keyword("ORDER"),
            Ref::keyword("BY"),
            MetaSegment::indent(),
            Delimited::new(vec_of_erased![Sequence::new(vec_of_erased![
                one_of(vec_of_erased![
                    Ref::keyword("ALL"),
                    Ref::new("ColumnReferenceSegment"),
                    Ref::new("NumericLiteralSegment"),
                    Ref::new("ExpressionSegment")
                ]),
                one_of(vec_of_erased![Ref::keyword("ASC"), Ref::keyword("DESC")]).config(
                    |config| {
                        config.optional();
                    }
                ),
                Sequence::new(vec_of_erased![
                    Ref::keyword("NULLS"),
                    one_of(vec_of_erased![Ref::keyword("FIRST"), Ref::keyword("LAST")])
                ])
                .config(|config| {
                    config.optional();
                })
            ])])
            .config(|config| {
                config.allow_trailing = true;
                config.terminators = vec_of_erased![Ref::new("OrderByClauseTerminators")];
            }),
            MetaSegment::dedent()
        ])
        .to_matchable(),
    );

    duckdb_dialect.replace_grammar(
        "GroupByClauseSegment",
        Sequence::new(vec_of_erased![
            Ref::keyword("GROUP"),
            Ref::keyword("BY"),
            MetaSegment::indent(),
            Delimited::new(vec_of_erased![one_of(vec_of_erased![
                Ref::keyword("ALL"),
                Ref::new("ColumnReferenceSegment"),
                Ref::new("NumericLiteralSegment"),
                Ref::new("ExpressionSegment")
            ])])
            .config(|config| {
                config.allow_trailing = true;
                config.terminators = vec_of_erased![Ref::new("GroupByClauseTerminatorGrammar")];
            }),
            MetaSegment::dedent()
        ])
        .to_matchable(),
    );

    duckdb_dialect.replace_grammar(
        "ObjectLiteralElementSegment",
        Sequence::new(vec_of_erased![
            one_of(vec_of_erased![
                Ref::new("NakedIdentifierSegment"),
                Ref::new("QuotedLiteralSegment")
            ]),
            Ref::new("ColonSegment"),
            Ref::new("BaseExpressionElementGrammar")
        ])
        .to_matchable(),
    );

    duckdb_dialect.replace_grammar(
        "StatementSegment",
        postgres::statement_segment().copy(
            Some(vec_of_erased![
                Ref::new("LoadStatementSegment"),
                Ref::new("SummarizeStatementSegment"),
                Ref::new("DescribeStatementSegment"),
                Ref::new("CreateMacroStatementSegment")
            ]),
            None,
            None,
            None,
            vec![],
            false,
        ),
    );

    duckdb_dialect
}
