package parser

import (
	"fmt"
	"strings"
	"unicode/utf8"
)

// TokenType represents the type of a token
type TokenType int

const (
	// Special tokens
	TokenEOF TokenType = iota
	TokenError

	// Literals
	TokenIdentifier
	TokenString
	TokenInteger
	TokenFloat
	TokenUUID
	TokenBlob

	// Keywords
	TokenSelect
	TokenFrom
	TokenWhere
	TokenInsert
	TokenInto
	TokenValues
	TokenUpdate
	TokenSet
	TokenDelete
	TokenCreate
	TokenDrop
	TokenAlter
	TokenTruncate
	TokenKeyspace
	TokenTable
	TokenIndex
	TokenPrimary
	TokenKey
	TokenIf
	TokenExists
	TokenNot
	TokenAnd
	TokenOr
	TokenIn
	TokenContains
	TokenBetween
	TokenAllow
	TokenFiltering
	TokenOrder
	TokenBy
	TokenAscending
	TokenDescending
	TokenLimit
	TokenOffset
	TokenCount
	TokenDistinct
	TokenAs
	TokenUsing
	TokenTTL
	TokenTimestamp
	TokenBegin
	TokenBatch
	TokenApply
	TokenUnlogged
	TokenLogged
	TokenOn
	TokenWith
	TokenCompact
	TokenStorage
	TokenPer
	TokenPartition
	TokenClustering
	TokenStatic
	TokenToken
	TokenAscii
	TokenBigint
	TokenBlobType
	TokenBoolean
	TokenCounter
	TokenDecimal
	TokenDouble
	TokenFloatType
	TokenInt
	TokenText
	TokenTimestampType
	TokenUUIDType
	TokenTimeUUID
	TokenVarchar
	TokenVarint
	TokenInet
	TokenDate
	TokenTime
	TokenSmallint
	TokenTinyint
	TokenList
	TokenMap
	TokenSetType
	TokenFrozen
	TokenTuple
	TokenVector
	TokenTrue
	TokenFalse
	TokenNull
	TokenAddKeyword
	TokenAggregate
	TokenAll
	TokenAuthorize
	TokenCalled
	TokenCast
	TokenColumnFamily
	TokenCustom
	TokenDefault
	TokenDescribe
	TokenDuration
	TokenEntries
	TokenExecute
	TokenFinalFunc
	TokenFull
	TokenFunction
	TokenFunctions
	TokenGrant
	TokenGroup
	TokenInfinity
	TokenInitCond
	TokenInput
	TokenIs
	TokenJSON
	TokenKeys
	TokenKeyspaces
	TokenLanguage
	TokenLike
	TokenLogin
	TokenMasked
	TokenMaterialized
	TokenMBean
	TokenMBeans
	TokenModify
	TokenNaN
	TokenNoLogin
	TokenNoRecursive
	TokenNoSuperuser
	TokenOf
	TokenOptions
	TokenPassword
	TokenPermission
	TokenPermissions
	TokenRename
	TokenReplace
	TokenReturns
	TokenRevoke
	TokenRole
	TokenRoles
	TokenSchema
	TokenSelectMasked
	TokenSFunc
	TokenSType
	TokenSuperuser
	TokenTo
	TokenTrigger
	TokenTypeKeyword
	TokenUnmask
	TokenUnset
	TokenUse
	TokenUser
	TokenUsers
	TokenView
	TokenWriteTime
	TokenBindMarker
	TokenEqual
	TokenLess
	TokenLessEqual
	TokenGreater
	TokenGreaterEqual
	TokenNotEqual
	TokenAssign
	TokenAdd
	TokenSubtract
	TokenMultiply
	TokenDivide
	TokenComma
	TokenSemicolon
	TokenLeftParen
	TokenRightParen
	TokenLeftBracket
	TokenRightBracket
	TokenLeftBrace
	TokenRightBrace
	TokenDot
	TokenAsterisk
	TokenColon
	TokenTypeHint
	TokenConsistencyOne
	TokenConsistencyTwo
	TokenConsistencyThree
	TokenConsistencyQuorum
	TokenConsistencyAll
	TokenConsistencyLocalQuorum
	TokenConsistencyEachQuorum
	TokenConsistencyLocalOne
	TokenConsistencySerial
	TokenConsistencyLocalSerial
)

// Token represents a lexical token
type Token struct {
	Type   TokenType
	Value  string
	Pos    Position
	Quoted bool
}

// Position represents the position of a token in the input
type Position struct {
	Line   int
	Column int
}

// Lexer tokenizes CQL input
type Lexer struct {
	input     string
	start     int
	pos       int
	width     int
	line      int
	lineStart int
	tokens    chan Token
}

// NewLexer creates a new lexer
func NewLexer(input string) *Lexer {
	l := &Lexer{
		input:     input,
		line:      1,
		lineStart: 0,
		tokens:    make(chan Token, 10),
	}
	go l.run()
	return l
}

// run lexes the input
func (l *Lexer) run() {
	for state := lexText; state != nil; {
		state = state(l)
	}
	close(l.tokens)
}

// NextToken returns the next token
func (l *Lexer) NextToken() Token {
	return <-l.tokens
}

// lexState represents a lexing state function
type lexState func(*Lexer) lexState

// lexText is the main lexing state
func lexText(l *Lexer) lexState {
	for {
		r := l.next()
		if r == eof {
			if l.pos > l.start {
				l.emit(TokenIdentifier)
			}
			l.emit(TokenEOF)
			return nil
		}

		switch {
		case r == '\n':
			if l.pos > l.start {
				l.emit(TokenIdentifier)
			}
			lexNewline(l)
			l.ignore()
		case r == '\r':
			// Ignore carriage return
		case r == ' ' || r == '\t':
			if l.pos > l.start {
				l.emit(TokenIdentifier)
			}
			l.ignore()
		case r == '-':
			if l.peek() == '-' {
				if l.pos > l.start {
					l.emit(TokenIdentifier)
				}
				l.next() // Consume second '-'
				return lexLineComment
			}
			l.backup()
			return lexOperator
		case r == '/':
			if l.peek() == '/' {
				if l.pos > l.start {
					l.emit(TokenIdentifier)
				}
				l.next() // Consume second '/'
				return lexLineComment
			}
			if l.peek() == '*' {
				if l.pos > l.start {
					l.emit(TokenIdentifier)
				}
				l.next() // Consume '*'
				return lexBlockComment
			}
			l.backup()
			return lexOperator
		case r == '\'':
			l.backup()
			if l.pos > l.start {
				l.emit(TokenIdentifier)
			}
			l.next()
			return lexString
		case r == '"':
			l.backup()
			if l.pos > l.start {
				l.emit(TokenIdentifier)
			}
			l.next()
			return lexQuotedIdentifier
		case r == '$':
			l.backup()
			if l.pos > l.start {
				l.emit(TokenIdentifier)
			}
			l.next()
			if l.peek() == '$' {
				l.next() // Consume second '$'
				return lexDollarString
			}
			return lexBlob
		case l.pos <= l.start+1 && isHexDigit(r) && looksLikeUUID(l.input[l.start:]):
			l.backup()
			return lexUUID
		case r == '0' && (l.peek() == 'x' || l.peek() == 'X'):
			l.backup()
			return lexHexBlob
		case l.pos <= l.start+1 && (isDigit(r) || (r == '.' && isDigit(l.peek()))):
			l.backup()
			return lexNumber
		case isOperator(r):
			if l.pos > l.start {
				l.emit(TokenIdentifier)
			}
			l.backup()
			return lexOperator
		case isPunctuation(r):
			if l.pos > l.start {
				l.emit(TokenIdentifier)
			}
			l.backup()
			return lexPunctuation
		}
	}
}

// lexLineComment lexes a line comment
func lexLineComment(l *Lexer) lexState {
	for {
		r := l.next()
		if r == '\n' || r == eof {
			l.ignore()
			if r == '\n' {
				lexNewline(l)
			}
			return lexText
		}
	}
}

// lexBlockComment lexes a block comment
func lexBlockComment(l *Lexer) lexState {
	depth := 1
	for {
		r := l.next()
		if r == eof {
			l.emitError("unclosed block comment")
			return nil
		}
		if r == '/' && l.peek() == '*' {
			l.next()
			depth++
		} else if r == '*' && l.peek() == '/' {
			l.next()
			depth--
			if depth == 0 {
				l.ignore()
				return lexText
			}
		} else if r == '\n' {
			lexNewline(l)
		}
	}
}

// lexNewline handles newlines
func lexNewline(l *Lexer) {
	l.line++
	l.lineStart = l.pos
}

// lexString lexes a string literal (quotes are stripped)
func lexString(l *Lexer) lexState {
	startPos := Position{
		Line:   l.line,
		Column: l.start - l.lineStart + 1,
	}
	l.start = l.pos

	var builder strings.Builder
	escaped := false

	for {
		r := l.next()
		if r == eof {
			l.emitError("unclosed string")
			return nil
		}
		if r == '\n' {
			lexNewline(l)
		}
		if r == '\'' {
			if l.peek() == '\'' {
				if !escaped {
					builder.Grow(l.pos - l.start)
					builder.WriteString(l.input[l.start : l.pos-1])
					escaped = true
				} else {
					builder.WriteString(l.input[l.start : l.pos-1])
				}
				l.next() // Consume escaped quote
				builder.WriteByte('\'')
				l.start = l.pos
				continue
			}

			var value string
			if escaped {
				builder.WriteString(l.input[l.start : l.pos-1])
				value = builder.String()
			} else {
				value = l.input[l.start : l.pos-1]
			}

			l.emitToken(TokenString, value, startPos, false)
			return lexText
		}
	}
}

// lexQuotedIdentifier lexes a quoted identifier (quotes are stripped, "" -> ").
func lexQuotedIdentifier(l *Lexer) lexState {
	startPos := Position{
		Line:   l.line,
		Column: l.start - l.lineStart + 1,
	}
	l.start = l.pos

	var builder strings.Builder
	escaped := false

	for {
		r := l.next()
		if r == eof || r == '\n' {
			l.emitError("unclosed quoted identifier")
			return nil
		}
		if r == '"' {
			if l.peek() == '"' {
				if !escaped {
					builder.Grow(l.pos - l.start)
					builder.WriteString(l.input[l.start : l.pos-1])
					escaped = true
				} else {
					builder.WriteString(l.input[l.start : l.pos-1])
				}
				l.next() // Consume escaped quote
				builder.WriteByte('"')
				l.start = l.pos
				continue
			}

			var value string
			if escaped {
				builder.WriteString(l.input[l.start : l.pos-1])
				value = builder.String()
			} else {
				value = l.input[l.start : l.pos-1]
			}

			l.emitToken(TokenIdentifier, value, startPos, true)
			return lexText
		}
	}
}

// lexDollarString lexes a $$...$$ string literal.
func lexDollarString(l *Lexer) lexState {
	startPos := Position{
		Line:   l.line,
		Column: l.start - l.lineStart + 1,
	}
	contentStart := l.pos

	for {
		r := l.next()
		if r == eof {
			l.emitError("unclosed dollar-quoted string")
			return nil
		}
		if r == '\n' {
			lexNewline(l)
		}
		if r == '$' && l.peek() == '$' {
			value := l.input[contentStart : l.pos-1]
			l.next() // Consume closing '$'
			l.emitToken(TokenString, value, startPos, false)
			return lexText
		}
	}
}

// lexBlob lexes a blob literal
func lexBlob(l *Lexer) lexState {
	// BLOB literals: $hexdigits$
	for {
		r := l.next()
		if r == '$' {
			l.emit(TokenBlob)
			return lexText
		}
		if r == eof || !isHexDigit(r) {
			l.emitError("invalid blob literal")
			return nil
		}
	}
}

// lexHexBlob lexes a hex blob literal (0x...).
func lexHexBlob(l *Lexer) lexState {
	// Expect 0x or 0X prefix.
	l.pos = l.start
	if l.next() != '0' {
		return lexText
	}
	x := l.next()
	if x != 'x' && x != 'X' {
		l.backup()
		return lexText
	}

	hasHex := false
	for {
		r := l.next()
		if r == eof || !isHexDigit(r) {
			l.backup()
			break
		}
		hasHex = true
	}

	if !hasHex {
		// Allow empty 0x literal to represent an empty blob.
		l.emit(TokenBlob)
		return lexText
	}

	l.emit(TokenBlob)
	return lexText
}

// lexUUID lexes a UUID literal.
func lexUUID(l *Lexer) lexState {
	if !looksLikeUUID(l.input[l.start:]) {
		l.emit(TokenIdentifier)
		return lexText
	}
	l.pos = l.start + 36
	l.emit(TokenUUID)
	return lexText
}

// lexNumber lexes a number literal
func lexNumber(l *Lexer) lexState {
	seenDot := false
	seenExp := false
	hasAlpha := false
	for {
		r := l.next()
		if r == '.' {
			if seenDot || seenExp {
				break
			}
			seenDot = true
		} else if (r == 'e' || r == 'E') && !hasAlpha {
			seenExp = true
			if l.peek() == '+' || l.peek() == '-' {
				l.next()
			}
		} else if isAlpha(r) || r == '_' {
			hasAlpha = true
		} else if !isDigit(r) {
			l.backup()
			break
		}
	}

	if hasAlpha && !seenDot && !seenExp {
		l.emit(TokenIdentifier)
	} else if seenDot || seenExp {
		l.emit(TokenFloat)
	} else {
		l.emit(TokenInteger)
	}
	return lexText
}

// lexOperator lexes operators
func lexOperator(l *Lexer) lexState {
	r := l.next()
	switch r {
	case '=':
		l.emit(TokenEqual)
	case '<':
		if l.peek() == '=' {
			l.next()
			l.emit(TokenLessEqual)
		} else if l.peek() == '>' {
			l.next()
			l.emit(TokenNotEqual)
		} else {
			l.emit(TokenLess)
		}
	case '>':
		if l.peek() == '=' {
			l.next()
			l.emit(TokenGreaterEqual)
		} else {
			l.emit(TokenGreater)
		}
	case '!':
		if l.peek() == '=' {
			l.next()
			l.emit(TokenNotEqual)
		} else {
			l.emit(TokenIdentifier)
		}
	case ':':
		if l.peek() == '=' {
			l.next()
			l.emit(TokenAssign)
		} else if l.peek() == ':' {
			l.next()
			l.emit(TokenTypeHint)
		} else {
			l.emit(TokenColon)
		}
	case '+':
		l.emit(TokenAdd)
	case '-':
		l.emit(TokenSubtract)
	case '/':
		l.emit(TokenDivide)
	default:
		l.emit(TokenIdentifier)
	}
	// Reset start position to avoid empty tokens
	l.start = l.pos
	return lexText
}

// lexPunctuation lexes punctuation
func lexPunctuation(l *Lexer) lexState {
	r := l.next()
	switch r {
	case ',':
		l.emit(TokenComma)
	case ';':
		l.emit(TokenSemicolon)
	case '(':
		l.emit(TokenLeftParen)
	case ')':
		l.emit(TokenRightParen)
	case '[':
		l.emit(TokenLeftBracket)
	case ']':
		l.emit(TokenRightBracket)
	case '{':
		l.emit(TokenLeftBrace)
	case '}':
		l.emit(TokenRightBrace)
	case '.':
		l.emit(TokenDot)
	case '*':
		l.emit(TokenAsterisk)
	case '?':
		l.emit(TokenBindMarker)
	default:
		l.emit(TokenIdentifier)
	}
	// Reset start position to avoid empty tokens
	l.start = l.pos
	return lexText
}

// next returns the next rune
func (l *Lexer) next() rune {
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	l.width = w
	l.pos += w
	return r
}

// peek returns the next rune without advancing
func (l *Lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

// backup steps back one rune
func (l *Lexer) backup() {
	l.pos -= l.width
}

// emit emits a token
func (l *Lexer) emit(t TokenType) {
	// Calculate end position
	endPos := l.pos

	// Only trim delimiters from identifiers, not from operators/punctuation
	if t == TokenIdentifier {
		for endPos > l.start {
			// Bounds check to prevent panic
			if endPos-1 >= len(l.input) {
				break
			}
			c := l.input[endPos-1]
			if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
				endPos--
			} else if isOperator(rune(c)) || isPunctuation(rune(c)) {
				endPos--
			} else {
				break
			}
		}
	}

	// Bounds check for start/end
	if l.start > len(l.input) {
		l.start = len(l.input)
	}
	if endPos > len(l.input) {
		endPos = len(l.input)
	}
	if l.start > endPos {
		endPos = l.start
	}

	value := l.input[l.start:endPos]

	// Don't emit empty tokens (this can happen after trimming delimiters)
	if value == "" {
		l.start = endPos
		return
	}

	pos := Position{
		Line:   l.line,
		Column: l.start - l.lineStart + 1,
	}
	t, value = normalizeToken(t, value, false)
	l.tokens <- Token{Type: t, Value: value, Pos: pos, Quoted: false}
	// Reset start position to the emitted end position
	l.start = endPos
}

func (l *Lexer) emitToken(t TokenType, value string, pos Position, quoted bool) {
	t, value = normalizeToken(t, value, quoted)
	l.tokens <- Token{Type: t, Value: value, Pos: pos, Quoted: quoted}
	l.start = l.pos
}

func normalizeToken(t TokenType, value string, quoted bool) (TokenType, string) {
	if t == TokenIdentifier && !quoted {
		value = strings.ToLower(value)
		t = lookupKeyword(value)
	}

	if t == TokenIdentifier && !quoted {
		switch value {
		case "true":
			t = TokenTrue
		case "false":
			t = TokenFalse
		case "null":
			t = TokenNull
		}
	}

	switch t {
	case TokenNaN, TokenInfinity:
		t = TokenFloat
	}

	return t, value
}

// ignore skips the current token
func (l *Lexer) ignore() {
	l.start = l.pos
}

// emitError emits an error token
func (l *Lexer) emitError(msg string) {
	pos := Position{
		Line:   l.line,
		Column: l.start - l.lineStart + 1,
	}
	l.tokens <- Token{Type: TokenError, Value: msg, Pos: pos, Quoted: false}
}

// lookupKeyword checks if an identifier is a keyword
func lookupKeyword(ident string) TokenType {
	switch strings.ToUpper(ident) {
	case "ADD":
		return TokenAddKeyword
	case "AGGREGATE":
		return TokenAggregate
	case "ALL":
		return TokenAll
	case "ALLOW":
		return TokenAllow
	case "ALTER":
		return TokenAlter
	case "AND":
		return TokenAnd
	case "APPLY":
		return TokenApply
	case "AS":
		return TokenAs
	case "ASC":
		return TokenAscending
	case "ASCII":
		return TokenAscii
	case "AUTHORIZE":
		return TokenAuthorize
	case "BATCH":
		return TokenBatch
	case "BEGIN":
		return TokenBegin
	case "BIGINT":
		return TokenBigint
	case "BLOB":
		return TokenBlobType
	case "BOOLEAN":
		return TokenBoolean
	case "BY":
		return TokenBy
	case "BETWEEN":
		return TokenBetween
	case "CALLED":
		return TokenCalled
	case "CAST":
		return TokenCast
	case "CLUSTERING":
		return TokenClustering
	case "COLUMNFAMILY":
		return TokenColumnFamily
	case "COMPACT":
		return TokenCompact
	case "CONTAINS":
		return TokenContains
	case "COUNT":
		return TokenCount
	case "COUNTER":
		return TokenCounter
	case "CREATE":
		return TokenCreate
	case "CUSTOM":
		return TokenCustom
	case "DATE":
		return TokenDate
	case "DECIMAL":
		return TokenDecimal
	case "DEFAULT":
		return TokenDefault
	case "DELETE":
		return TokenDelete
	case "DESC":
		return TokenDescending
	case "DESCRIBE":
		return TokenDescribe
	case "DISTINCT":
		return TokenDistinct
	case "DOUBLE":
		return TokenDouble
	case "DROP":
		return TokenDrop
	case "DURATION":
		return TokenDuration
	case "ENTRIES":
		return TokenEntries
	case "EXECUTE":
		return TokenExecute
	case "EXISTS":
		return TokenExists
	case "FILTERING":
		return TokenFiltering
	case "FINALFUNC":
		return TokenFinalFunc
	case "FLOAT":
		return TokenFloatType
	case "FROM":
		return TokenFrom
	case "FROZEN":
		return TokenFrozen
	case "FULL":
		return TokenFull
	case "FUNCTION":
		return TokenFunction
	case "FUNCTIONS":
		return TokenFunctions
	case "GRANT":
		return TokenGrant
	case "GROUP":
		return TokenGroup
	case "IF":
		return TokenIf
	case "IN":
		return TokenIn
	case "INDEX":
		return TokenIndex
	case "INET":
		return TokenInet
	case "INFINITY":
		return TokenInfinity
	case "INITCOND":
		return TokenInitCond
	case "INPUT":
		return TokenInput
	case "INSERT":
		return TokenInsert
	case "INT":
		return TokenInt
	case "INTO":
		return TokenInto
	case "IS":
		return TokenIs
	case "JSON":
		return TokenJSON
	case "KEY":
		return TokenKey
	case "KEYS":
		return TokenKeys
	case "KEYSPACE":
		return TokenKeyspace
	case "KEYSPACES":
		return TokenKeyspaces
	case "LANGUAGE":
		return TokenLanguage
	case "LIKE":
		return TokenLike
	case "LIMIT":
		return TokenLimit
	case "OFFSET":
		return TokenOffset
	case "LIST":
		return TokenList
	case "LOGIN":
		return TokenLogin
	case "LOGGED":
		return TokenLogged
	case "MAP":
		return TokenMap
	case "MASKED":
		return TokenMasked
	case "MATERIALIZED":
		return TokenMaterialized
	case "MBEAN":
		return TokenMBean
	case "MBEANS":
		return TokenMBeans
	case "MODIFY":
		return TokenModify
	case "NAN":
		return TokenNaN
	case "NOLOGIN":
		return TokenNoLogin
	case "NORECURSIVE":
		return TokenNoRecursive
	case "NOSUPERUSER":
		return TokenNoSuperuser
	case "NOT":
		return TokenNot
	case "NULL":
		return TokenNull
	case "OF":
		return TokenOf
	case "ON":
		return TokenOn
	case "OPTIONS":
		return TokenOptions
	case "OR":
		return TokenOr
	case "ORDER":
		return TokenOrder
	case "PARTITION":
		return TokenPartition
	case "PASSWORD":
		return TokenPassword
	case "PER":
		return TokenPer
	case "PERMISSION":
		return TokenPermission
	case "PERMISSIONS":
		return TokenPermissions
	case "PRIMARY":
		return TokenPrimary
	case "RENAME":
		return TokenRename
	case "REPLACE":
		return TokenReplace
	case "RETURNS":
		return TokenReturns
	case "REVOKE":
		return TokenRevoke
	case "ROLE":
		return TokenRole
	case "ROLES":
		return TokenRoles
	case "SCHEMA":
		return TokenSchema
	case "SELECT":
		return TokenSelect
	case "SELECT_MASKED":
		return TokenSelectMasked
	case "SET":
		return TokenSet
	case "SFUNC":
		return TokenSFunc
	case "SMALLINT":
		return TokenSmallint
	case "STATIC":
		return TokenStatic
	case "STORAGE":
		return TokenStorage
	case "STYPE":
		return TokenSType
	case "SUPERUSER":
		return TokenSuperuser
	case "TABLE":
		return TokenTable
	case "TEXT":
		return TokenText
	case "TIME":
		return TokenTime
	case "TIMESTAMP":
		return TokenTimestampType
	case "TIMEUUID":
		return TokenTimeUUID
	case "TINYINT":
		return TokenTinyint
	case "TO":
		return TokenTo
	case "TOKEN":
		return TokenToken
	case "TRIGGER":
		return TokenTrigger
	case "TRUNCATE":
		return TokenTruncate
	case "TTL":
		return TokenTTL
	case "TUPLE":
		return TokenTuple
	case "TYPE":
		return TokenTypeKeyword
	case "VECTOR":
		return TokenVector
	case "UNLOGGED":
		return TokenUnlogged
	case "UNMASK":
		return TokenUnmask
	case "UNSET":
		return TokenUnset
	case "UPDATE":
		return TokenUpdate
	case "USE":
		return TokenUse
	case "USER":
		return TokenUser
	case "USERS":
		return TokenUsers
	case "USING":
		return TokenUsing
	case "UUID":
		return TokenUUIDType
	case "VALUES":
		return TokenValues
	case "VARCHAR":
		return TokenVarchar
	case "VARINT":
		return TokenVarint
	case "VIEW":
		return TokenView
	case "WHERE":
		return TokenWhere
	case "WITH":
		return TokenWith
	case "WRITETIME":
		return TokenWriteTime
	}

	return TokenIdentifier
}

// Helper functions
func isDigit(r rune) bool {
	return r >= '0' && r <= '9'
}

func isHexDigit(r rune) bool {
	return isDigit(r) || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F')
}

func isAlpha(r rune) bool {
	return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
}

func looksLikeUUID(s string) bool {
	if len(s) < 36 {
		return false
	}
	for i := 0; i < 36; i++ {
		ch := s[i]
		switch i {
		case 8, 13, 18, 23:
			if ch != '-' {
				return false
			}
		default:
			if !isHexDigit(rune(ch)) {
				return false
			}
		}
	}
	return true
}

func isOperator(r rune) bool {
	return r == '=' || r == '<' || r == '>' || r == '!' || r == '+' || r == '-' || r == ':' || r == '/'
}

func isSpace(r rune) bool {
	return r == ' ' || r == '\t' || r == '\n' || r == '\r'
}

func isPunctuation(r rune) bool {
	return r == ',' || r == ';' || r == '(' || r == ')' || r == '[' || r == ']' || r == '{' || r == '}' || r == '.' || r == '*' || r == '?' || r == '$'
}

const eof = -1

// String returns a string representation of the token type
func (t TokenType) String() string {
	switch t {
	case TokenEOF:
		return "EOF"
	case TokenError:
		return "ERROR"
	case TokenIdentifier:
		return "IDENTIFIER"
	case TokenString:
		return "STRING"
	case TokenInteger:
		return "INTEGER"
	case TokenFloat:
		return "FLOAT"
	case TokenUUID:
		return "UUID"
	case TokenBlob:
		return "BLOB"
	case TokenSelect:
		return "SELECT"
	case TokenFrom:
		return "FROM"
	case TokenWhere:
		return "WHERE"
	default:
		return fmt.Sprintf("TOKEN_%d", t)
	}
}

// String returns a string representation of the token
func (t Token) String() string {
	return fmt.Sprintf("%s(%q)", t.Type, t.Value)
}
