package parser

import (
	"encoding/hex"
	"fmt"
	"strconv"
	"strings"
)

// parseExpression parses an expression with operator precedence
func (p *Parser) parseExpression() (Expression, error) {
	return p.parseBinary(1)
}

// parseBinary parses binary expressions with precedence climbing
func (p *Parser) parseBinary(minPrec int) (Expression, error) {
	left, err := p.parseUnary()
	if err != nil {
		return nil, err
	}

	for {
		prec := p.precedence()
		if prec < minPrec {
			break
		}

		switch p.token.Type {
		case TokenIs:
			token := p.token.Pos
			p.nextToken()
			not := false
			if p.token.Type == TokenNot {
				not = true
				p.nextToken()
			}
			if p.token.Type != TokenNull {
				return nil, fmt.Errorf("expected NULL after IS")
			}
			p.nextToken()
			left = &IsNullExpr{
				Token: token,
				Expr:  left,
				Not:   not,
			}
			continue
		case TokenIn:
			p.nextToken()
			if p.token.Type != TokenLeftParen {
				return nil, fmt.Errorf("expected '(' after IN")
			}
			p.nextToken()
			elems := make([]Expression, 0)
			if p.token.Type != TokenRightParen {
				for {
					expr, err := p.parseExpression()
					if err != nil {
						return nil, err
					}
					elems = append(elems, expr)
					if p.token.Type != TokenComma {
						break
					}
					p.nextToken()
				}
			}
			if p.token.Type != TokenRightParen {
				return nil, fmt.Errorf("expected ')' after IN list")
			}
			p.nextToken()
			left = &BinaryExpr{
				Token: left.Position(),
				Op:    "IN",
				Left:  left,
				Right: &ListLiteral{Token: left.Position(), Elements: elems},
			}
			continue
		case TokenBetween:
			p.nextToken()
			start, err := p.parseBinary(3)
			if err != nil {
				return nil, err
			}
			if p.token.Type != TokenAnd {
				return nil, fmt.Errorf("expected AND in BETWEEN clause")
			}
			p.nextToken()
			end, err := p.parseBinary(3)
			if err != nil {
				return nil, err
			}
			left = &BinaryExpr{
				Token: left.Position(),
				Op:    "BETWEEN",
				Left:  left,
				Right: &TupleLiteral{Token: left.Position(), Elements: []Expression{start, end}},
			}
			continue
		case TokenContains:
			p.nextToken()
			op := "CONTAINS"
			if p.token.Type == TokenKey {
				op = "CONTAINS KEY"
				p.nextToken()
			}
			right, err := p.parseExpression()
			if err != nil {
				return nil, err
			}
			left = &BinaryExpr{
				Token: left.Position(),
				Op:    op,
				Left:  left,
				Right: right,
			}
			continue
		}

		op := p.token.Value
		p.nextToken()

		right, err := p.parseBinary(prec + 1)
		if err != nil {
			return nil, err
		}

		left = &BinaryExpr{
			Token: left.Position(),
			Op:    op,
			Left:  left,
			Right: right,
		}
	}

	return left, nil
}

// parseUnary parses unary expressions
func (p *Parser) parseUnary() (Expression, error) {
	switch p.token.Type {
	case TokenNot:
		token := p.token
		p.nextToken()
		expr, err := p.parseUnary()
		if err != nil {
			return nil, err
		}
		return &UnaryExpr{
			Token: token.Pos,
			Op:    "NOT",
			Expr:  expr,
		}, nil
	case TokenSubtract, TokenAdd:
		token := p.token
		p.nextToken()
		expr, err := p.parseUnary()
		if err != nil {
			return nil, err
		}
		return &UnaryExpr{
			Token: token.Pos,
			Op:    token.Value,
			Expr:  expr,
		}, nil
	default:
		return p.parsePrimary()
	}
}

// parsePrimary parses primary expressions
func (p *Parser) parsePrimary() (Expression, error) {
	var expr Expression
	switch p.token.Type {
	case TokenIdentifier:
		var err error
		expr, err = p.parseIdentifierOrFunction()
		if err != nil {
			return nil, err
		}
	case TokenString:
		value := p.token.Value
		token := p.token.Pos
		p.nextToken()
		expr = &StringLiteral{Token: token, Value: value}
	case TokenInteger:
		value, _ := strconv.ParseInt(p.token.Value, 10, 64)
		token := p.token.Pos
		p.nextToken()
		expr = &IntegerLiteral{Token: token, Value: value}
	case TokenFloat:
		raw := p.token.Value
		value, _ := strconv.ParseFloat(raw, 64)
		token := p.token.Pos
		p.nextToken()
		expr = &FloatLiteral{Token: token, Value: value, Raw: raw}
	case TokenUUID:
		value := p.token.Value
		token := p.token.Pos
		p.nextToken()
		expr = &UUIDLiteral{Token: token, Value: value}
	case TokenBlob:
		value := p.token.Value
		token := p.token.Pos
		p.nextToken()
		decoded, err := parseBlobLiteral(value)
		if err != nil {
			return nil, err
		}
		expr = &BlobLiteral{Token: token, Value: decoded}
	case TokenTrue, TokenFalse:
		value := p.token.Value
		token := p.token.Pos
		p.nextToken()
		expr = &BooleanLiteral{Token: token, Value: value}
	case TokenNull:
		token := p.token.Pos
		p.nextToken()
		expr = &NullLiteral{Token: token}
	case TokenAsterisk:
		token := p.token.Pos
		p.nextToken()
		expr = &StarExpr{Token: token}
	case TokenBindMarker:
		token := p.token.Pos
		p.nextToken()
		expr = &BindMarker{Token: token}
	case TokenColon:
		token := p.token.Pos
		p.nextToken()
		name, err := p.parseIdentifierValue()
		if err != nil {
			return nil, fmt.Errorf("expected identifier after ':'")
		}
		expr = &NamedBindMarker{Token: token, Name: name}
	case TokenToken:
		var err error
		expr, err = p.parseIdentifierOrFunction()
		if err != nil {
			return nil, err
		}
		if _, ok := expr.(*FunctionCall); !ok {
			return nil, fmt.Errorf("expected function call after TOKEN")
		}
	case TokenLeftParen:
		var err error
		expr, err = p.parseParenExpression()
		if err != nil {
			return nil, err
		}
	case TokenLeftBracket:
		var err error
		expr, err = p.parseListLiteral()
		if err != nil {
			return nil, err
		}
	case TokenLeftBrace:
		var err error
		expr, err = p.parseMapOrSetLiteral()
		if err != nil {
			return nil, err
		}
	default:
		// Check if this is a keyword that can be used as an identifier
		if isKeywordThatCanBeIdentifier(p.token.Type) {
			var err error
			expr, err = p.parseIdentifierOrFunction()
			if err != nil {
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("unexpected token in expression: %s", p.token)
		}
	}

	for {
		switch p.token.Type {
		case TokenLeftBracket:
			var err error
			expr, err = p.parseCollectionAccess(expr)
			if err != nil {
				return nil, err
			}
		case TokenTypeHint:
			token := p.token.Pos
			p.nextToken()
			typ, err := p.parseType()
			if err != nil {
				return nil, err
			}
			expr = &TypeHintExpr{
				Token: token,
				Expr:  expr,
				Type:  typ,
			}
		default:
			return expr, nil
		}
	}
}

func parseBlobLiteral(raw string) ([]byte, error) {
	s := strings.TrimSpace(raw)
	if len(s) >= 2 && s[0] == '$' && s[len(s)-1] == '$' {
		s = s[1 : len(s)-1]
	}
	if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") {
		s = s[2:]
	}
	if len(s)%2 != 0 {
		return nil, fmt.Errorf("invalid blob literal length")
	}
	if s == "" {
		return []byte{}, nil
	}
	decoded, err := hex.DecodeString(s)
	if err != nil {
		return nil, fmt.Errorf("invalid blob literal")
	}
	return decoded, nil
}

// parseIdentifierOrFunction parses an identifier or function call
func (p *Parser) parseIdentifierOrFunction() (Expression, error) {
	firstIdent := p.token.Value
	token := p.token.Pos
	p.nextToken()

	// Check if this is a function call
	if p.token.Type == TokenLeftParen {
		if strings.EqualFold(firstIdent, "CAST") {
			return p.parseCast(token)
		}

		p.nextToken() // Consume (

		args := make([]Expression, 0)
		distinct := false
		if p.token.Type != TokenRightParen {
			if p.token.Type == TokenDistinct {
				distinct = true
				p.nextToken()
			}
			for {
				arg, err := p.parseExpression()
				if err != nil {
					return nil, err
				}
				args = append(args, arg)

				if p.token.Type != TokenComma {
					break
				}
				p.nextToken()
			}
		}

		if p.token.Type != TokenRightParen {
			return nil, fmt.Errorf("expected ')' in function call")
		}
		p.nextToken() // Consume )

		return &FunctionCall{
			Token:    token,
			Name:     firstIdent,
			Args:     args,
			Distinct: distinct,
		}, nil
	}

	// Check for qualified name (table.column, table.*, keyspace.table, keyspace.table.column, keyspace.table.*)
	if p.token.Type == TokenDot {
		p.nextToken() // Consume dot

		// Check for table.*
		if p.token.Type == TokenAsterisk {
			p.nextToken()
			return &StarExpr{
				Token: token,
				Table: firstIdent,
			}, nil
		}

		// Check for table.column or keyspace.table
		if p.token.Type != TokenIdentifier && !isKeywordThatCanBeIdentifier(p.token.Type) {
			return nil, fmt.Errorf("expected identifier or * after '.', got %s", p.token)
		}

		secondIdent := p.token.Value
		p.nextToken()

		// Check for keyspace.table.column or keyspace.table.*
		if p.token.Type == TokenDot {
			p.nextToken() // Consume dot

			// Check for keyspace.table.*
			if p.token.Type == TokenAsterisk {
				p.nextToken()
				return &StarExpr{
					Token: token,
					Table: firstIdent + "." + secondIdent,
				}, nil
			}

			// Check for keyspace.table.column
			if p.token.Type != TokenIdentifier && !isKeywordThatCanBeIdentifier(p.token.Type) {
				return nil, fmt.Errorf("expected identifier or * after '%s.', got %s", secondIdent, p.token)
			}
			thirdIdent := p.token.Value
			p.nextToken()
			return &ColumnExpr{
				Token: token,
				Table: firstIdent + "." + secondIdent,
				Name:  thirdIdent,
			}, nil
		}

		// It's table.column
		return &ColumnExpr{
			Token: token,
			Table: firstIdent,
			Name:  secondIdent,
		}, nil
	}

	// It's a simple column reference
	return &ColumnExpr{
		Token: token,
		Name:  firstIdent,
	}, nil
}

func (p *Parser) parseParenExpression() (Expression, error) {
	token := p.token.Pos
	p.nextToken() // Consume (

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

	if p.token.Type == TokenComma {
		elems := []Expression{first}
		for p.token.Type == TokenComma {
			p.nextToken()
			expr, err := p.parseExpression()
			if err != nil {
				return nil, err
			}
			elems = append(elems, expr)
		}
		if p.token.Type != TokenRightParen {
			return nil, fmt.Errorf("expected ')' after tuple literal")
		}
		p.nextToken()
		return &TupleLiteral{Token: token, Elements: elems}, nil
	}

	if p.token.Type != TokenRightParen {
		return nil, fmt.Errorf("expected ')'")
	}
	p.nextToken()
	return first, nil
}

func (p *Parser) parseListLiteral() (Expression, error) {
	token := p.token.Pos
	p.nextToken() // Consume [

	elems := make([]Expression, 0)
	if p.token.Type != TokenRightBracket {
		for {
			expr, err := p.parseExpression()
			if err != nil {
				return nil, err
			}
			elems = append(elems, expr)
			if p.token.Type != TokenComma {
				break
			}
			p.nextToken()
		}
	}

	if p.token.Type != TokenRightBracket {
		return nil, fmt.Errorf("expected ']' after list literal")
	}
	p.nextToken()
	return &ListLiteral{Token: token, Elements: elems}, nil
}

func (p *Parser) parseMapOrSetLiteral() (Expression, error) {
	token := p.token.Pos
	p.nextToken() // Consume {

	if p.token.Type == TokenRightBrace {
		p.nextToken()
		return &SetLiteral{Token: token, Elements: nil}, nil
	}

	key, err := p.parseExpression()
	if err != nil {
		return nil, err
	}

	if p.token.Type == TokenColon {
		entries := make([]MapEntry, 0)
		for {
			p.nextToken() // Consume :
			val, err := p.parseExpression()
			if err != nil {
				return nil, err
			}
			entries = append(entries, MapEntry{Key: key, Value: val})
			if p.token.Type != TokenComma {
				break
			}

			// Consume one or more commas. Some generators may emit extra commas or
			// allow a trailing comma before '}' (be permissive).
			for p.token.Type == TokenComma {
				p.nextToken()
			}
			if p.token.Type == TokenRightBrace {
				break
			}

			key, err = p.parseExpression()
			if err != nil {
				return nil, err
			}
			if p.token.Type != TokenColon {
				return nil, fmt.Errorf("expected ':' in map literal")
			}
		}
		if p.token.Type != TokenRightBrace {
			return nil, fmt.Errorf("expected '}' after map literal")
		}
		p.nextToken()
		return &MapLiteral{Token: token, Entries: entries}, nil
	}

	elems := []Expression{key}
	for p.token.Type == TokenComma {
		p.nextToken()
		expr, err := p.parseExpression()
		if err != nil {
			return nil, err
		}
		elems = append(elems, expr)
	}

	if p.token.Type != TokenRightBrace {
		return nil, fmt.Errorf("expected '}' after set literal")
	}
	p.nextToken()
	return &SetLiteral{Token: token, Elements: elems}, nil
}

func (p *Parser) parseCollectionAccess(target Expression) (Expression, error) {
	token := p.token.Pos
	p.nextToken() // Consume [

	var start Expression
	var end Expression
	if p.token.Type != TokenColon && p.token.Type != TokenRightBracket {
		expr, err := p.parseExpression()
		if err != nil {
			return nil, err
		}
		start = expr
	}

	if p.token.Type == TokenColon {
		p.nextToken()
		if p.token.Type != TokenRightBracket {
			expr, err := p.parseExpression()
			if err != nil {
				return nil, err
			}
			end = expr
		}
		if p.token.Type != TokenRightBracket {
			return nil, fmt.Errorf("expected ']' after slice")
		}
		p.nextToken()
		return &CollectionAccessExpr{
			Token:  token,
			Target: target,
			Start:  start,
			End:    end,
			Slice:  true,
		}, nil
	}

	if p.token.Type != TokenRightBracket {
		return nil, fmt.Errorf("expected ']' after collection access")
	}
	p.nextToken()
	return &CollectionAccessExpr{
		Token:  token,
		Target: target,
		Start:  start,
		Slice:  false,
	}, nil
}

func (p *Parser) parseCast(token Position) (Expression, error) {
	p.nextToken() // Consume (

	expr, err := p.parseExpression()
	if err != nil {
		return nil, err
	}
	if p.token.Type != TokenAs {
		return nil, fmt.Errorf("expected AS in CAST")
	}
	p.nextToken()

	typ, err := p.parseType()
	if err != nil {
		return nil, err
	}

	if p.token.Type != TokenRightParen {
		return nil, fmt.Errorf("expected ')' after CAST")
	}
	p.nextToken()
	return &CastExpr{Token: token, Expr: expr, Type: typ}, nil
}

// parseType parses a CQL type
func (p *Parser) parseType() (Type, error) {
	switch p.token.Type {
	case TokenAscii, TokenBigint, TokenBlobType, TokenBoolean, TokenCounter,
		TokenDecimal, TokenDouble, TokenFloatType, TokenInt, TokenText,
		TokenTimestampType, TokenTimestamp, TokenUUIDType, TokenTimeUUID, TokenVarchar, TokenVarint,
		TokenInet, TokenDate, TokenTime, TokenSmallint, TokenTinyint, TokenDuration:
		typ := &SimpleType{Name: strings.ToUpper(p.token.Value)}
		p.nextToken()
		return typ, nil

	case TokenList, TokenSet, TokenSetType, TokenMap, TokenTuple:
		return p.parseCollectionType()

	case TokenVector:
		p.nextToken()
		if p.token.Type != TokenLess {
			return nil, fmt.Errorf("expected < for vector type")
		}
		p.nextToken()
		elemType, err := p.parseType()
		if err != nil {
			return nil, err
		}
		if p.token.Type != TokenComma {
			return nil, fmt.Errorf("expected , between vector element type and size")
		}
		p.nextToken()
		if p.token.Type != TokenInteger {
			return nil, fmt.Errorf("expected vector size")
		}
		size, err := strconv.Atoi(p.token.Value)
		if err != nil {
			return nil, fmt.Errorf("invalid vector size %q", p.token.Value)
		}
		p.nextToken()
		if p.token.Type != TokenGreater {
			return nil, fmt.Errorf("expected > for vector type")
		}
		p.nextToken()
		return &VectorType{ElemType: elemType, Size: size}, nil

	case TokenFrozen:
		p.nextToken()
		if p.token.Type != TokenLess {
			return nil, fmt.Errorf("expected < for frozen type")
		}
		p.nextToken()
		inner, err := p.parseType()
		if err != nil {
			return nil, err
		}
		if p.token.Type != TokenGreater {
			return nil, fmt.Errorf("expected > for frozen type")
		}
		p.nextToken()
		return &SimpleType{Name: "FROZEN<" + inner.TypeString() + ">"}, nil

	case TokenIdentifier:
		name, err := p.parseTypeName()
		if err != nil {
			return nil, err
		}
		return &SimpleType{Name: name}, nil

	default:
		if isKeywordThatCanBeIdentifier(p.token.Type) {
			name, err := p.parseTypeName()
			if err != nil {
				return nil, err
			}
			return &SimpleType{Name: name}, nil
		}
		return nil, fmt.Errorf("expected type, got %s", p.token)
	}
}

// parseCollectionType parses collection types
func (p *Parser) parseCollectionType() (Type, error) {
	token := p.token
	p.nextToken()

	switch token.Type {
	case TokenList, TokenSet, TokenSetType:
		if p.token.Type != TokenLess {
			return nil, fmt.Errorf("expected < for collection type")
		}
		p.nextToken()

		elemType, err := p.parseType()
		if err != nil {
			return nil, err
		}

		if p.token.Type != TokenGreater {
			return nil, fmt.Errorf("expected > for collection type")
		}
		p.nextToken()

		return &CollectionType{
			Kind: token.Value,
			Type: elemType,
		}, nil

	case TokenMap:
		if p.token.Type != TokenLess {
			return nil, fmt.Errorf("expected < for map type")
		}
		p.nextToken()

		keyType, err := p.parseType()
		if err != nil {
			return nil, err
		}

		if p.token.Type != TokenComma {
			return nil, fmt.Errorf("expected , between map key and value types")
		}
		p.nextToken()

		valueType, err := p.parseType()
		if err != nil {
			return nil, err
		}

		if p.token.Type != TokenGreater {
			return nil, fmt.Errorf("expected > for map type")
		}
		p.nextToken()

		return &CollectionType{
			Kind:      "map",
			Type:      keyType,
			ValueType: valueType,
		}, nil

	case TokenTuple:
		if p.token.Type != TokenLess {
			return nil, fmt.Errorf("expected < for tuple type")
		}
		p.nextToken()

		types := make([]Type, 0)
		for {
			typ, err := p.parseType()
			if err != nil {
				return nil, err
			}
			types = append(types, typ)

			if p.token.Type != TokenComma {
				break
			}
			p.nextToken()
		}

		if p.token.Type != TokenGreater {
			return nil, fmt.Errorf("expected > for tuple type")
		}
		p.nextToken()

		return &TupleType{Types: types}, nil
	}

	return nil, fmt.Errorf("unknown collection type: %s", token.Value)
}

// precedence returns the precedence of the current token
func (p *Parser) precedence() int {
	switch p.token.Type {
	case TokenOr:
		return 1
	case TokenAnd:
		return 2
	case TokenEqual, TokenNotEqual, TokenLess, TokenLessEqual,
		TokenGreater, TokenGreaterEqual, TokenIn, TokenBetween, TokenContains, TokenIs:
		return 3
	case TokenAdd, TokenSubtract:
		return 4
	case TokenMultiply, TokenDivide, TokenAsterisk:
		return 5
	default:
		return 0
	}
}

func (p *Parser) parseTypeName() (string, error) {
	name, err := p.parseIdentifierValue()
	if err != nil {
		return "", err
	}
	if p.token.Type == TokenDot {
		p.nextToken()
		part, err := p.parseIdentifierValue()
		if err != nil {
			return "", err
		}
		name = name + "." + part
	}
	return name, nil
}
