package engine

import (
	_ "embed"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/dop251/goja"
	"github.com/goccy/go-yaml"

	"github.com/antonmedv/fx/internal/jsonx"
)

//go:embed stdlib.js
var Stdlib string

//go:embed prelude.js
var prelude string

func Reduce(args []string) {
	if len(args) < 1 {
		panic("args must have at least one element")
	}

	var (
		flagYaml  bool
		flagRaw   bool
		flagSlurp bool
	)

	var src io.Reader = os.Stdin
	if isFile(args[0]) {
		src = open(args[0], &flagYaml)
		args = args[1:]
	} else if isFile(args[len(args)-1]) {
		src = open(args[len(args)-1], &flagYaml)
		args = args[:len(args)-1]
	}

	var fns []string
	for _, arg := range args {
		switch arg {
		case "--yaml":
			flagYaml = true
		case "--raw", "-r":
			flagRaw = true
		case "--slurp", "-s":
			flagSlurp = true
		case "-rs", "-sr":
			flagRaw = true
			flagSlurp = true
		default:
			fns = append(fns, arg)
		}
	}

	if flagSlurp {
		println("Error: Built-in JS engine does not support \"--slurp\" flag. Install Node.js or Deno to use this flag.")
		os.Exit(1)
	}

	data, err := io.ReadAll(src)
	if err != nil {
		panic(err)
	}

	if flagRaw {
		data = []byte(strconv.Quote(string(data)))
	} else if flagYaml {
		data, err = yaml.YAMLToJSON(data)
		if err != nil {
			println(err.Error())
			os.Exit(1)
		}
	} else {
		node, err := jsonx.Parse(data)
		if err != nil {
			println(err.Error())
			os.Exit(1)
		}
		data = []byte(node.String())
	}

	var code strings.Builder
	code.WriteString(prelude)
	code.WriteString(Stdlib)
	code.WriteString(fmt.Sprintf("let json = JSON.parse(%q)\n", data))
	for _, fn := range fns {
		code.WriteString(Transform(fn))
	}
	code.WriteString("JSON.stringify(json)")

	vm := goja.New()
	vm.Set("println", func(s string) any {
		fmt.Println(s)
		return nil
	})

	value, err := vm.RunString(code.String())
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}

	output, ok := value.Export().(string)
	if !ok {
		println("undefined")
		return
	}

	node, err := jsonx.Parse([]byte(output))
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}

	if len(node.Value) > 0 && node.Value[0] == '"' {
		s, _ := strconv.Unquote(string(node.Value))
		fmt.Println(s)
		return
	}
	fmt.Print(node.PrettyPrint())
}
