package parser

import (
	"fmt"
)

func (p *Parser) parseDDL(pos Pos) (DDL, error) {
	switch {
	case p.matchKeyword(KeywordCreate),
		p.matchKeyword(KeywordAttach):
		_ = p.lexer.consumeToken()
		orReplace := p.tryConsumeKeywords(KeywordOr, KeywordReplace)
		if orReplace && !p.matchOneOfKeywords(KeywordTemporary, KeywordTable, KeywordView, KeywordFunction, KeywordDictionary) {
			return nil, fmt.Errorf("expected keyword: TEMPORARY|TABLE|VIEW|FUNCTION|DICTIONARY, but got %q", p.last().String)
		}
		switch {
		case p.matchKeyword(KeywordDatabase):
			return p.parseCreateDatabase(pos)
		case p.matchKeyword(KeywordDictionary):
			return p.parseCreateDictionary(pos, orReplace)
		case p.matchKeyword(KeywordTable),
			p.matchKeyword(KeywordTemporary):
			return p.parseCreateTable(pos, orReplace)
		case p.matchKeyword(KeywordFunction):
			return p.parseCreateFunction(pos, orReplace)
		case p.matchKeyword(KeywordMaterialized):
			return p.parseCreateMaterializedView(pos)
		case p.matchKeyword(KeywordLive):
			return p.parseCreateLiveView(pos)
		case p.matchKeyword(KeywordView):
			return p.parseCreateView(pos, orReplace)
		case p.matchKeyword(KeywordRole):
			return p.parseCreateRole(pos)
		case p.matchKeyword(KeywordUser):
			return p.parseCreateUser(pos)
		default:
			return nil, fmt.Errorf("expected keyword: DATABASE|DICTIONARY|TABLE|VIEW|ROLE|USER|FUNCTION|MATERIALIZED, but got %q",
				p.lastTokenKind())
		}
	case p.matchKeyword(KeywordAlter):
		_ = p.lexer.consumeToken()
		switch {
		case p.matchKeyword(KeywordRole):
			return p.parseAlterRole(pos)
		case p.matchKeyword(KeywordTable):
			return p.parseAlterTable(pos)
		default:
			return nil, fmt.Errorf("expected keyword: TABLE|ROLE, but got %q", p.last().String)
		}
	case p.matchKeyword(KeywordDrop),
		p.matchKeyword(KeywordDetach):
		_ = p.lexer.consumeToken()
		switch {
		case p.matchKeyword(KeywordDatabase):
			return p.parseDropDatabase(pos)
		case p.matchKeyword(KeywordTemporary),
			p.matchKeyword(KeywordView),
			p.matchKeyword(KeywordDictionary),
			p.matchKeyword(KeywordTable):
			return p.parseDropStmt(pos)
		case p.matchKeyword(KeywordUser),
			p.matchKeyword(KeywordRole):
			return p.parserDropUserOrRole(pos)
		default:
			return nil, fmt.Errorf("expected keyword: DATABASE|TABLE, but got %q", p.last().String)
		}
	case p.matchKeyword(KeywordTruncate):
		return p.parseTruncateTable(pos)
	case p.matchKeyword(KeywordRename):
		return p.parseRenameStmt(pos)
	}
	return nil, nil // nolint
}

func (p *Parser) parseCreateDatabase(pos Pos) (*CreateDatabase, error) {
	if err := p.expectKeyword(KeywordDatabase); err != nil {
		return nil, err
	}

	// try to parse IF NOT EXISTS clause
	ifNotExists, err := p.tryParseIfNotExists()
	if err != nil {
		return nil, err
	}
	// parse database name
	name, err := p.parseIdent()
	if err != nil {
		return nil, err
	}
	StatementEnd := name.End()
	onCluster, err := p.tryParseClusterClause(p.Pos())
	if err != nil {
		return nil, err
	}
	if onCluster != nil {
		StatementEnd = onCluster.End()
	}
	engineExpr, err := p.tryParseEngineExpr(p.Pos())
	if err != nil {
		return nil, err
	}
	if engineExpr != nil {
		StatementEnd = onCluster.End()
	}
	commentExpr, err := p.tryParseComment()
	if err != nil {
		return nil, err
	}
	return &CreateDatabase{
		CreatePos:    pos,
		StatementEnd: StatementEnd,
		Name:         name,
		IfNotExists:  ifNotExists,
		OnCluster:    onCluster,
		Engine:       engineExpr,
		Comment:      commentExpr,
	}, nil
}

func (p *Parser) parseCreateDictionary(pos Pos, orReplace bool) (*CreateDictionary, error) {
	if err := p.expectKeyword(KeywordDictionary); err != nil {
		return nil, err
	}

	createDict := &CreateDictionary{
		CreatePos: pos,
		OrReplace: orReplace,
	}

	// parse IF NOT EXISTS clause if exists
	var err error
	createDict.IfNotExists, err = p.tryParseIfNotExists()
	if err != nil {
		return nil, err
	}

	// parse dictionary name
	name, err := p.parseTableIdentifier(p.Pos())
	if err != nil {
		return nil, err
	}
	createDict.Name = name

	// try parse UUID clause if exists
	uuid, err := p.tryParseUUID()
	if err != nil {
		return nil, err
	}
	createDict.UUID = uuid

	// parse ON CLUSTER clause if exists
	onCluster, err := p.tryParseClusterClause(p.Pos())
	if err != nil {
		return nil, err
	}
	createDict.OnCluster = onCluster

	// parse dictionary schema clause (required)
	schema, err := p.parseDictionarySchemaClause(p.Pos())
	if err != nil {
		return nil, err
	}
	createDict.Schema = schema

	// parse dictionary engine clause (required)
	engine, err := p.parseDictionaryEngineClause(p.Pos())
	if err != nil {
		return nil, err
	}
	createDict.Engine = engine
	createDict.StatementEnd = engine.End()

	return createDict, nil
}

func (p *Parser) parseCreateTable(pos Pos, orReplace bool) (*CreateTable, error) {
	createTable := &CreateTable{CreatePos: pos, OrReplace: orReplace}
	createTable.HasTemporary = p.tryConsumeKeywords(KeywordTemporary)

	if err := p.expectKeyword(KeywordTable); err != nil {
		return nil, err
	}

	// parse IF NOT EXISTS clause if exists
	var err error
	createTable.IfNotExists, err = p.tryParseIfNotExists()
	if err != nil {
		return nil, err
	}

	tableIdentifier, err := p.parseTableIdentifier(p.Pos())
	if err != nil {
		return nil, err
	}
	createTable.Name = tableIdentifier

	// try parse UUID clause if exists
	uuid, err := p.tryParseUUID()
	if err != nil {
		return nil, err
	}
	createTable.UUID = uuid
	// parse ON CLUSTER clause if exists
	onCluster, err := p.tryParseClusterClause(p.Pos())
	if err != nil {
		return nil, err
	}
	createTable.OnCluster = onCluster

	tableSchema, err := p.parseTableSchemaClause(p.Pos())
	if err != nil {
		return nil, err
	}
	createTable.TableSchema = tableSchema

	engineExpr, err := p.tryParseEngineExpr(p.Pos())
	if err != nil {
		return nil, err
	}
	if engineExpr != nil {
		createTable.Engine = engineExpr
		createTable.StatementEnd = engineExpr.End()
	}

	if p.tryConsumeKeywords(KeywordAs) {
		subQuery, err := p.parseSubQuery(p.Pos())
		if err != nil {
			return nil, err
		}
		createTable.SubQuery = subQuery
		createTable.StatementEnd = subQuery.End()
	}

	comment, err := p.tryParseComment()
	if err != nil {
		return nil, err
	}
	createTable.Comment = comment
	return createTable, nil
}

func (p *Parser) parseIdentOrFunction(_ Pos) (Expr, error) {
	ident, err := p.parseIdent()
	if err != nil {
		return nil, err
	}
	switch {
	case p.matchTokenKind(TokenKindLBracket):
		params, err := p.parseArrayParams(p.Pos())
		if err != nil {
			return nil, err
		}
		return &ObjectParams{
			Object: ident,
			Params: params,
		}, nil
	case p.matchTokenKind(TokenKindLParen):
		params, err := p.parseFunctionParams(p.Pos())
		if err != nil {
			return nil, err
		}
		funcExpr := &FunctionExpr{
			Name:   ident,
			Params: params,
		}

		overPos := p.Pos()
		if p.tryConsumeKeywords(KeywordOver) {
			var overExpr Expr
			switch {
			case p.matchTokenKind(TokenKindIdent):
				overExpr, err = p.parseIdent()
			case p.matchTokenKind(TokenKindLParen):
				overExpr, err = p.parseWindowCondition(p.Pos())
				if err != nil {
					return nil, err
				}
			default:
				return nil, fmt.Errorf("expected IDENT or (, but got %q", p.lastTokenKind())
			}

			if err != nil {
				return nil, err
			}
			return &WindowFunctionExpr{
				Function: funcExpr,
				OverPos:  overPos,
				OverExpr: overExpr,
			}, nil
		}
		return funcExpr, nil
	case p.tryConsumeTokenKind(TokenKindDot) != nil:
		switch {
		case p.matchTokenKind(TokenKindIdent):
			fields := []*Ident{ident}
			for {
				child, err := p.parseIdent()
				if err != nil {
					return nil, err
				}
				fields = append(fields, child)
				if p.tryConsumeTokenKind(TokenKindDot) == nil {
					break
				}
			}
			return &Path{Fields: fields}, nil
		case p.matchTokenKind("*"):
			nextIdent, err := p.parseColumnStar(p.Pos())
			if err != nil {
				return nil, err
			}
			return &NestedIdentifier{
				Ident:    ident,
				DotIdent: nextIdent,
			}, nil
		case p.matchTokenKind(TokenKindInt):
			i, err := p.parseNumber(p.Pos())
			if err != nil {
				return nil, err
			}
			return &IndexOperation{
				Object:    ident,
				Operation: TokenKindDot,
				Index:     i,
			}, nil
		default:
			return nil, fmt.Errorf("expected IDENT, NUMBER or *, but got %q", p.lastTokenKind())
		}
	}
	return ident, nil
}

func (p *Parser) parseTableIdentifier(_ Pos) (*TableIdentifier, error) {
	ident, err := p.parseIdentOrString()
	if err != nil {
		return nil, err
	}
	dotIdent, err := p.tryParseDotIdentOrString(p.Pos())
	if err != nil {
		return nil, err
	}
	if dotIdent != nil {
		return &TableIdentifier{
			Database: ident,
			Table:    dotIdent,
		}, nil
	}
	return &TableIdentifier{
		Table: ident,
	}, nil
}

func (p *Parser) parseTableSchemaClause(pos Pos) (*TableSchemaClause, error) {
	switch {
	case p.matchTokenKind(TokenKindLParen):
		// parse column definitions
		if err := p.expectTokenKind(TokenKindLParen); err != nil {
			return nil, err
		}

		columns, err := p.parseTableColumns()
		if err != nil {
			return nil, err
		}

		rightParenPos := p.Pos()
		if err := p.expectTokenKind(TokenKindRParen); err != nil {
			return nil, err
		}
		return &TableSchemaClause{
			SchemaPos: pos,
			SchemaEnd: rightParenPos,
			Columns:   columns,
		}, nil
	case p.tryConsumeKeywords(KeywordAs):
		switch {
		case p.matchTokenKind(TokenKindIdent):
			ident, err := p.parseIdent()
			if err != nil {
				return nil, err
			}
			switch {
			case p.matchTokenKind(TokenKindDot):
				// it's a database.table
				dotIdent, err := p.tryParseDotIdent(p.Pos())
				if err != nil {
					return nil, err
				}
				return &TableSchemaClause{
					SchemaPos: pos,
					SchemaEnd: dotIdent.End(),
					AliasTable: &TableIdentifier{
						Database: ident,
						Table:    dotIdent,
					},
				}, nil
			case p.matchTokenKind(TokenKindLParen):
				// it's a table function
				argsExpr, err := p.parseTableArgList(pos)
				if err != nil {
					return nil, err
				}
				return &TableSchemaClause{
					SchemaPos: pos,
					SchemaEnd: p.End(),
					TableFunction: &TableFunctionExpr{
						Name: ident,
						Args: argsExpr,
					},
				}, nil
			default:
				return &TableSchemaClause{
					SchemaPos: pos,
					SchemaEnd: p.End(),
					AliasTable: &TableIdentifier{
						Table: ident,
					},
				}, nil
			}
		}
	}
	// no schema is ok for MATERIALIZED VIEW
	return nil, nil
}

func (p *Parser) parseTableColumns() ([]Expr, error) {
	columns := make([]Expr, 0)
	for !p.lexer.isEOF() {
		switch {
		case p.matchKeyword(KeywordIndex):
			indexPos := p.Pos()
			_ = p.lexer.consumeToken()
			index, err := p.parseTableIndex(indexPos)
			if err != nil {
				return nil, err
			}
			columns = append(columns, index)
		case p.matchKeyword(KeywordConstraint):
			constraintPos := p.Pos()
			_ = p.lexer.consumeToken()
			ident, err := p.parseIdent()
			if err != nil {
				return nil, err
			}
			if err := p.expectKeyword(KeywordCheck); err != nil {
				return nil, err
			}
			expr, err := p.parseExpr(p.Pos())
			if err != nil {
				return nil, err
			}
			columns = append(columns, &ConstraintClause{
				ConstraintPos: constraintPos,
				Constraint:    ident,
				Expr:          expr,
			})
		case p.matchKeyword(KeywordProjection):
			projection, err := p.parseTableProjection(p.Pos(), true)
			if err != nil {
				return nil, err
			}
			columns = append(columns, projection)
		default:
			column, err := p.tryParseTableColumnExpr(p.Pos())
			if err != nil {
				return nil, err
			}
			if column == nil {
				break
			}
			columns = append(columns, column)
		}
		if p.tryConsumeTokenKind(TokenKindComma) == nil {
			break
		}
	}
	// end of column definitions
	return columns, nil
}

func (p *Parser) tryParseTableColumnExpr(pos Pos) (*ColumnDef, error) {
	if !p.matchTokenKind(TokenKindIdent) {
		return nil, nil // nolint
	}
	return p.parseTableColumnExpr(pos)
}

func (p *Parser) parseTableColumnExpr(pos Pos) (*ColumnDef, error) {
	// Not a column definition, just return
	column := &ColumnDef{NamePos: pos}
	// parse column name
	name, err := p.ParseNestedIdentifier(p.Pos())
	if err != nil {
		return nil, err
	}
	column.Name = name
	columnEnd := name.End()

	if p.matchTokenKind(TokenKindIdent) && !p.matchKeyword(KeywordRemove) {
		columnType, err := p.parseColumnType(p.Pos())
		if err != nil {
			return nil, err
		}
		column.Type = columnType
		columnEnd = columnType.End()
	}

	nullable := p.tryParseNull(p.Pos())
	if nullable != nil {
		columnEnd = nullable.End()
	}
	notNull, err := p.tryParseNotNull(p.Pos())
	if err != nil {
		return nil, err
	}
	if notNull != nil {
		columnEnd = notNull.End()
	}

	switch {
	case p.tryConsumeKeywords(KeywordDefault):
		column.DefaultExpr, err = p.parseExpr(p.Pos())
		columnEnd = column.DefaultExpr.End()
	case p.tryConsumeKeywords(KeywordMaterialized):
		column.MaterializedExpr, err = p.parseExpr(p.Pos())
		columnEnd = column.MaterializedExpr.End()
	case p.tryConsumeKeywords(KeywordAlias):
		column.AliasExpr, err = p.parseExpr(p.Pos())
		columnEnd = column.AliasExpr.End()
	}
	if err != nil {
		return nil, err
	}

	comment, err := p.tryParseColumnComment(p.Pos())
	if err != nil {
		return nil, err
	}
	if comment != nil {
		columnEnd = comment.End()
	}

	codec, err := p.tryParseCompressionCodecs(p.Pos())
	if err != nil {
		return nil, err
	}
	if codec != nil {
		columnEnd = codec.End()
	}
	ttl, err := p.tryParseTTLClause(p.Pos(), false)
	if err != nil {
		return nil, err
	}
	if ttl != nil {
		columnEnd = ttl.End()
	}
	column.TTL = ttl

	column.ColumnEnd = columnEnd
	column.Comment = comment
	column.Codec = codec
	column.Nullable = nullable
	column.NotNull = notNull
	return column, nil
}

func (p *Parser) parseTableArgExpr(pos Pos) (Expr, error) {
	switch {
	case p.matchTokenKind(TokenKindIdent):
		ident, err := p.parseIdent()
		if err != nil {
			return nil, err
		}
		switch {
		// nest identifier
		case p.matchTokenKind(TokenKindDot):
			dotIdent, err := p.tryParseDotIdent(p.Pos())
			if err != nil {
				return nil, err
			}
			return &NestedIdentifier{
				Ident:    ident,
				DotIdent: dotIdent,
			}, nil
		case p.matchTokenKind(TokenKindLParen):
			argsExpr, err := p.parseTableArgList(pos)
			if err != nil {
				return nil, err
			}
			return &TableFunctionExpr{
				Name: ident,
				Args: argsExpr,
			}, nil
		default:
			return ident, nil
		}
	case p.matchTokenKind(TokenKindLParen):
		return p.parseSubQuery(p.Pos())
	case p.matchTokenKind(TokenKindInt), p.matchTokenKind(TokenKindString), p.matchKeyword(KeywordNull):
		return p.parseLiteral(p.Pos())
	default:
		return nil, fmt.Errorf("unexpected token: %q, expected <Name>, <literal>", p.last().String)
	}
}

func (p *Parser) parseTableArgList(pos Pos) (*TableArgListExpr, error) {
	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	args := make([]Expr, 0)
	for !p.lexer.isEOF() {
		arg, err := p.parseTableArgExpr(p.Pos())
		if err != nil {
			return nil, err
		}
		args = append(args, arg)
		if p.tryConsumeTokenKind(TokenKindComma) == nil {
			break
		}
	}

	rightParenPos := p.Pos()
	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}

	return &TableArgListExpr{
		LeftParenPos:  pos,
		RightParenPos: rightParenPos,
		Args:          args,
	}, nil
}

func (p *Parser) tryParseClusterClause(pos Pos) (*ClusterClause, error) {
	if !p.tryConsumeKeywords(KeywordOn) {
		return nil, nil // nolint
	}
	if err := p.expectKeyword(KeywordCluster); err != nil {
		return nil, err
	}

	var expr Expr
	var err error
	switch {
	case p.matchTokenKind(TokenKindIdent):
		expr, err = p.parseIdent()
	case p.matchTokenKind(TokenKindString):
		expr, err = p.parseString(p.Pos())
	default:
		return nil, fmt.Errorf("unexpected token: %q, expected <IDENT> or <STRING>", p.last().String)
	}
	if err != nil {
		return nil, err
	}
	return &ClusterClause{
		OnPos: pos,
		Expr:  expr,
	}, nil
}

func (p *Parser) tryParsePartitionByClause(pos Pos) (*PartitionByClause, error) {
	if !p.tryConsumeKeywords(KeywordPartition) {
		return nil, nil // nolint
	}

	if err := p.expectKeyword(KeywordBy); err != nil {
		return nil, err
	}

	// parse partition key list
	columnExpr, err := p.parseColumnExprListWithLParen(p.Pos())
	if err != nil {
		return nil, err
	}
	return &PartitionByClause{
		PartitionPos: pos,
		Expr:         columnExpr,
	}, nil
}

func (p *Parser) tryParsePrimaryKeyClause(pos Pos) (*PrimaryKeyClause, error) {
	if !p.tryConsumeKeywords(KeywordPrimary) {
		return nil, nil // nolint
	}

	if err := p.expectKeyword(KeywordKey); err != nil {
		return nil, err
	}

	// parse partition key list
	columnExpr, err := p.parseExpr(p.Pos())
	if err != nil {
		return nil, err
	}
	return &PrimaryKeyClause{
		PrimaryPos: pos,
		Expr:       columnExpr,
	}, nil
}

func (p *Parser) tryParseOrderByClause(pos Pos) (*OrderByClause, error) {
	if !p.tryConsumeKeywords(KeywordOrder) {
		return nil, nil // nolint
	}

	if err := p.expectKeyword(KeywordBy); err != nil {
		return nil, err
	}
	return p.parseOrderByClause(pos)
}

func (p *Parser) parseOrderByClause(pos Pos) (*OrderByClause, error) {
	orderByListExpr := &OrderByClause{OrderPos: pos, ListEnd: pos}
	items := make([]Expr, 0)
	for {
		expr, err := p.parseOrderExpr(pos)
		if err != nil {
			return nil, err
		}
		if expr == nil {
			break
		}
		items = append(items, expr)

		if p.lexer.isEOF() || p.tryConsumeTokenKind(TokenKindComma) == nil {
			break
		}
	}
	if len(items) > 0 {
		orderByListExpr.ListEnd = items[len(items)-1].End()
	}
	orderByListExpr.Items = items
	return orderByListExpr, nil
}

func (p *Parser) parseOrderExpr(pos Pos) (*OrderExpr, error) {
	// parse column expr
	columnExpr, err := p.parseExpr(pos)
	if err != nil {
		return nil, err
	}

	var alias *Ident
	if p.matchKeyword(KeywordAs) {
		// It should be a subquery instead of an order by alias if the `AS` is followed by `SELECT` keyword.
		if nextToken, err := p.lexer.peekToken(); err == nil && nextToken.ToString() == KeywordSelect {
			return &OrderExpr{
				OrderPos: pos,
				Expr:     columnExpr,
			}, nil
		}
		// consume the `AS` keyword
		_ = p.lexer.consumeToken()
		alias, err = p.parseIdent()
		if err != nil {
			return nil, err
		}
	} else if p.matchKeyword(KeywordTtl) {
		return &OrderExpr{
			OrderPos: pos,
			Expr:     columnExpr,
		}, nil
	}

	direction := OrderDirectionNone
	switch {
	case p.matchKeyword(KeywordAsc), p.matchKeyword(KeywordAscending):
		direction = OrderDirectionAsc
		_ = p.lexer.consumeToken()
	case p.matchKeyword(KeywordDesc), p.matchKeyword(KeywordDescending):
		direction = OrderDirectionDesc
		_ = p.lexer.consumeToken()
	}
	return &OrderExpr{
		OrderPos:  pos,
		Alias:     alias,
		Expr:      columnExpr,
		Direction: direction,
	}, nil
}

func (p *Parser) tryParseTTLClause(pos Pos, allowMultiValues bool) (*TTLClause, error) {
	if !p.tryConsumeKeywords(KeywordTtl) {
		return nil, nil // nolint
	}
	ttlExprList := &TTLClause{TTLPos: pos, ListEnd: pos}
	// accept the TTL keyword
	items, err := p.parseTTLClause(pos, allowMultiValues)
	if err != nil {
		return nil, err
	}
	if len(items) > 0 {
		ttlExprList.ListEnd = items[len(items)-1].End()
	}
	ttlExprList.Items = items
	return ttlExprList, nil
}

// parseTTLClause parses the TTL clause.
// allowMultiValues is used to determine whether to allow multiple TTL values.
func (p *Parser) parseTTLClause(pos Pos, allowMultiValues bool) ([]*TTLExpr, error) {
	items := make([]*TTLExpr, 0)
	expr, err := p.parseTTLExpr(pos)
	if err != nil {
		return nil, err
	}
	items = append(items, expr)
	for allowMultiValues && !p.lexer.isEOF() && p.tryConsumeTokenKind(TokenKindComma) != nil {
		expr, err = p.parseTTLExpr(pos)
		if err != nil {
			return nil, err
		}
		items = append(items, expr)
	}
	return items, nil
}

func (p *Parser) tryParseTTLPolicy(pos Pos) (*TTLPolicy, error) {
	var rule *TTLPolicyRule
	switch {
	case p.tryConsumeKeywords(KeywordTo):
		if p.tryConsumeKeywords(KeywordDisk) {
			value, err := p.parseString(p.Pos())
			if err != nil {
				return nil, err
			}
			rule = &TTLPolicyRule{RulePos: pos, ToDisk: value}
		} else if p.tryConsumeKeywords(KeywordVolume) {
			value, err := p.parseString(p.Pos())
			if err != nil {
				return nil, err
			}
			rule = &TTLPolicyRule{RulePos: pos, ToVolume: value}
		} else {
			return nil, fmt.Errorf("unexpected token: %q, expected DISK or VOLUME", p.lastTokenKind())
		}
	case p.matchKeyword(KeywordDelete), p.matchKeyword(KeywordRecompress):
		token := p.last()
		_ = p.lexer.consumeToken()
		action := &TTLPolicyRuleAction{
			ActionPos: token.Pos,
			ActionEnd: token.End,
			Action:    token.ToString(),
		}
		codec, err := p.tryParseCompressionCodecs(p.Pos())
		if err != nil {
			return nil, err
		}
		action.Codec = codec
		rule = &TTLPolicyRule{RulePos: pos, Action: action}
	default:
		return nil, nil // nolint
	}
	policy := &TTLPolicy{Item: rule}

	where, err := p.tryParseWhereClause(p.Pos())
	if err != nil {
		return nil, err
	}
	policy.Where = where

	groupBy, err := p.tryParseGroupByClause(p.Pos())
	if err != nil {
		return nil, err
	}
	policy.GroupBy = groupBy
	return policy, nil
}

func (p *Parser) parseTTLExpr(pos Pos) (*TTLExpr, error) {
	columnExpr, err := p.parseExpr(pos)
	if err != nil {
		return nil, err
	}
	policy, err := p.tryParseTTLPolicy(p.Pos())
	if err != nil {
		return nil, err
	}
	return &TTLExpr{
		TTLPos: pos,
		Expr:   columnExpr,
		Policy: policy,
	}, nil
}

func (p *Parser) tryParseSampleByClause(pos Pos) (*SampleByClause, error) {
	if !p.tryConsumeKeywords(KeywordSample) {
		return nil, nil // nolint
	}

	if err := p.expectKeyword(KeywordBy); err != nil {
		return nil, err
	}

	// parse sample by expr
	columnExpr, err := p.parseExpr(p.Pos())
	if err != nil {
		return nil, err
	}
	return &SampleByClause{
		SamplePos: pos,
		Expr:      columnExpr,
	}, nil
}

func (p *Parser) tryParseSettingsClause(pos Pos) (*SettingsClause, error) {
	if !p.tryConsumeKeywords(KeywordSettings) {
		return nil, nil // nolint
	}
	return p.parseSettingsClause(pos)
}

func (p *Parser) parseSettingsClause(pos Pos) (*SettingsClause, error) {
	settings := &SettingsClause{SettingsPos: pos, ListEnd: pos}
	items, err := p.parseSettingsList(p.Pos())
	if err != nil {
		return nil, err
	}

	if len(items) == 0 {
		return nil, fmt.Errorf("settings list is empty")
	}
	settings.ListEnd = items[len(items)-1].End()
	settings.Items = items
	return settings, nil
}

func (p *Parser) parseSettingsList(pos Pos) ([]*SettingExpr, error) {
	items := make([]*SettingExpr, 0)
	expr, err := p.parseSettingsExpr(pos)
	if err != nil {
		return nil, err
	}
	items = append(items, expr)
	for p.tryConsumeTokenKind(TokenKindComma) != nil {
		expr, err = p.parseSettingsExpr(p.Pos())
		if err != nil {
			return nil, err
		}
		items = append(items, expr)
	}
	return items, nil
}

func (p *Parser) parseSettingsExpr(pos Pos) (*SettingExpr, error) {
	ident, err := p.parseIdent()
	if err != nil {
		return nil, err
	}

	if err := p.expectTokenKind(TokenKindSingleEQ); err != nil {
		return nil, err
	}

	var expr Expr
	switch {
	case p.matchTokenKind(TokenKindInt):
		number, err := p.parseNumber(p.Pos())
		if err != nil {
			return nil, err
		}
		expr = number
	case p.matchTokenKind(TokenKindString):
		str, err := p.parseString(p.Pos())
		expr = str
		if err != nil {
			return nil, err
		}
	case p.matchTokenKind(TokenKindLBrace):
		m, err := p.parseMapLiteral(p.Pos())
		if err != nil {
			return nil, err
		}
		expr = m
	default:
		return nil, fmt.Errorf("unexpected token: %q, expected <number> or <string>", p.last().String)
	}

	return &SettingExpr{
		SettingsPos: pos,
		Name:        ident,
		Expr:        expr,
	}, nil
}

func (p *Parser) parseDestinationClause(pos Pos) (*DestinationClause, error) {
	if err := p.expectKeyword(KeywordTo); err != nil {
		return nil, err
	}

	tableIdentifier, err := p.parseTableIdentifier(p.Pos())
	if err != nil {
		return nil, err
	}
	return &DestinationClause{
		ToPos:           pos,
		TableIdentifier: tableIdentifier,
	}, nil
}

func (p *Parser) tryParseEngineExpr(pos Pos) (*EngineExpr, error) {
	if !p.matchKeyword(KeywordEngine) {
		return nil, nil // nolint
	}
	return p.parseEngineExpr(pos)
}

func (p *Parser) parseEngineExpr(pos Pos) (*EngineExpr, error) {
	if err := p.expectKeyword(KeywordEngine); err != nil {
		return nil, err
	}
	_ = p.tryConsumeTokenKind(TokenKindSingleEQ)

	engineExpr := &EngineExpr{EnginePos: pos}
	var engineEnd Pos
	switch {
	case p.matchTokenKind(TokenKindIdent):
		ident, err := p.parseIdent()
		if err != nil {
			return nil, err
		}
		engineExpr.Name = ident.Name
		engineEnd = ident.End()
		if p.matchTokenKind(TokenKindLParen) {
			params, err := p.parseFunctionParams(p.Pos())
			if err != nil {
				return nil, err
			}
			engineExpr.Params = params
			engineExpr.EngineEnd = params.End()
		}
	default:
		return nil, fmt.Errorf("unexpected token: %s", p.lastTokenKind())
	}

	for !p.lexer.isEOF() {
		switch {
		case p.matchKeyword(KeywordOrder):
			orderBy, err := p.tryParseOrderByClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engineExpr.OrderBy = orderBy
			engineEnd = orderBy.End()
		case p.matchKeyword(KeywordPartition):
			partitionBy, err := p.tryParsePartitionByClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engineExpr.PartitionBy = partitionBy
			engineEnd = partitionBy.End()
		case p.matchKeyword(KeywordPrimary):
			primaryKey, err := p.tryParsePrimaryKeyClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engineExpr.PrimaryKey = primaryKey
			engineEnd = primaryKey.End()
		case p.matchKeyword(KeywordSample):
			sampleBy, err := p.tryParseSampleByClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engineExpr.SampleBy = sampleBy
			engineEnd = sampleBy.End()
		case p.matchKeyword(KeywordTtl):
			ttl, err := p.tryParseTTLClause(p.Pos(), true)
			if err != nil {
				return nil, err
			}
			engineExpr.TTL = ttl
			engineEnd = ttl.End()
		case p.matchKeyword(KeywordSettings):
			settingsClause, err := p.tryParseSettingsClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engineExpr.Settings = settingsClause
			engineEnd = settingsClause.End()
		default:
			engineExpr.EngineEnd = engineEnd
			return engineExpr, nil
		}
	}
	engineExpr.EngineEnd = engineEnd
	return engineExpr, nil
}

func (p *Parser) parseStmt(pos Pos) (Expr, error) {
	var err error
	var expr Expr
	switch {
	case p.matchKeyword(KeywordCreate),
		p.matchKeyword(KeywordAttach),
		p.matchKeyword(KeywordAlter),
		p.matchKeyword(KeywordDrop),
		p.matchKeyword(KeywordDetach),
		p.matchKeyword(KeywordTruncate),
		p.matchKeyword(KeywordRename):
		expr, err = p.parseDDL(pos)
	case p.matchKeyword(KeywordSelect), p.matchKeyword(KeywordWith):
		expr, err = p.parseSelectQuery(pos)
	case p.matchKeyword(KeywordDelete):
		expr, err = p.parseDeleteClause(pos)
	case p.matchKeyword(KeywordInsert):
		expr, err = p.parseInsertStmt(p.Pos())
	case p.matchKeyword(KeywordUse):
		expr, err = p.parseUseStmt(pos)
	case p.matchKeyword(KeywordSet):
		expr, err = p.parseSetStmt(pos)
	case p.matchKeyword(KeywordSystem):
		expr, err = p.parseSystemStmt(pos)
	case p.matchKeyword(KeywordOptimize):
		expr, err = p.parseOptimizeStmt(pos)
	case p.matchKeyword(KeywordCheck):
		expr, err = p.parseCheckStmt(pos)
	case p.matchKeyword(KeywordExplain):
		expr, err = p.parseExplainStmt(pos)
	case p.matchKeyword(KeywordGrant):
		expr, err = p.parseGrantPrivilegeStmt(pos)
	case p.matchKeyword(KeywordShow):
		expr, err = p.parseShowStmt(pos)
	case p.matchKeyword(KeywordDesc), p.matchKeyword(KeywordDescribe):
		expr, err = p.parseDescribeStmt(pos)
	default:
		return nil, fmt.Errorf("unexpected token: %q", p.last().String)
	}
	if err != nil {
		return nil, err
	}
	_, err = p.tryParseFormat(p.Pos())
	if err != nil {
		return nil, err
	}

	// Statement can be terminated by ';' or EOF
	if p.last() != nil && !p.matchTokenKind(";") {
		return nil, fmt.Errorf("<EOF> or ';' was expected, but got: %q", p.last().String)
	}
	return expr, nil
}

func (p *Parser) ParseStmts() ([]Expr, error) {
	var stmts []Expr
	for {
		_ = p.lexer.consumeToken()
		if p.lexer.isEOF() {
			break
		}
		if p.matchTokenKind(";") {
			continue
		}
		stmt, err := p.parseStmt(p.Pos())
		if err != nil {
			return nil, p.wrapError(err)
		}
		stmts = append(stmts, stmt)
	}
	return stmts, nil
}

func (p *Parser) parseUseStmt(pos Pos) (*UseStmt, error) {
	if err := p.expectKeyword(KeywordUse); err != nil {
		return nil, err
	}

	database, err := p.parseIdent()
	if err != nil {
		return nil, err
	}

	return &UseStmt{
		UsePos:       pos,
		Database:     database,
		StatementEnd: database.End(),
	}, nil
}

func (p *Parser) parseShowStmt(pos Pos) (*ShowStmt, error) {
	if err := p.expectKeyword(KeywordShow); err != nil {
		return nil, err
	}

	var showType string
	var target *TableIdentifier

	// Parse the type of SHOW statement
	switch {
	case p.matchKeyword(KeywordCreate):
		// SHOW CREATE TABLE table_name
		showType = "CREATE"
		_ = p.lexer.consumeToken()

		if err := p.expectKeyword(KeywordTable); err != nil {
			return nil, err
		}
		showType += " TABLE"

		tableIdent, err := p.parseTableIdentifier(p.Pos())
		if err != nil {
			return nil, err
		}
		target = tableIdent

	case p.matchKeyword(KeywordDatabases):
		// SHOW DATABASES [optional clauses]
		showType = "DATABASES"
		_ = p.lexer.consumeToken()

	case p.matchKeyword(KeywordTables):
		// SHOW TABLES
		showType = "TABLES"
		_ = p.lexer.consumeToken()

	default:
		return nil, fmt.Errorf("expected CREATE, DATABASES, or TABLES after SHOW, got %q", p.last().String)
	}

	stmt := &ShowStmt{
		ShowPos:  pos,
		ShowType: showType,
		Target:   target,
	}

	// Parse optional clauses for SHOW DATABASES
	if showType == "DATABASES" {
		// Parse [[NOT] LIKE | ILIKE '<pattern>']
		if p.matchKeyword(KeywordNot) {
			stmt.NotLike = true
			_ = p.lexer.consumeToken()
		}

		if p.matchKeyword(KeywordLike) || p.matchKeyword(KeywordIlike) {
			if p.matchKeyword(KeywordLike) {
				stmt.LikeType = "LIKE"
			} else {
				stmt.LikeType = "ILIKE"
			}
			_ = p.lexer.consumeToken()

			// Parse pattern expression
			pattern, err := p.parseExpr(p.Pos())
			if err != nil {
				return nil, err
			}
			stmt.LikePattern = pattern
		}

		// Parse [LIMIT <N>]
		if p.matchKeyword(KeywordLimit) {
			_ = p.lexer.consumeToken()
			limit, err := p.parseExpr(p.Pos())
			if err != nil {
				return nil, err
			}
			stmt.Limit = limit
		}

		// Parse [INTO OUTFILE filename]
		if p.matchKeyword(KeywordInto) {
			_ = p.lexer.consumeToken()
			if err := p.expectKeyword(KeywordOutfile); err != nil {
				return nil, err
			}

			// Parse filename as a string literal
			outFile, err := p.parseString(p.Pos())
			if err != nil {
				return nil, err
			}
			stmt.OutFile = outFile
		}

		// Parse [FORMAT format]
		if p.matchKeyword(KeywordFormat) {
			_ = p.lexer.consumeToken()

			// Format can be an identifier or a string
			if p.matchTokenKind(TokenKindString) {
				format, err := p.parseString(p.Pos())
				if err != nil {
					return nil, err
				}
				stmt.Format = format
			} else if p.matchTokenKind(TokenKindIdent) {
				// Handle format as identifier (like JSON, CSV, etc.)
				token := p.last()
				_ = p.lexer.consumeToken()
				stmt.Format = &StringLiteral{
					LiteralPos: token.Pos,
					LiteralEnd: token.End,
					Literal:    token.String,
				}
			} else {
				return nil, fmt.Errorf("expected format specification after FORMAT, got %q", p.last().String)
			}
		}
	}

	// Set statement end position
	stmt.StatementEnd = p.End()

	return stmt, nil
}

func (p *Parser) parseDescribeStmt(pos Pos) (*DescribeStmt, error) {
	// DESC and DESCRIBE are both supported
	if !p.matchKeyword(KeywordDesc) && !p.matchKeyword(KeywordDescribe) {
		return nil, fmt.Errorf("expected DESC or DESCRIBE")
	}
	_ = p.lexer.consumeToken()

	// TABLE keyword is optional after DESC/DESCRIBE
	var describeType string
	if p.matchKeyword(KeywordTable) {
		_ = p.lexer.consumeToken()
		describeType = "TABLE"
	}

	tableIdent, err := p.parseTableIdentifier(p.Pos())
	if err != nil {
		return nil, err
	}

	return &DescribeStmt{
		DescribePos:  pos,
		StatementEnd: tableIdent.End(),
		DescribeType: describeType,
		Target:       tableIdent,
	}, nil
}

// syntax: TRUNCATE TEMPORARY? TABLE (IF EXISTS)? tableIdentifier clusterClause?;
func (p *Parser) parseTruncateTable(pos Pos) (*TruncateTable, error) {
	if err := p.expectKeyword(KeywordTruncate); err != nil {
		return nil, err
	}

	isTemporary := p.tryConsumeKeywords(KeywordTemporary)

	if err := p.expectKeyword(KeywordTable); err != nil {
		return nil, err
	}

	ifExists, err := p.tryParseIfExists()
	if err != nil {
		return nil, err
	}

	tableName, err := p.parseTableIdentifier(p.Pos())
	if err != nil {
		return nil, err
	}

	onCluster, err := p.tryParseClusterClause(p.Pos())
	if err != nil {
		return nil, err
	}

	truncateTable := &TruncateTable{
		TruncatePos:  pos,
		IsTemporary:  isTemporary,
		IfExists:     ifExists,
		Name:         tableName,
		OnCluster:    onCluster,
		StatementEnd: tableName.End(),
	}

	if onCluster != nil {
		truncateTable.StatementEnd = onCluster.End()
	}

	return truncateTable, nil
}

func (p *Parser) parseDeleteClause(pos Pos) (*DeleteClause, error) {
	if err := p.expectKeyword(KeywordDelete); err != nil {
		return nil, err
	}
	if err := p.expectKeyword(KeywordFrom); err != nil {
		return nil, err
	}
	tableIdentifier, err := p.parseTableIdentifier(p.Pos())
	if err != nil {
		return nil, err
	}
	onCluster, err := p.tryParseClusterClause(p.Pos())
	if err != nil {
		return nil, err
	}

	if err := p.expectKeyword(KeywordWhere); err != nil {
		return nil, err
	}
	whereExpr, err := p.parseExpr(p.Pos())
	if err != nil {
		return nil, err
	}

	return &DeleteClause{
		DeletePos: pos,
		Table:     tableIdentifier,
		OnCluster: onCluster,
		WhereExpr: whereExpr,
	}, nil
}

func (p *Parser) parseColumnNamesExpr(pos Pos) (*ColumnNamesExpr, error) {
	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	var columnNames []NestedIdentifier
	for !p.lexer.isEOF() && p.tryConsumeTokenKind(TokenKindRParen) == nil {
		name, err := p.ParseNestedIdentifier(p.Pos())
		if err != nil {
			return nil, err
		}

		columnNames = append(columnNames, *name)
		if p.tryConsumeTokenKind(TokenKindComma) == nil {
			break
		}
	}
	rightParenPos := p.Pos()
	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}
	return &ColumnNamesExpr{
		LeftParenPos:  pos,
		RightParenPos: rightParenPos,
		ColumnNames:   columnNames,
	}, nil
}

func (p *Parser) parseTypedPlaceholder(pos Pos) (Expr, error) {
	if err := p.expectTokenKind(TokenKindLBrace); err != nil {
		return nil, err
	}

	name, err := p.parseIdent()
	if err != nil {
		return nil, err
	}
	if err := p.expectTokenKind(TokenKindColon); err != nil {
		return nil, err
	}
	columnType, err := p.parseColumnType(p.Pos())
	if err != nil {
		return nil, err
	}

	if err := p.expectTokenKind(TokenKindRBrace); err != nil {
		return nil, err
	}
	return &TypedPlaceholder{
		LeftBracePos:  pos,
		RightBracePos: p.Pos(),
		Name:          name,
		Type:          columnType,
	}, nil
}

func (p *Parser) parseAssignmentValues(pos Pos) (*AssignmentValues, error) {
	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	var value Expr
	var err error
	values := make([]Expr, 0)
	for !p.lexer.isEOF() && p.tryConsumeTokenKind(TokenKindRParen) == nil {
		switch {
		case p.matchTokenKind(TokenKindLParen):
			value, err = p.parseAssignmentValues(p.Pos())
		case p.matchTokenKind(TokenKindLBrace):
			// placeholder with type, e.g. {a :Int32}, {b :DateTime(6)}
			value, err = p.parseTypedPlaceholder(p.Pos())
		default:
			value, err = p.parseExpr(p.Pos())
		}
		if err != nil {
			return nil, err
		}
		values = append(values, value)
		if p.tryConsumeTokenKind(TokenKindComma) == nil {
			break
		}
	}
	rightParenPos := p.Pos()
	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}

	return &AssignmentValues{
		LeftParenPos:  pos,
		RightParenPos: rightParenPos,
		Values:        values,
	}, nil
}

func (p *Parser) parseInsertStmt(pos Pos) (*InsertStmt, error) {
	if err := p.expectKeyword(KeywordInsert); err != nil {
		return nil, err
	}
	if err := p.expectKeyword(KeywordInto); err != nil {
		return nil, err
	}

	insertExpr := &InsertStmt{InsertPos: pos}
	insertExpr.HasTableKeyword = p.tryConsumeKeywords(KeywordTable)

	var table Expr
	var err error
	if p.tryConsumeKeywords(KeywordFunction) {
		table, err = p.parseFunctionExpr(p.Pos())
	} else {
		table, err = p.parseTableIdentifier(p.Pos())
	}
	if err != nil {
		return nil, err
	}
	insertExpr.Table = table

	if p.matchTokenKind(TokenKindLParen) {
		// parse column names
		insertExpr.ColumnNames, err = p.parseColumnNamesExpr(p.Pos())
		if err != nil {
			return nil, err
		}
	}

	switch {
	case p.matchKeyword(KeywordFormat):
		insertExpr.Format, err = p.parseFormat(p.Pos())
	case p.matchKeyword(KeywordValues):
		// consume VALUES keyword
		_ = p.lexer.consumeToken()
		values := make([]*AssignmentValues, 0)
		for !p.lexer.isEOF() {
			value, err := p.parseAssignmentValues(p.Pos())
			if err != nil {
				return nil, err
			}
			values = append(values, value)
			if p.tryConsumeTokenKind(TokenKindComma) == nil {
				break
			}
		}
		insertExpr.Values = values
	case p.matchKeyword(KeywordSelect):
		insertExpr.SelectExpr, err = p.parseSelectQuery(p.Pos())
	default:
		// do nothing
	}

	if err != nil {
		return nil, err
	}
	return insertExpr, nil
}

func (p *Parser) parseRenameStmt(pos Pos) (*RenameStmt, error) {
	if err := p.expectKeyword(KeywordRename); err != nil {
		return nil, err
	}

	renameTarget := KeywordTable
	switch {
	case p.tryConsumeKeywords(KeywordDictionary):
		renameTarget = KeywordDictionary
	case p.tryConsumeKeywords(KeywordDatabase):
		renameTarget = KeywordDatabase
	default:
		if err := p.expectKeyword(KeywordTable); err != nil {
			return nil, err
		}
	}

	targetPair, err := p.parseTargetPair(p.Pos())
	if err != nil {
		return nil, err
	}
	tablePairList := []*TargetPair{targetPair}
	for p.tryConsumeTokenKind(TokenKindComma) != nil {
		tablePair, err := p.parseTargetPair(p.Pos())
		if err != nil {
			return nil, err
		}
		tablePairList = append(tablePairList, tablePair)
	}

	renameStmt := &RenameStmt{
		RenamePos:    pos,
		StatementEnd: tablePairList[len(tablePairList)-1].End(),

		RenameTarget:   renameTarget,
		TargetPairList: tablePairList,
	}

	onCluster, err := p.tryParseClusterClause(p.Pos())
	if err != nil {
		return nil, err
	}
	if onCluster != nil {
		renameStmt.OnCluster = onCluster
		renameStmt.StatementEnd = onCluster.End()
	}

	return renameStmt, nil
}

func (p *Parser) parseTargetPair(_ Pos) (*TargetPair, error) {
	oldTable, err := p.parseTableIdentifier(p.Pos())
	if err != nil {
		return nil, err
	}
	if err = p.expectKeyword(KeywordTo); err != nil {
		return nil, err
	}
	newTable, err := p.parseTableIdentifier(p.Pos())
	if err != nil {
		return nil, err
	}

	return &TargetPair{
		Old: oldTable,
		New: newTable,
	}, nil
}

func (p *Parser) parseCreateFunction(pos Pos, orReplace bool) (*CreateFunction, error) {
	if err := p.expectKeyword(KeywordFunction); err != nil {
		return nil, err
	}
	ifNotExists, err := p.tryParseIfNotExists()
	if err != nil {
		return nil, err
	}
	functionName, err := p.parseIdent()
	if err != nil {
		return nil, err
	}
	onCluster, err := p.tryParseClusterClause(p.Pos())
	if err != nil {
		return nil, err
	}
	if err := p.expectKeyword(KeywordAs); err != nil {
		return nil, err
	}
	params, err := p.parseFunctionParams(p.Pos())
	if err != nil {
		return nil, err
	}
	if err := p.expectTokenKind(TokenKindArrow); err != nil {
		return nil, err
	}
	expr, err := p.parseExpr(p.Pos())
	if err != nil {
		return nil, err
	}
	return &CreateFunction{
		CreatePos:    pos,
		OrReplace:    orReplace,
		IfNotExists:  ifNotExists,
		FunctionName: functionName,
		OnCluster:    onCluster,
		Params:       params,
		Expr:         expr,
	}, nil
}

// Dictionary parsing functions

func (p *Parser) parseDictionarySchemaClause(pos Pos) (*DictionarySchemaClause, error) {
	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	schema := &DictionarySchemaClause{
		SchemaPos: pos,
	}

	// Parse first attribute
	attr, err := p.parseDictionaryAttribute(p.Pos())
	if err != nil {
		return nil, err
	}
	schema.Attributes = append(schema.Attributes, attr)

	// Parse additional attributes
	for p.tryConsumeTokenKind(TokenKindComma) != nil {
		attr, err := p.parseDictionaryAttribute(p.Pos())
		if err != nil {
			return nil, err
		}
		schema.Attributes = append(schema.Attributes, attr)
	}

	rParenPos := p.Pos()
	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}
	schema.RParenPos = rParenPos

	return schema, nil
}

func (p *Parser) parseDictionaryAttribute(pos Pos) (*DictionaryAttribute, error) {
	name, err := p.parseIdent()
	if err != nil {
		return nil, err
	}

	columnType, err := p.parseColumnType(p.Pos())
	if err != nil {
		return nil, err
	}

	attr := &DictionaryAttribute{
		NamePos: pos,
		Name:    name,
		Type:    columnType,
	}

	// Parse optional attribute properties
	for {
		switch {
		case p.tryConsumeKeywords(KeywordDefault):
			if attr.Default != nil {
				return nil, fmt.Errorf("duplicate DEFAULT clause")
			}
			literal, err := p.parseLiteral(p.Pos())
			if err != nil {
				return nil, err
			}
			attr.Default = literal
		case p.tryConsumeKeywords(KeywordExpression):
			if attr.Expression != nil {
				return nil, fmt.Errorf("duplicate EXPRESSION clause")
			}
			expr, err := p.parseExpr(p.Pos())
			if err != nil {
				return nil, err
			}
			attr.Expression = expr
		case p.tryConsumeKeywords(KeywordHierarchical):
			if attr.Hierarchical {
				return nil, fmt.Errorf("duplicate HIERARCHICAL clause")
			}
			attr.Hierarchical = true
		case p.tryConsumeKeywords(KeywordInjective):
			if attr.Injective {
				return nil, fmt.Errorf("duplicate INJECTIVE clause")
			}
			attr.Injective = true
		case p.tryConsumeKeywords(KeywordIs_object_id):
			if attr.IsObjectId {
				return nil, fmt.Errorf("duplicate IS_OBJECT_ID clause")
			}
			attr.IsObjectId = true
		default:
			// No more attribute properties
			return attr, nil
		}
	}
}

func (p *Parser) parseDictionaryEngineClause(pos Pos) (*DictionaryEngineClause, error) {
	engine := &DictionaryEngineClause{
		EnginePos: pos,
	}

	// Parse PRIMARY KEY clause (optional)
	if p.matchKeyword(KeywordPrimary) {
		primaryKey, err := p.parseDictionaryPrimaryKeyClause(p.Pos())
		if err != nil {
			return nil, err
		}
		engine.PrimaryKey = primaryKey
	}

	// Parse engine clauses (SOURCE, LIFETIME, LAYOUT, RANGE, SETTINGS)
	for {
		switch {
		case p.matchKeyword(KeywordSource):
			if engine.Source != nil {
				return nil, fmt.Errorf("duplicate SOURCE clause")
			}
			source, err := p.parseDictionarySourceClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engine.Source = source
		case p.matchKeyword(KeywordLifetime):
			if engine.Lifetime != nil {
				return nil, fmt.Errorf("duplicate LIFETIME clause")
			}
			lifetime, err := p.parseDictionaryLifetimeClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engine.Lifetime = lifetime
		case p.matchKeyword(KeywordLayout):
			if engine.Layout != nil {
				return nil, fmt.Errorf("duplicate LAYOUT clause")
			}
			layout, err := p.parseDictionaryLayoutClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engine.Layout = layout
		case p.matchKeyword(KeywordRange):
			if engine.Range != nil {
				return nil, fmt.Errorf("duplicate RANGE clause")
			}
			rangeClause, err := p.parseDictionaryRangeClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engine.Range = rangeClause
		case p.matchKeyword(KeywordSettings):
			if engine.Settings != nil {
				return nil, fmt.Errorf("duplicate SETTINGS clause")
			}
			settings, err := p.parseDictionarySettingsClause(p.Pos())
			if err != nil {
				return nil, err
			}
			engine.Settings = settings
		default:
			// No more engine clauses
			if engine.Source == nil {
				return nil, fmt.Errorf("SOURCE clause is required for dictionary")
			}
			return engine, nil
		}
	}
}

func (p *Parser) parseDictionaryPrimaryKeyClause(pos Pos) (*DictionaryPrimaryKeyClause, error) {
	if err := p.expectKeyword(KeywordPrimary); err != nil {
		return nil, err
	}
	if err := p.expectKeyword(KeywordKey); err != nil {
		return nil, err
	}

	keys, err := p.parseColumnExprList(p.Pos())
	if err != nil {
		return nil, err
	}

	return &DictionaryPrimaryKeyClause{
		PrimaryKeyPos: pos,
		Keys:          keys,
		RParenPos:     keys.End() - 1,
	}, nil
}

func (p *Parser) parseDictionarySourceClause(pos Pos) (*DictionarySourceClause, error) {
	if err := p.expectKeyword(KeywordSource); err != nil {
		return nil, err
	}

	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	sourceName, err := p.parseIdent()
	if err != nil {
		return nil, err
	}

	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	var args []*DictionaryArgExpr
	// Parse optional arguments
	for !p.matchTokenKind(TokenKindRParen) {
		arg, err := p.parseDictionaryArgExpr(p.Pos())
		if err != nil {
			return nil, err
		}
		args = append(args, arg)

		// If there's no right paren, we expect another arg (no comma needed)
		if p.matchTokenKind(TokenKindRParen) {
			break
		}
	}

	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}

	rParenPos := p.Pos()
	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}

	return &DictionarySourceClause{
		SourcePos: pos,
		Source:    sourceName,
		Args:      args,
		RParenPos: rParenPos,
	}, nil
}

func (p *Parser) parseDictionaryArgExpr(pos Pos) (*DictionaryArgExpr, error) {
	name, err := p.parseIdent()
	if err != nil {
		return nil, err
	}

	var value Expr
	// Parse the value part
	switch {
	case p.matchTokenKind(TokenKindString):
		literal, err := p.parseLiteral(p.Pos())
		if err != nil {
			return nil, err
		}
		value = literal
	case p.matchTokenKind(TokenKindInt), p.matchTokenKind(TokenKindFloat):
		literal, err := p.parseLiteral(p.Pos())
		if err != nil {
			return nil, err
		}
		value = literal
	case p.matchTokenKind(TokenKindIdent):
		ident, err := p.parseIdent()
		if err != nil {
			return nil, err
		}
		// Check if it's followed by optional parentheses
		if p.matchTokenKind(TokenKindLParen) {
			_ = p.lexer.consumeToken() // consume (
			if err := p.expectTokenKind(TokenKindRParen); err != nil {
				return nil, err
			}
			value = &FunctionExpr{
				Name: ident,
			}
		} else {
			value = ident
		}
	default:
		return nil, fmt.Errorf("expected identifier, string or number in dictionary argument, got %s", p.lastTokenKind())
	}

	return &DictionaryArgExpr{
		ArgPos: pos,
		Name:   name,
		Value:  value,
	}, nil
}

func (p *Parser) parseDictionaryLifetimeClause(pos Pos) (*DictionaryLifetimeClause, error) {
	if err := p.expectKeyword(KeywordLifetime); err != nil {
		return nil, err
	}

	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	lifetime := &DictionaryLifetimeClause{
		LifetimePos: pos,
	}

	// Check for MIN/MAX form
	if p.matchKeyword(KeywordMin) || p.matchKeyword(KeywordMax) {
		isMinFirst := p.matchKeyword(KeywordMin)
		_ = p.lexer.consumeToken() // consume MIN or MAX

		first, err := p.parseNumber(p.Pos())
		if err != nil {
			return nil, err
		}

		// Expect the other keyword
		if isMinFirst {
			if err := p.expectKeyword(KeywordMax); err != nil {
				return nil, err
			}
		} else {
			if err := p.expectKeyword(KeywordMin); err != nil {
				return nil, err
			}
		}

		second, err := p.parseNumber(p.Pos())
		if err != nil {
			return nil, err
		}

		if isMinFirst {
			lifetime.Min = first
			lifetime.Max = second
		} else {
			lifetime.Min = second
			lifetime.Max = first
		}
	} else {
		// Simple form: LIFETIME(value)
		value, err := p.parseNumber(p.Pos())
		if err != nil {
			return nil, err
		}
		lifetime.Value = value
	}

	rParenPos := p.Pos()
	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}
	lifetime.RParenPos = rParenPos

	return lifetime, nil
}

func (p *Parser) parseDictionaryLayoutClause(pos Pos) (*DictionaryLayoutClause, error) {
	if err := p.expectKeyword(KeywordLayout); err != nil {
		return nil, err
	}

	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	layoutName, err := p.parseIdent()
	if err != nil {
		return nil, err
	}

	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	var args []*DictionaryArgExpr
	// Parse optional arguments
	for !p.matchTokenKind(TokenKindRParen) {
		arg, err := p.parseDictionaryArgExpr(p.Pos())
		if err != nil {
			return nil, err
		}
		args = append(args, arg)

		// If there's no right paren, we expect another arg (no comma needed)
		if p.matchTokenKind(TokenKindRParen) {
			break
		}
	}

	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}

	rParenPos := p.Pos()
	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}

	return &DictionaryLayoutClause{
		LayoutPos: pos,
		Layout:    layoutName,
		Args:      args,
		RParenPos: rParenPos,
	}, nil
}

func (p *Parser) parseDictionaryRangeClause(pos Pos) (*DictionaryRangeClause, error) {
	if err := p.expectKeyword(KeywordRange); err != nil {
		return nil, err
	}

	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	rangeClause := &DictionaryRangeClause{
		RangePos: pos,
	}

	// Parse MIN identifier MAX identifier or MAX identifier MIN identifier
	if p.matchKeyword(KeywordMin) {
		if err := p.expectKeyword(KeywordMin); err != nil {
			return nil, err
		}
		min, err := p.parseIdent()
		if err != nil {
			return nil, err
		}
		if err := p.expectKeyword(KeywordMax); err != nil {
			return nil, err
		}
		max, err := p.parseIdent()
		if err != nil {
			return nil, err
		}
		rangeClause.Min = min
		rangeClause.Max = max
	} else if p.matchKeyword(KeywordMax) {
		if err := p.expectKeyword(KeywordMax); err != nil {
			return nil, err
		}
		max, err := p.parseIdent()
		if err != nil {
			return nil, err
		}
		if err := p.expectKeyword(KeywordMin); err != nil {
			return nil, err
		}
		min, err := p.parseIdent()
		if err != nil {
			return nil, err
		}
		rangeClause.Min = min
		rangeClause.Max = max
	} else {
		return nil, fmt.Errorf("expected MIN or MAX in RANGE clause")
	}

	rParenPos := p.Pos()
	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}
	rangeClause.RParenPos = rParenPos

	return rangeClause, nil
}

func (p *Parser) parseDictionarySettingsClause(pos Pos) (*SettingsClause, error) {
	if err := p.expectKeyword(KeywordSettings); err != nil {
		return nil, err
	}

	if err := p.expectTokenKind(TokenKindLParen); err != nil {
		return nil, err
	}

	settings := &SettingsClause{SettingsPos: pos, ListEnd: pos}
	items := make([]*SettingExpr, 0)
	// Parse first setting
	expr, err := p.parseSettingsExpr(p.Pos())
	if err != nil {
		return nil, err
	}
	items = append(items, expr)

	// Parse additional settings
	for p.tryConsumeTokenKind(TokenKindComma) != nil {
		expr, err := p.parseSettingsExpr(p.Pos())
		if err != nil {
			return nil, err
		}
		items = append(items, expr)
	}

	if len(items) > 0 {
		settings.ListEnd = items[len(items)-1].End()
	}
	settings.Items = items

	if err := p.expectTokenKind(TokenKindRParen); err != nil {
		return nil, err
	}

	return settings, nil
}
