package repl

import (
	"io"
	"reflect"
	"slices"
	"ucl.lmika.dev/ucl"
)

type CommandOpt interface {
	config(cmdName string, r *REPL)
}

type REPL struct {
	inst *ucl.Inst

	commandDocs  map[string]Doc
	typePrinters map[reflect.Type]func(w io.Writer, v any, brief bool) error
}

func New(opts ...ucl.InstOption) *REPL {
	r := &REPL{
		commandDocs:  make(map[string]Doc),
		typePrinters: make(map[reflect.Type]func(w io.Writer, v any, brief bool) error),
	}

	instOpts := append(slices.Clone(opts), ucl.WithCustomEchoPrinter(r.echoPrinter))
	r.inst = ucl.New(instOpts...)

	r.SetCommand("help", r.helpBuiltin, Doc{
		Brief: "displays help about a command",
		Usage: "[command]",
		Args: []ArgDoc{
			{Name: "command", Brief: "command to display detailed help for"},
		},
		Detailed: `
			When used without arguments, 'help' will display the list of known commands,
			along with a brief description on what each one does.

			When used with an argument, 'help' will display a more detailed explanation
			of what each command does.
		`,
	})

	AddTypePrinter(r, func(w io.Writer, t NoResults, concise bool) error { return nil })

	return r
}

func (r *REPL) Inst() *ucl.Inst {
	return r.inst
}

func (r *REPL) SetCommand(name string, fn ucl.BuiltinHandler, opts ...CommandOpt) {
	r.commandDocs[name] = Doc{}
	for _, opt := range opts {
		opt.config(name, r)
	}

	r.inst.SetBuiltin(name, fn)
}