package prompts

import (
	"fmt"
	"strings"

	"github.com/mudler/LocalAGI/core/agent"
	"github.com/mudler/LocalAGI/pkg/config"
	"github.com/mudler/LocalAGI/pkg/xlog"
	"github.com/traefik/yaegi/interp"
	"github.com/traefik/yaegi/stdlib"
)

type DynamicPrompt struct {
	config    map[string]string
	goPkgPath string
	i         *interp.Interpreter
}

func NewDynamicPrompt(config map[string]string, goPkgPath string) (*DynamicPrompt, error) {
	a := &DynamicPrompt{
		config:    config,
		goPkgPath: goPkgPath,
	}

	if err := a.initializeInterpreter(); err != nil {
		return nil, err
	}

	if err := a.callInit(); err != nil {
		xlog.Error("Error calling custom action init", "error", err)
	}

	return a, nil
}

func (a *DynamicPrompt) callInit() error {
	if a.i == nil {
		return nil
	}

	v, err := a.i.Eval(fmt.Sprintf("%s.Init", a.config["name"]))
	if err != nil {
		return err
	}

	run := v.Interface().(func() error)

	return run()
}

func NewDynamicPromptConfigMeta() config.FieldGroup {
	return config.FieldGroup{
		Name:  "custom",
		Label: "Custom Prompt",
		Fields: []config.Field{
			{
				Name:        "name",
				Label:       "Name",
				Type:        config.FieldTypeText,
				Required:    true,
				HelpText:    "A unique name for your custom prompt",
				Placeholder: "Enter a unique name",
			},
			{
				Name:        "code",
				Label:       "Go Code",
				Type:        config.FieldTypeTextarea,
				Required:    true,
				HelpText:    "Enter code that implements the Render and Role functions here",
				Placeholder: "Write your Go code here",
			},
			{
				Name:     "unsafe",
				Label:    "Unsafe Code",
				Type:     config.FieldTypeCheckbox,
				Required: false,
				HelpText: "Enable if the code needs to use unsafe Go features",
			},
		},
	}
}

func (a *DynamicPrompt) initializeInterpreter() error {
	if _, exists := a.config["code"]; exists && a.i == nil {
		unsafe := strings.ToLower(a.config["unsafe"]) == "true"
		i := interp.New(interp.Options{
			GoPath:       a.goPkgPath,
			Unrestricted: unsafe,
		})
		if err := i.Use(stdlib.Symbols); err != nil {
			return err
		}

		if _, exists := a.config["name"]; !exists {
			a.config["name"] = "custom"
		}

		_, err := i.Eval(fmt.Sprintf("package %s\n%s", a.config["name"], a.config["code"]))
		if err != nil {
			return err
		}

		a.i = i
	}

	return nil
}

func (a *DynamicPrompt) Render(c *agent.Agent) (string, error) {
	v, err := a.i.Eval(fmt.Sprintf("%s.Render", a.config["name"]))
	if err != nil {
		return "", err
	}

	run := v.Interface().(func() (string, error))

	return run()
}

func (a *DynamicPrompt) Role() string {
	v, err := a.i.Eval(fmt.Sprintf("%s.Role", a.config["name"]))
	if err != nil {
		return "system"
	}

	run := v.Interface().(func() string)

	return run()
}
