package compiler

import (
	"bytes"
	"strings"

	"go.starlark.net/lib/math"
	"go.starlark.net/starlark"
)

// executeScript runs a Starlark script and returns the output lines
func executeScript(scriptLines []string, ctx *CompilerContext) ([]string, error) {
	// Join script lines
	scriptText := strings.Join(scriptLines, "\n")

	// Expand |varname| -> actual variable names
	scriptText = expandVariables(scriptText, ctx)

	// Wrap in function (Starlark requires control flow inside functions)
	wrappedScript := "def _main():\n"
	for _, line := range strings.Split(scriptText, "\n") {
		wrappedScript += "    " + line + "\n"
	}
	wrappedScript += "_main()\n"

	// Capture print output
	var output bytes.Buffer
	thread := &starlark.Thread{
		Print: func(_ *starlark.Thread, msg string) {
			output.WriteString(msg)
			output.WriteString("\n")
		},
	}

	// Set execution limit (prevent infinite loops)
	thread.SetMaxExecutionSteps(1000000) // 1M steps

	// Predeclared functions (math module)
	predeclared := starlark.StringDict{
		"math": math.Module,
	}

	// Execute
	_, err := starlark.ExecFile(thread, "script.star", wrappedScript, predeclared)
	if err != nil {
		return nil, err
	}

	// Split output into lines for assembly
	outputStr := output.String()
	if outputStr == "" {
		return []string{}, nil
	}
	lines := strings.Split(strings.TrimRight(outputStr, "\n"), "\n")
	return lines, nil
}

// expandVariables replaces |varname| with expanded variable names from symbol table
func expandVariables(text string, ctx *CompilerContext) string {
	result := text
	for {
		start := strings.IndexByte(result, '|')
		if start == -1 {
			break
		}
		end := strings.IndexByte(result[start+1:], '|')
		if end == -1 {
			break // unclosed, let script fail
		}
		end += start + 1

		varName := result[start+1 : end]
		expandedName := ctx.SymbolTable.ExpandName(varName, ctx.CurrentScope())
		result = result[:start] + expandedName + result[end+1:]
	}
	return result
}
