package compiler

import (
	"fmt"
	"reflect"
	"sort"
	"strings"
	"net/http"
	"database/sql"
	"os"

	"gitlab.com/olanguage/olang/ast"
	"gitlab.com/olanguage/olang/code"
	"gitlab.com/olanguage/olang/object"
	"gitlab.com/olanguage/olang/vm"

	"github.com/buger/jsonparser"
	"github.com/clbanning/mxj"
	"github.com/julienschmidt/httprouter"
	"gitlab.com/olanguage/olang/color"
	"gitlab.com/olanguage/olang/filedb"

	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
)

// getBuiltinNames PreBuiltFunctions'taki tüm builtin isimlerini sıralı döndürür
func getBuiltinNames() []string {
	var names []string
	for name := range PreBuiltFunctions {
		names = append(names, name)
	}
	sort.Strings(names) // Sıralı döndür
	return names
}

func init() {
	// builtins.go'daki PreBuiltFunctions'ı yükle
	for _, builtin := range PreBuiltFunctions {
		// Builtins global değişkenine ekle
		Builtins = append(Builtins, builtin)
	}
}

// Bytecode contains the Instructions our Compiler generated and the Constants the
// Compiler evaluated
type Bytecode struct {
	Instructions code.Instructions
	Constants    []object.Object
}

// EmittedInstruction represents an instruction through an opcode and it's position
type EmittedInstruction struct {
	Opcode   code.Opcode
	Position int
}

// CompilationScope - Before we start compiling a function's body (enter a new scope) we push
// a new CompilationScope on to the scopes stack. While compiling inside this scope, the emit
// method of the compiler will modify only the fields of the current CompilationScope. Once we're
// done compiling the function, we leave the scope by popping it off the scopes stack and putting
// the instructions in a new *object.CompiledFunction
type CompilationScope struct {
	instructions        code.Instructions
	lastInstruction     EmittedInstruction
	previousInstruction EmittedInstruction
}

// Compiler defines our compiler with instructions which hold the generated bytecode,
// constants which serve as our constant pool, and the last and previous instructions
type Compiler struct {
	constants    []object.Object
	symbolTable  *SymbolTable
	scopes       []CompilationScope
	scopeIndex   int
	builtins     map[string]*object.Builtin
	errors       []string
}

// Builtins tüm builtin fonksiyonları içeren global değişken
var Builtins = []*object.Builtin{
	// builtins.go'dan alınan tüm builtin fonksiyonlar
}

// New creates and returns a pointer to a Compiler with initialized instructions & constants
func New() *Compiler {
	mainScope := CompilationScope{
		instructions:        code.Instructions{},
		lastInstruction:     EmittedInstruction{},
		previousInstruction: EmittedInstruction{},
	}

	symbolTable := NewSymbolTable()
	
	// Builtins'ı PreBuiltFunctions'dan yükle
	builtinsMap := make(map[string]*object.Builtin)
	for name, builtin := range PreBuiltFunctions {
		builtinsMap[name] = builtin
	}
	
	// Builtin fonksiyonlarını sıralı olarak SymbolTable'a tanımla
	builtinNames := getBuiltinNames()
	for i, name := range builtinNames {
		symbolTable.DefineBuiltin(i, name)
	}

	return &Compiler{
		constants:    []object.Object{},
		symbolTable:  symbolTable,
		scopes:      []CompilationScope{mainScope},
		scopeIndex:  0,
		builtins:    builtinsMap,
		errors:      []string{},
	}
}

// Bytecode returns a pointer to a Bytecode intialized with our compilers instructions & constants
func (c *Compiler) Bytecode() *vm.Bytecode {
	return &vm.Bytecode{
		Instructions: c.currentInstructions(),
		Constants:    c.constants,
	}
}

func (c *Compiler) loadSymbol(s Symbol) {
	switch s.Scope {
	case GlobalScope:
		c.emit(code.OpGetGlobal, s.Index)
	case LocalScope:
		c.emit(code.OpGetLocal, s.Index)
	case BuiltinScope:
		c.emit(code.OpGetBuiltin, s.Index)
	case FreeScope:
		c.emit(code.OpGetFree, s.Index)
	case FunctionScope:
		c.emit(code.OpCurrentClosure)
	}
}

func (c *Compiler) enterScope() {
	scope := CompilationScope{
		instructions:        code.Instructions{},
		lastInstruction:     EmittedInstruction{},
		previousInstruction: EmittedInstruction{},
	}
	c.scopes = append(c.scopes, scope)
	c.scopeIndex++
	c.symbolTable = NewEnclosedSymbolTable(c.symbolTable)
}

func (c *Compiler) leaveScope() code.Instructions {
	instructions := c.currentInstructions()

	c.scopes = c.scopes[:len(c.scopes)-1]
	c.scopeIndex--
	c.symbolTable = c.symbolTable.Outer

	return instructions
}

// currentInstructions returns the instructions for the current scopes index
func (c *Compiler) currentInstructions() code.Instructions {
	return c.scopes[c.scopeIndex].instructions
}

// Compile walks the AST recursively and compiles nodes
func (c *Compiler) Compile(node ast.Node) error {
	if node == nil {
		return fmt.Errorf("nil node passed to Compile")
	}

	switch node := node.(type) {
	case *ast.Program:
		for _, s := range node.Statements {
			if err := c.Compile(s); err != nil {
				return fmt.Errorf("failed to compile statement: %w", err)
			}
		}

	case *ast.ExpressionStatement:
		err := c.Compile(node.Expression)
		if err != nil {
			return err
		}
		c.emit(code.OpPop)

	case *ast.InfixExpression:
		if node.Operator == "<" || node.Operator == "<=" {
			err := c.Compile(node.Right)
			if err != nil {
				return err
			}

			err = c.Compile(node.Left)
			if err != nil {
				return err
			}

			if node.Operator == "<" {
				c.emit(code.OpGreater)
			} else {
				c.emit(code.OpGreaterEqual)
			}

			return nil
		}

		err := c.Compile(node.Left)
		if err != nil {
			return err
		}

		err = c.Compile(node.Right)
		if err != nil {
			return err
		}

		switch node.Operator {
		case "+":
			c.emit(code.OpAdd)
		case "-":
			c.emit(code.OpSub)
		case "*":
			c.emit(code.OpMul)
		case "/":
			c.emit(code.OpDiv)
		case "%":
			c.emit(code.OpMod)
		case ">":
			c.emit(code.OpGreater)
		case ">=":
			c.emit(code.OpGreaterEqual)
		case "==":
			c.emit(code.OpEqualEqual)
		case "!=":
			c.emit(code.OpNotEqual)
		case "&&":
			c.emit(code.OpAnd)
		case "||":
			c.emit(code.OpOr)
		default:
			return fmt.Errorf("Unknown operator %s", node.Operator)
		}

	case *ast.IntegerLiteral:
		integer := &object.Integer{Value: node.Value}
		c.emit(code.OpConstant, c.addConstant(integer))

	case *ast.Boolean:
		if node.Value {
			c.emit(code.OpTrue)
		} else {
			c.emit(code.OpFalse)
		}

	case *ast.PrefixExpression:
		err := c.Compile(node.Right)
		if err != nil {
			return err
		}

		switch node.Operator {
		case "!":
			c.emit(code.OpBang)
		case "-":
			c.emit(code.OpMinus)
		default:
			return fmt.Errorf("Unknown operator %s", node.Operator)
		}

	case *ast.IfExpression:
		err := c.Compile(node.Condition)
		if err != nil {
			return err
		}

		// Emit an `OpJumpNotTruthy` with a bogus value. After compiling the consequence, we will know
		// how far to jump and can come back and change it - "back-patching". Because we are creating
		// a single pass compiler this is the solution, however more complex compilers may not come
		// back to change this on first pass and instead fill it in on another pass
		jumpNotTruthyPos := c.emit(code.OpJumpNotTruthy, 9999)

		err = c.Compile(node.Consequence)
		if err != nil {
			return err
		}

		if c.lastInstructionIs(code.OpPop) {
			c.removeLastPop()
		}

		// Emit an `OpJump` with bogus value - see similar explanation above `jumpNotTruthyPos` variable declaration
		jumpPos := c.emit(code.OpJump, 9999)

		afterConsequencePos := len(c.currentInstructions())

		// Change the jump-to position in the OpJumpNotTruthy emission earlier
		c.changeOperand(jumpNotTruthyPos, afterConsequencePos)

		if node.Alternative == nil {
			c.emit(code.OpNull)
		} else {
			err := c.Compile(node.Alternative)
			if err != nil {
				return err
			}

			if c.lastInstructionIs(code.OpPop) {
				c.removeLastPop()
			}
		}

		afterAlternativePos := len(c.currentInstructions())
		c.changeOperand(jumpPos, afterAlternativePos)

	case *ast.BlockStatement:
		for _, s := range node.Statements {
			err := c.Compile(s)
			if err != nil {
				return err
			}
		}

	case *ast.LetStatement:
		symbol := c.symbolTable.Define(node.Name.Value)

		err := c.Compile(node.Value)
		if err != nil {
			return err
		}

		if symbol.Scope == GlobalScope {
			c.emit(code.OpSetGlobal, symbol.Index)
		} else {
			c.emit(code.OpSetLocal, symbol.Index)
		}

	case *ast.Identifier:
		symbol, ok := c.symbolTable.Resolve(node.Value)
		if !ok {
			return fmt.Errorf("Undefined variable %s", node.Value)
		}

		c.loadSymbol(symbol)

	case *ast.StringLiteral:
		str := &object.String{Value: node.Value}
		c.emit(code.OpConstant, c.addConstant(str))

	case *ast.ArrayLiteral:
		for _, el := range node.Elements {
			err := c.Compile(el)
			if err != nil {
				return err
			}
		}
		c.emit(code.OpArray, len(node.Elements))

	case *ast.HashLiteral:
		keys := []ast.Expression{}
		for k := range node.Pairs {
			keys = append(keys, k)
		}
		sort.Slice(keys, func(i, j int) bool {
			return keys[i].String() < keys[j].String()
		})

		for _, k := range keys {
			err := c.Compile(k)
			if err != nil {
				return err
			}
			err = c.Compile(node.Pairs[k])
			if err != nil {
				return err
			}
		}

		c.emit(code.OpHash, len(node.Pairs)*2)

	case *ast.IndexExpression:
		err := c.Compile(node.Left)
		if err != nil {
			return err
		}

		err = c.Compile(node.Index)
		if err != nil {
			return err
		}

		c.emit(code.OpIndex)

	case *ast.FunctionLiteral:
		c.enterScope()

		// Function name definition for named functions
		if node.Token.Literal != "" {
			c.symbolTable.DefineFunctionName(node.Token.Literal)
		}

		for _, p := range node.Parameters {
			c.symbolTable.Define(p.Value)
		}

		err := c.Compile(node.Body)
		if err != nil {
			return err
		}

		if c.lastInstructionIs(code.OpPop) {
			c.replaceLastPopWithReturn()
		}
		if !c.lastInstructionIs(code.OpReturnValue) {
			c.emit(code.OpReturn)
		}

		freeSymbols := c.symbolTable.FreeSymbols
		numLocals := c.symbolTable.numDefinitions
		instructions := c.leaveScope()

		for _, s := range freeSymbols {
			c.loadSymbol(s)
		}

		compiledFunc := &object.CompiledFunction{
			Instructions:  instructions,
			NumLocals:     numLocals,
			NumParameters: len(node.Parameters),
		}

		fnIndex := c.addConstant(compiledFunc)
		c.emit(code.OpClosure, fnIndex, len(freeSymbols))

	case *ast.ReturnStatement:
		err := c.Compile(node.ReturnValue)
		if err != nil {
			return err
		}

		c.emit(code.OpReturnValue)

	case *ast.CallExpression:
		if ident, ok := node.Function.(*ast.Identifier); ok {
			// Builtin fonksiyon mu kontrol et
			if builtin, exists := c.builtins[ident.Value]; exists {
				return c.compileBuiltinCall(node, builtin)
			}
		}
		
		err := c.Compile(node.Function)
		if err != nil {
			return err
		}

		for _, a := range node.Arguments {
			err := c.Compile(a)
			if err != nil {
				return err
			}
		}

		c.emit(code.OpCall, len(node.Arguments))

	case *ast.LoopExpression:
		return c.compileLoop(node)
	case *ast.WhileExpression:
		return c.compileWhile(node)
	}

	return nil
}

func (c *Compiler) addConstant(obj object.Object) int {
	c.constants = append(c.constants, obj)
	return len(c.constants) - 1
}

func (c *Compiler) emit(op code.Opcode, operands ...int) int {
	ins := code.Make(op, operands...)
	pos := c.addInstruction(ins)

	c.setLastInstruction(op, pos)

	return pos
}

func (c *Compiler) addInstruction(ins []byte) int {
	newInstructionPos := len(c.currentInstructions())
	updatedInstructions := append(c.currentInstructions(), ins...)
	c.scopes[c.scopeIndex].instructions = updatedInstructions

	return newInstructionPos
}

func (c *Compiler) setLastInstruction(op code.Opcode, pos int) {
	previous := c.scopes[c.scopeIndex].lastInstruction
	last := EmittedInstruction{Opcode: op, Position: pos}

	c.scopes[c.scopeIndex].previousInstruction = previous
	c.scopes[c.scopeIndex].lastInstruction = last
}

func (c *Compiler) lastInstructionIs(op code.Opcode) bool {
	if len(c.currentInstructions()) == 0 {
		return false
	}
	return c.scopes[c.scopeIndex].lastInstruction.Opcode == op
}

func (c *Compiler) removeLastPop() {
	last := c.scopes[c.scopeIndex].lastInstruction
	previous := c.scopes[c.scopeIndex].previousInstruction

	old := c.currentInstructions()
	new := old[:last.Position]

	c.scopes[c.scopeIndex].instructions = new
	c.scopes[c.scopeIndex].lastInstruction = previous
}

func (c *Compiler) replaceInstruction(pos int, newInstruction []byte) {
	ins := c.currentInstructions()

	for i := 0; i < len(newInstruction); i++ {
		ins[pos+i] = newInstruction[i]
	}
}

func (c *Compiler) changeOperand(opPos int, operand int) {
	op := code.Opcode(c.currentInstructions()[opPos])
	newInstruction := code.Make(op, operand)
	c.replaceInstruction(opPos, newInstruction)
}

func (c *Compiler) replaceLastPopWithReturn() {
	lastPos := c.scopes[c.scopeIndex].lastInstruction.Position
	c.replaceInstruction(lastPos, code.Make(code.OpReturnValue))
	c.scopes[c.scopeIndex].lastInstruction.Opcode = code.OpReturnValue
}

// NewWithState creates a new compiler with the passed in symbol table and constants (needed in REPL)
func NewWithState(s *SymbolTable, constants []object.Object) *Compiler {
	mainScope := CompilationScope{
		instructions:        code.Instructions{},
		lastInstruction:     EmittedInstruction{},
		previousInstruction: EmittedInstruction{},
	}

	// Builtins'ı PreBuiltFunctions'dan yükle
	builtinsMap := make(map[string]*object.Builtin)
	for name, builtin := range PreBuiltFunctions {
		builtinsMap[name] = builtin
	}

	return &Compiler{
		constants:    constants,
		symbolTable:  s,
		scopes:      []CompilationScope{mainScope},
		scopeIndex:  0,
		builtins:    builtinsMap,
		errors:      []string{},
	}
}

func (c *Compiler) compileBuiltinCall(node *ast.CallExpression, builtin *object.Builtin) error {
	// Önce builtin fonksiyonunu stack'e koy
	// Builtin'ın SymbolTable'daki index'ini bul
	ident, ok := node.Function.(*ast.Identifier)
	if !ok {
		return fmt.Errorf("builtin function must be an identifier")
	}
	
	symbol, ok := c.symbolTable.Resolve(ident.Value)
	if !ok {
		return fmt.Errorf("builtin function %s not found in symbol table", ident.Value)
	}
	
	c.loadSymbol(symbol)
	
	// Argümanları stack'e koy
	for _, arg := range node.Arguments {
		err := c.Compile(arg)
		if err != nil {
			return err
		}
	}
	
	c.emit(code.OpCallBuiltin, len(node.Arguments))
	return nil
}

func newError(format string, a ...interface{}) *object.Error {
	return &object.Error{Message: fmt.Sprintf(format, a...)}
}

func newWarning(format string, a ...interface{}) *object.Warning {
	return &object.Warning{Message: fmt.Sprintf(format, a...)}
}

func MakeStruct(typeName string, vals map[string]interface{}) interface{} {
	var sfs []reflect.StructField
	for k, v := range vals {

		t := reflect.TypeOf(v)
		val := reflect.ValueOf(k)

		sf := reflect.StructField{
			Name: strings.Title(fmt.Sprintf("%s", val)),
			Type: t,
		}
		sfs = append(sfs, sf)
	}
	st := reflect.StructOf(sfs)
	so := reflect.New(st)

	return so.Interface()
}

func HashKeyExist(hash *object.Hash, hashpair object.HashPair) bool {
	for _, item := range hash.Pairs {
		if item.Key.Result() == hashpair.Key.Result() {
			return true
		}
	}

	return false
}

func modelCreateSql(model *object.Model, items object.Object) object.Object {
	cols := model.Columns
	db := model.Database.Connection.(*sql.DB)
	crSql := "INSERT INTO " + model.Name + " (%elements%) VALUES (%values%);"
	selPtr := ""
	valPtr := ""

	switch items := items.(type) {
	case *object.Hash:

		for _, item := range items.Pairs {
			switch cols := cols.(type) {
			case *object.Hash:
				v := HashKeyExist(cols, item)
				if !v {
					return newError("%s key not found in %s model", item.Key.Result(), model.Name)
				}
			}
		}
	}

	vals := []interface{}{}

	switch items := items.(type) {
	case *object.Hash:
		lastObjCount := len(items.Pairs) - 1
		i := 0

		for _, pair := range items.Pairs {
			selPtr += pair.Key.Result()
			valPtr += "?"
			vals = append(vals, pair.Value.Result())

			if i != lastObjCount {
				selPtr += ","
				valPtr += ","
			}
			i++
		}
	}

	crSql = strings.Replace(crSql, "%elements%", selPtr, 1)
	crSql = strings.Replace(crSql, "%values%", valPtr, 1)

	stmt, err := db.Prepare(crSql) // error here
	if err != nil {
		return newError("CREATE: %s", err)
	}
	stmt.Exec(vals...)
	defer stmt.Close()

	crSql = "SELECT %select% FROM " + model.Name + " WHERE %where%;"
	selPtr = ""
	wherePtr := ""
	vals = []interface{}{}

	switch items := items.(type) {
	case *object.Hash:
		lastObjCount := len(items.Pairs) - 1
		i := 0
		for _, pair := range items.Pairs {
			wherePtr += pair.Key.Result() + " = ?"
			vals = append(vals, pair.Value.Result())

			if i != lastObjCount {
				wherePtr += " AND "
			}
			i++
		}
	}

	switch cols := cols.(type) {
	case *object.Hash:
		lastObjCount := len(cols.Pairs) - 1
		i := 0
		for _, pair := range cols.Pairs {
			selPtr += pair.Key.Result()

			if i != lastObjCount {
				selPtr += ","
			}

			i++
		}
	}

	crSql = strings.Replace(crSql, "%select%", selPtr, 1)
	crSql = strings.Replace(crSql, "%where%", wherePtr, 1)
	query := &object.String{Value: crSql}
	run := runDbQuery(db, query, vals...)
	elements := run.(*object.Array).Elements

	return elements[len(elements)-1]
}

func modelDeleteSql(model *object.Model, items object.Object) object.Object {
	cols := model.Columns
	db := model.Database.Connection.(*sql.DB)
	crSql := "DELETE FROM " + model.Name + " WHERE %where%;"
	wherePtr := ""

	switch items := items.(type) {
	case *object.Hash:
		for _, item := range items.Pairs {
			switch cols := cols.(type) {
			case *object.Hash:
				v := HashKeyExist(cols, item)
				if !v {
					return newError("%s key not found in %s model", item.Key.Result(), model.Name)
				}
			}
		}
	}

	vals := []interface{}{}

	switch items := items.(type) {
	case *object.Hash:
		lastObjCount := len(items.Pairs) - 1
		i := 0
		for _, pair := range items.Pairs {
			wherePtr += pair.Key.Result() + " = ?"
			vals = append(vals, pair.Value.Result())

			if i != lastObjCount {
				wherePtr += " AND "
			}
			i++
		}
	}

	crSql = strings.Replace(crSql, "%where%", wherePtr, 1)

	stmt, err := db.Prepare(crSql) // error here
	if err != nil {
		return newError("DELETE: %s", err)
	}
	stmt.Exec(vals...)
	defer stmt.Close()

	return &object.Boolean{Value: true}
}

func modelDropSql(model *object.Model) object.Object {
	db := model.Database.Connection.(*sql.DB)
	crSql := "DROP TABLE " + model.Name + ";"

	_, err := db.Exec(crSql) // error here
	if err != nil {
		return newError("DROP: %s", err)
	}

	return &object.Boolean{Value: true}
}

func modelTruncateSql(model *object.Model) object.Object {
	db := model.Database.Connection.(*sql.DB)
	crSql := "TRUNCATE TABLE " + model.Name + ";"

	_, err := db.Exec(crSql) // error here
	if err != nil {
		return newError("TRUNCATE: %s", err)
	}

	return &object.Boolean{Value: true}
}

func modelMigrateSql(model *object.Model) object.Object {
	cols := model.Columns
	db := model.Database.Connection.(*sql.DB)
	crSql := "CREATE TABLE " + model.Name + " (%elements%);"

	switch rowList := cols.(type) {
	case *object.Hash:
		elements := ""
		lastObjCount := len(rowList.Pairs) - 1
		i := 0
		elements += "UUID text,"
		for _, pair := range rowList.Pairs {
			elements += pair.Key.Result() + " \"" + pair.Value.Result() + "\""

			if i != lastObjCount {
				elements += ", "
			}
			i++
		}

		crSql = strings.Replace(crSql, "%elements%", elements, 1)
	}

	_, err := db.Exec(crSql)

	if err != nil {
		return newError("MIGRATE ERROR: %s, QUERY: %s", err, crSql)
	}

	return &object.Boolean{Value: true}
}

func modelFetchSql(model *object.Model, items object.Object) object.Object {
	cols := model.Columns
	db := model.Database.Connection.(*sql.DB)

	crSql := "SELECT %select% FROM " + model.Name + "%where%;"
	selPtr := ""
	wherePtr := ""

	switch cols := cols.(type) {
	case *object.Hash:
		lastObjCount := len(cols.Pairs) - 1
		i := 0
		for _, pair := range cols.Pairs {
			selPtr += pair.Key.Result()

			if i != lastObjCount {
				selPtr += ","
			}

			i++
		}
	}

	vals := []interface{}{}

	switch items := items.(type) {
	case *object.Hash:
		lastObjCount := len(items.Pairs) - 1
		i := 0
		for _, pair := range items.Pairs {
			wherePtr += pair.Key.Result() + " = ?"
			vals = append(vals, pair.Value.Result())

			if i != lastObjCount {
				wherePtr += " AND "
			}
			i++
		}
	}

	crSql = strings.Replace(crSql, "%select%", selPtr, 1)
	if wherePtr == "" {
		crSql = strings.Replace(crSql, "%where%", "", 1)
	} else {
		crSql = strings.Replace(crSql, "%where%", " WHERE "+wherePtr, 1)
	}

	query := &object.String{Value: crSql}
	return runDbQuery(db, query, vals...)
}

func modelUpdateSql(model *object.Model, items object.Object, sets object.Object) object.Object {
	cols := model.Columns
	db := model.Database.Connection.(*sql.DB)
	crSql := "UPDATE " + model.Name + " SET %update% WHERE %where%;"
	updatePtr := ""
	wherePtr := ""

	switch sets := sets.(type) {
	case *object.Hash:
		for _, set := range sets.Pairs {
			switch cols := cols.(type) {
			case *object.Hash:
				v := HashKeyExist(cols, set)
				if !v {
					return newError("%s key not found in %s model", set.Key.Result(), model.Name)
				}
			}
		}
	}

	vals := []interface{}{}

	switch sets := sets.(type) {
	case *object.Hash:
		lastObjCount := len(sets.Pairs) - 1
		i := 0
		for _, pair := range sets.Pairs {
			updatePtr += pair.Key.Result() + " = ?"
			vals = append(vals, pair.Value.Result())

			if i != lastObjCount {
				updatePtr += ", "
			}
			i++
		}
	}

	switch items := items.(type) {
	case *object.Hash:
		lastObjCount := len(items.Pairs) - 1
		i := 0
		for _, pair := range items.Pairs {
			wherePtr += pair.Key.Result() + " = ?"
			vals = append(vals, pair.Value.Result())

			if i != lastObjCount {
				wherePtr += " AND "
			}
			i++
		}
	}

	crSql = strings.Replace(crSql, "%update%", updatePtr, 1)
	crSql = strings.Replace(crSql, "%where%", wherePtr, 1)

	// run
	stmt, err := db.Prepare(crSql) // error here
	if err != nil {
		return newError("UPDATE: %s", err)
	}
	stmt.Exec(vals...)
	defer stmt.Close()

	selPtr := ""

	switch cols := cols.(type) {
	case *object.Hash:
		lastObjCount := len(cols.Pairs) - 1
		i := 0
		for _, pair := range cols.Pairs {
			selPtr += pair.Key.Result()

			if i != lastObjCount {
				selPtr += ","
			}

			i++
		}
	}

	wherePtr = ""
	vals = []interface{}{}

	switch sets := sets.(type) {
	case *object.Hash:
		lastObjCount := len(sets.Pairs) - 1
		i := 0
		for _, pair := range sets.Pairs {
			wherePtr += pair.Key.Result() + " = ?"
			vals = append(vals, pair.Value.Result())

			if i != lastObjCount {
				wherePtr += " AND "
			}
			i++
		}
	}

	crSql = "SELECT %select% FROM " + model.Name + " WHERE %where%;"
	crSql = strings.Replace(crSql, "%where%", wherePtr, 1)
	crSql = strings.Replace(crSql, "%select%", selPtr, 1)

	query := &object.String{Value: crSql}
	return runDbQuery(db, query, vals...)
}

func runPreparedQuery(db *sql.DB, query *object.String, args *object.Array) object.Object {

	vals := []interface{}{}

	for _, arg := range args.Elements {
		vals = append(vals, arg.Result())
	}

	return runDbQuery(db, query, vals...)
}

func runDbQuery(db *sql.DB, query *object.String, args ...interface{}) object.Object {
	rows, err := db.Query(query.Result(), args...)
	if err != nil {
		return newError("%s", err)
	}

	cols, err := rows.Columns()
	if err != nil {
		return newError("%s", err)
	}

	arr := make([]object.Object, 0)
	i := 0
	for rows.Next() {
		columns := make([]interface{}, len(cols))
		columnPointers := make([]interface{}, len(cols))
		for i := range columns {
			columnPointers[i] = &columns[i]
		}

		if err := rows.Scan(columnPointers...); err != nil {
			return newError("%s", err)
		}

		pairs := make(map[object.HashKey]object.HashPair)
		var elm object.Object
		for i, colName := range cols {

			val := columnPointers[i].(*interface{})

			if *val != nil {
				nk := &object.String{Value: string(colName)}
				hashed := nk.HashKey()

				var nv object.Object
				switch ty := reflect.ValueOf(val).Elem().Interface().(type) {
				case int32:
					nv = &object.Integer{Value: int64(ty)}
				case int64:
					nv = &object.Integer{Value: int64(ty)}
				case string:
					nv = &object.String{Value: string(ty)}
				case []byte:
					nv = &object.String{Value: string(ty)}
				case bool:
					nv = &object.Boolean{Value: bool(ty)}
				default:
					nv = &object.Empty{}
				}
				hp := object.HashPair{Key: nk, Value: nv}
				pairs[hashed] = hp
			}
			elm = &object.Hash{Pairs: pairs}
		}

		arr = append(arr, elm)
		i++
	}
	return &object.Array{Elements: arr}
}

func modelCreateInternalDb(model *object.Model, items object.Object) object.Object {
	db := model.Database.Connection.(*filedb.Filedb)

	switch items := items.(type) {
	case *object.Hash:
		for _, item := range items.Pairs {
			db.Store(item.Key.Result(), item.Value.Result())
		}
	}

	return &object.Boolean{Value: true}
}

func modelDeleteInternalDb(model *object.Model, items object.Object) object.Object {
	db := model.Database.Connection.(*filedb.Filedb)

	switch items := items.(type) {
	case *object.Hash:
		for _, item := range items.Pairs {
			db.Delete(item.Key.Result())
		}
	case *object.Array:
		for _, value := range items.Elements {
			db.Delete(value.Result())
		}
	case *object.String:
		db.Delete(items.Result())
	case *object.Integer:
		db.Delete(string(items.Result()))
	}

	return &object.Boolean{Value: true}
}

func modelFetchInternalDb(model *object.Model, items object.Object) object.Object {
	db := model.Database.Connection.(*filedb.Filedb)

	switch items := items.(type) {
	case *object.Hash:
		pairs := make(map[object.HashKey]object.HashPair)
		i := 0
		for _, item := range items.Pairs {
			hashed := object.HashKey{Value: uint64(i)}
			pair := object.HashPair{Key: item.Key, Value: db.Get(item.Key.Result())}
			pairs[hashed] = pair
			i++
		}
		return &object.Hash{Pairs: pairs}
	case *object.Array:
		pairs := make(map[object.HashKey]object.HashPair)
		i := 0
		for _, value := range items.Elements {
			hashed := object.HashKey{Value: uint64(i)}
			pair := object.HashPair{Key: value, Value: db.Get(value.Result())}
			pairs[hashed] = pair
			i++
		}
		return &object.Hash{Pairs: pairs}
	case *object.String:
		return db.Get(items.Result())
	case *object.Integer:
		return db.Get(string(items.Result()))
	}

	return &object.Empty{}
}

func modelUpdateInternalDb(model *object.Model, items object.Object) object.Object {
	db := model.Database.Connection.(*filedb.Filedb)

	switch items := items.(type) {
	case *object.Hash:
		for _, item := range items.Pairs {
			db.Update(item.Key.Result(), item.Key.Result())
		}
	}

	return &object.Boolean{Value: true}
}

func modelSearchInternalDb(model *object.Model, items object.Object) object.Object {
	db := model.Database.Connection.(*filedb.Filedb)

	switch items := items.(type) {
	case *object.Array:
		i := 0
		pairs := make(map[object.HashKey]object.HashPair)
		for _, item := range items.Elements {
			res := db.Find(item.Result())

			for k, v := range res {

				nk := &object.String{Value: k}
				nv := &object.String{Value: v}
				hashed := object.HashKey{Value: uint64(i)}

				hp := object.HashPair{Key: nk, Value: nv}
				pairs[hashed] = hp
				i++
			}

			i++
		}
		return &object.Hash{Pairs: pairs}
	case *object.String:
		res := db.Find(items.Result())
		i := 0
		pairs := make(map[object.HashKey]object.HashPair)
		for k, v := range res {

			nk := &object.String{Value: k}
			nv := &object.String{Value: v}
			hashed := object.HashKey{Value: uint64(i)}

			hp := object.HashPair{Key: nk, Value: nv}
			pairs[hashed] = hp
			i++
		}

		return &object.Hash{Pairs: pairs}
	}
	return &object.Boolean{Value: false}
}

func ParseJsonToString(data object.Object) string {
	result := ""
	switch content := data.(type) {
	case *object.Hash:
		result += `{`
		i := 0
		lastKey := len(content.Pairs) - 1
		for _, pair := range content.Pairs {
			result += ParseJsonToString(pair.Key) + `:` + ParseJsonToString(pair.Value)

			if i != lastKey {
				result += ","
			}
			i++
		}
		result += `}`
	case *object.Array:
		result += `[`
		i := 0
		lastKey := len(content.Elements) - 1
		for _, element := range content.Elements {

			result += ParseJsonToString(element)

			if i != lastKey {
				result += ","
			}

			i++
		}
		result += `]`
	case *object.String:
		result += `"` + content.Result() + `"`
	case *object.Integer:
		result += content.Result()
	case *object.Boolean:
		result += content.Result()
	}

	return result
}

func ParseJson(data []byte) *object.Hash {
	i := 0
	pairs := make(map[object.HashKey]object.HashPair)
	jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {

		if dataType != jsonparser.String {
			if dataType == jsonparser.Array {
				nk := &object.String{Value: string(key)}
				length := len(value)
				newElements := make([]object.Object, 0, length)
				jsonparser.ArrayEach(value, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
					//fmt.Println(jsonparser.Get(value, "url"))
					newElements = append(newElements, ParseJson(value))
				})
				nv := &object.Array{Elements: newElements}
				hashed := object.HashKey{Value: uint64(i)}

				hp := object.HashPair{Key: nk, Value: nv}
				pairs[hashed] = hp
			}
		} else {
			nk := &object.String{Value: string(key)}
			nv := &object.String{Value: string(value)}
			hashed := object.HashKey{Value: uint64(i)}

			hp := object.HashPair{Key: nk, Value: nv}
			pairs[hashed] = hp
		}

		i++
		return nil
	})
	return &object.Hash{Pairs: pairs}
}

func ParseXML(file string) *object.Hash {
	xmlFile, _ := os.Open(file)
	defer xmlFile.Close()
	m, _ := mxj.NewMapXmlReader(xmlFile)

	leafnodes := m.LeafNodes()
	pairs := make(map[object.HashKey]object.HashPair)
	i := 0
	for _, node := range leafnodes {
		val := node.Value.(string)
		nk := &object.String{Value: string(node.Path)}
		nv := &object.String{Value: string(val)}
		hashed := object.HashKey{Value: uint64(i)}

		hp := object.HashPair{Key: nk, Value: nv}
		pairs[hashed] = hp
		i++
	}
	return &object.Hash{Pairs: pairs}
}


func checkType2(obj object.Object) object.Object {
	switch ty := obj.(type) {
	case *object.String:
		result := &object.String{Value: ty.Value}
		return result
	case *object.Hash:
		result := &object.Hash{Pairs: ty.Pairs}
		return result
	case *object.Array:
		result := &object.Array{Elements: ty.Elements}
		return result
	case *object.Integer:
		result := &object.Integer{Value: ty.Value}
		return result
	case *object.Boolean:
		result := &object.Boolean{Value: ty.Value}
		return result
	default:
		result := &object.Empty{}
		return result
	}
}

func BasicAuth(h httprouter.Handle, requiredUser, requiredPassword string) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		// Get the Basic Authentication credentials
		user, password, hasAuth := r.BasicAuth()

		if hasAuth && user == requiredUser && password == requiredPassword {
			// Delegate request to the given handle
			h(w, r, ps)
		} else {
			// Request Basic Authentication otherwise
			w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
			http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		}
	}
}

func DebugInterface(a interface{}) {
	color.Println(fmt.Sprintf("%#v\n", a))
}

func isError(obj object.Object) bool {
	if obj != nil {
		return obj.Type() == object.ERROR_OBJ
	}

	return false
}


func checkType(obj ast.Expression) object.Object {
	switch ty := obj.(type) {
	case *ast.StringLiteral:
		result := &object.String{Value: ty.Value}
		return result
	case *ast.IntegerLiteral:
		result := &object.Integer{Value: ty.Value}
		return result
	case *ast.Boolean:
		result := &object.Boolean{Value: ty.Value}
		return result
	default:
		result := &object.Empty{}
		return result
	}
}

func evalChangeScopeVal(stmt *ast.ScopeChangeExpression, env *object.Environment) object.Object {
	res, _ := env.Get(stmt.Left)
	right := stmt.Right
	val := stmt.Value

	switch topScope := res.(type) {
	case *object.Scope:
		for i := range topScope.Body.Statements {
			switch newStmt := topScope.Body.Statements[i].(type) {
			case *ast.LetStatement:
				lit := newStmt.Name.Token.Literal
				if string(lit) == right {
					topScope.Body.Statements[i] = val
				}
			}

		}
	}
	return &object.Empty{}
}



func applyFunction(fn object.Object, args []object.Object) object.Object {
	switch fn := fn.(type) {
	case *object.Function:
		// Compile the function body
		c := New()
		err := c.Compile(fn.Body)
		if err != nil {
			return newError("compilation error: %s", err)
		}

		// Get the bytecode
		bytecode := c.Bytecode()
		
		machine := vm.New(bytecode)
		err = machine.Run()
		if err != nil {
			return newError("vm error: %s", err)
		}

		return machine.LastPoppedStackElement()

	case *object.Builtin:
		return fn.Fn(args...)

	default:
		return newError("not a function: %s", color.Bold(fn.Type()))
	}
}

func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
	env := object.NewEnclosedEnvironment(fn.Env)

	for i, param := range fn.Parameters {
		env.Set(param.Value, args[i])
	}

	return env
}

func unwrapReturnValue(obj object.Object) object.Object {
	if returnValue, ok := obj.(*object.ReturnValue); ok {
		return returnValue.Value
	}
	return obj
}

// RegisterBuiltin düzeltmesi
func (c *Compiler) RegisterBuiltin(name string, builtin *object.Builtin) {
	if c.builtins == nil {
		c.builtins = make(map[string]*object.Builtin)
	}
	c.builtins[name] = builtin
}

// Eval ve isTruthy fonksiyonları için yardımcı fonksiyonlar
func evalExpression(node ast.Node, env *object.Environment) object.Object {
	// Implementation
	return nil
}

func isTruthy(obj object.Object) bool {
	switch obj := obj.(type) {
	case *object.Boolean:
		return obj.Value
	case *object.Null:
		return false
	default:
		return true
	}
}

func (c *Compiler) compileLoop(le *ast.LoopExpression) error {
	err := c.Compile(le.Condition)
	if err != nil {
		return err
	}
	return c.Compile(le.Consequence)
}

func (c *Compiler) compileWhile(we *ast.WhileExpression) error {
	err := c.Compile(we.Condition)
	if err != nil {
		return err
	}
	return c.Compile(we.Consequence)
}

func (c *Compiler) addError(format string, a ...interface{}) {
	msg := fmt.Sprintf(format, a...)
	c.errors = append(c.errors, msg)
}

func (c *Compiler) Errors() []string {
	return c.errors
}
